/*****************************************************************************
 * Xerox Alto font dump.
 *
 * Copyright (C) 2007 by Juergen Buchmueller <pullmoll@t-online.de>
 * Partially based on info found in Eric Smith's Alto simulator: Altogether
 *
 *  This program 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 program 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 program; if not, write to the Free Software
 *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 *
 * This is a tool to visualize a Xerox Alto *.AL font file.
 * It can handle fonts with iat most one extension, i.e. max width = 32.
 *
 * The output can be plain text (default), or a PNG image containing
 * zoomed bitmaps of the font's glyphs.
 *
 * $Id: aldump.c,v 1.2 2008/07/27 10:07:13 pm Exp $
 *****************************************************************************/
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <string.h>
#include <stdint.h>
#include <errno.h>
#include <fcntl.h>

#include "png.h"
#include "md5.h"

/** @brief number of 32 bit words per glyph bitmap line */
#define	BITMAPW	(256/32)

int verbose;

/** @brief structure of an Alto font */
typedef struct {
	/** @brief word: font height in scanlines */
	unsigned char fontheight[2];

	/** @brief bits 7-0: font max. width, bits 14-8: font ascent, bit 15: fixed font flag */
	unsigned char fonttype[2];

	/** @brief self relative offset to the character XW (0..255) */
	unsigned char xw[256*2];

	/** @brief data words (more following) */
	unsigned char data[2];
}	alto_font_t;


static int png_write(void *cookie, uint8_t *buff, int size)
{
	FILE *fp = (FILE *)cookie;
	if (size == fwrite(buff, 1, size, fp))
		return -1;
	return 0;
}

static void png_bits(png_t *png, int x0, int y0, int pw, int ph, int w, int y, uint32_t *bitmap)
{
	int x, x1, y1;

	for (x = 0; x < w; x++) {
		if (bitmap[x/32] & (0x80000000 >> (x % 32))) {
			for (y1 = y * ph; y1 < (y + 1) * ph; y1++)
				for (x1 = x * pw; x1 < (x + 1) * pw; x1++)
					png_put_pixel(png, x0 + x1, y0 + y1, 1, 0);
		}
	}
}

#define	FONTW	6
#define	FONTH	10
#define	FONTC	2		/* color palette index to use for png_putch and png_printf */
static const uint8_t chargen[128*FONTH] = {
	0x00,0xa8,0x00,0x88,0x00,0x88,0x00,0xa8,0x00,0x00,
	0x00,0xa8,0x00,0x88,0x00,0x88,0x00,0xa8,0x00,0x00,
	0x00,0xa8,0x00,0x88,0x00,0x88,0x00,0xa8,0x00,0x00,
	0x00,0xa8,0x00,0x88,0x00,0x88,0x00,0xa8,0x00,0x00,
	0x00,0xa8,0x00,0x88,0x00,0x88,0x00,0xa8,0x00,0x00,
	0x00,0xa8,0x00,0x88,0x00,0x88,0x00,0xa8,0x00,0x00,
	0x00,0xa8,0x00,0x88,0x00,0x88,0x00,0xa8,0x00,0x00,
	0x00,0xa8,0x00,0x88,0x00,0x88,0x00,0xa8,0x00,0x00,
	0x00,0xa8,0x00,0x88,0x00,0x88,0x00,0xa8,0x00,0x00,
	0x00,0xa8,0x00,0x88,0x00,0x88,0x00,0xa8,0x00,0x00,
	0x00,0xa8,0x00,0x88,0x00,0x88,0x00,0xa8,0x00,0x00,
	0x00,0xa8,0x00,0x88,0x00,0x88,0x00,0xa8,0x00,0x00,
	0x00,0xa8,0x00,0x88,0x00,0x88,0x00,0xa8,0x00,0x00,
	0x00,0xa8,0x00,0x88,0x00,0x88,0x00,0xa8,0x00,0x00,
	0x00,0xa8,0x00,0x88,0x00,0x88,0x00,0xa8,0x00,0x00,
	0x00,0xa8,0x00,0x88,0x00,0x88,0x00,0xa8,0x00,0x00,
	0x00,0xa8,0x00,0x88,0x00,0x88,0x00,0xa8,0x00,0x00,
	0x00,0xa8,0x00,0x88,0x00,0x88,0x00,0xa8,0x00,0x00,
	0x00,0xa8,0x00,0x88,0x00,0x88,0x00,0xa8,0x00,0x00,
	0x00,0xa8,0x00,0x88,0x00,0x88,0x00,0xa8,0x00,0x00,
	0x00,0xa8,0x00,0x88,0x00,0x88,0x00,0xa8,0x00,0x00,
	0x00,0xa8,0x00,0x88,0x00,0x88,0x00,0xa8,0x00,0x00,
	0x00,0xa8,0x00,0x88,0x00,0x88,0x00,0xa8,0x00,0x00,
	0x00,0xa8,0x00,0x88,0x00,0x88,0x00,0xa8,0x00,0x00,
	0x00,0xa8,0x00,0x88,0x00,0x88,0x00,0xa8,0x00,0x00,
	0x00,0xa8,0x00,0x88,0x00,0x88,0x00,0xa8,0x00,0x00,
	0x00,0xa8,0x00,0x88,0x00,0x88,0x00,0xa8,0x00,0x00,
	0x00,0xa8,0x00,0x88,0x00,0x88,0x00,0xa8,0x00,0x00,
	0x00,0xa8,0x00,0x88,0x00,0x88,0x00,0xa8,0x00,0x00,
	0x00,0xa8,0x00,0x88,0x00,0x88,0x00,0xa8,0x00,0x00,
	0x00,0x08,0x18,0x38,0x78,0x78,0x38,0x18,0x08,0x00,
	0x00,0x80,0xc0,0xe0,0xf0,0xf0,0xe0,0xc0,0x80,0x00,
	0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
	0x00,0x20,0x20,0x20,0x20,0x20,0x00,0x20,0x00,0x00,
	0x50,0x50,0x50,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
	0x00,0x00,0x50,0xf8,0x50,0x50,0xf8,0x50,0x00,0x00,
	0x20,0x70,0xa8,0xa0,0x70,0x28,0xa8,0x70,0x20,0x00,
	0x00,0xc8,0xc8,0x10,0x20,0x40,0x98,0x98,0x00,0x00,
	0x00,0x40,0xa0,0xa0,0x40,0xa8,0x90,0x68,0x00,0x00,
	0x20,0x20,0x20,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
	0x10,0x20,0x20,0x40,0x40,0x40,0x20,0x20,0x10,0x00,
	0x40,0x20,0x20,0x10,0x10,0x10,0x20,0x20,0x40,0x00,
	0x00,0x20,0xa8,0x70,0x20,0x70,0xa8,0x20,0x00,0x00,
	0x00,0x00,0x20,0x20,0xf8,0x20,0x20,0x00,0x00,0x00,
	0x00,0x00,0x00,0x00,0x00,0x00,0x60,0x60,0xc0,0x00,
	0x00,0x00,0x00,0x00,0xf8,0x00,0x00,0x00,0x00,0x00,
	0x00,0x00,0x00,0x00,0x00,0x00,0x60,0x60,0x00,0x00,
	0x00,0x08,0x10,0x10,0x20,0x40,0x40,0x80,0x00,0x00,
	0x00,0x30,0x48,0x48,0x48,0x48,0x48,0x30,0x00,0x00,
	0x00,0x20,0x60,0x20,0x20,0x20,0x20,0x70,0x00,0x00,
	0x00,0x70,0x88,0x08,0x10,0x20,0x40,0xf8,0x00,0x00,
	0x00,0xf8,0x08,0x10,0x30,0x08,0x88,0x70,0x00,0x00,
	0x00,0x10,0x30,0x50,0x90,0xf8,0x10,0x10,0x00,0x00,
	0x00,0xf8,0x80,0xf0,0x08,0x08,0x88,0x70,0x00,0x00,
	0x00,0x30,0x40,0x80,0xf0,0x88,0x88,0x70,0x00,0x00,
	0x00,0xf8,0x08,0x10,0x10,0x20,0x20,0x20,0x00,0x00,
	0x00,0x70,0x88,0x88,0x70,0x88,0x88,0x70,0x00,0x00,
	0x00,0x70,0x88,0x88,0x78,0x08,0x10,0x60,0x00,0x00,
	0x00,0x00,0x00,0x60,0x60,0x00,0x60,0x60,0x00,0x00,
	0x00,0x00,0x00,0x60,0x60,0x00,0x60,0x60,0xc0,0x00,
	0x00,0x00,0x10,0x20,0x40,0x20,0x10,0x00,0x00,0x00,
	0x00,0x00,0x00,0xf8,0x00,0xf8,0x00,0x00,0x00,0x00,
	0x00,0x00,0x40,0x20,0x10,0x20,0x40,0x00,0x00,0x00,
	0x00,0x70,0x88,0x10,0x20,0x20,0x00,0x20,0x00,0x00,
	0x00,0x70,0x88,0xb8,0xa8,0xb8,0x80,0x70,0x00,0x00,
	0x00,0x70,0x88,0x88,0xf8,0x88,0x88,0x88,0x00,0x00,
	0x00,0xf0,0x48,0x48,0x70,0x48,0x48,0xf0,0x00,0x00,
	0x00,0x70,0x88,0x80,0x80,0x80,0x88,0x70,0x00,0x00,
	0x00,0xf0,0x48,0x48,0x48,0x48,0x48,0xf0,0x00,0x00,
	0x00,0xf8,0x80,0x80,0xf0,0x80,0x80,0xf8,0x00,0x00,
	0x00,0xf8,0x80,0x80,0xf0,0x80,0x80,0x80,0x00,0x00,
	0x00,0x70,0x88,0x80,0x80,0x98,0x88,0x70,0x00,0x00,
	0x00,0x88,0x88,0x88,0xf8,0x88,0x88,0x88,0x00,0x00,
	0x00,0x70,0x20,0x20,0x20,0x20,0x20,0x70,0x00,0x00,
	0x00,0x38,0x10,0x10,0x10,0x10,0x90,0x60,0x00,0x00,
	0x00,0x88,0x90,0xa0,0xc0,0xa0,0x90,0x88,0x00,0x00,
	0x00,0x80,0x80,0x80,0x80,0x80,0x80,0xf8,0x00,0x00,
	0x00,0x88,0xd8,0xa8,0x88,0x88,0x88,0x88,0x00,0x00,
	0x00,0x88,0x88,0xc8,0xa8,0x98,0x88,0x88,0x00,0x00,
	0x00,0x70,0x88,0x88,0x88,0x88,0x88,0x70,0x00,0x00,
	0x00,0xf0,0x88,0x88,0xf0,0x80,0x80,0x80,0x00,0x00,
	0x00,0x70,0x88,0x88,0x88,0xa8,0x90,0x68,0x00,0x00,
	0x00,0xf0,0x88,0x88,0xf0,0xa0,0x90,0x88,0x00,0x00,
	0x00,0x70,0x88,0x80,0x70,0x08,0x88,0x70,0x00,0x00,
	0x00,0xf8,0x20,0x20,0x20,0x20,0x20,0x20,0x00,0x00,
	0x00,0x88,0x88,0x88,0x88,0x88,0x88,0x70,0x00,0x00,
	0x00,0x88,0x88,0x88,0x88,0x50,0x50,0x20,0x00,0x00,
	0x00,0x88,0x88,0x88,0x88,0xa8,0xa8,0x50,0x00,0x00,
	0x00,0x88,0x88,0x50,0x20,0x50,0x88,0x88,0x00,0x00,
	0x00,0x88,0x88,0x50,0x20,0x20,0x20,0x20,0x00,0x00,
	0x00,0xf8,0x08,0x10,0x20,0x40,0x80,0xf8,0x00,0x00,
	0x70,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x70,0x00,
	0x00,0x80,0x40,0x40,0x20,0x10,0x10,0x08,0x00,0x00,
	0x70,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x70,0x00,
	0x20,0x50,0x88,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
	0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xf8,
	0x40,0x20,0x10,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
	0x00,0x00,0x00,0x70,0x08,0x78,0x88,0x78,0x00,0x00,
	0x00,0x80,0x80,0xf0,0x88,0x88,0x88,0xf0,0x00,0x00,
	0x00,0x00,0x00,0x70,0x88,0x80,0x88,0x70,0x00,0x00,
	0x00,0x08,0x08,0x78,0x88,0x88,0x88,0x78,0x00,0x00,
	0x00,0x00,0x00,0x70,0x88,0xf0,0x80,0x70,0x00,0x00,
	0x00,0x30,0x48,0x40,0xe0,0x40,0x40,0x40,0x00,0x00,
	0x00,0x00,0x00,0x70,0x88,0x88,0x88,0x78,0x08,0x70,
	0x00,0x80,0x80,0xf0,0x88,0x88,0x88,0x88,0x00,0x00,
	0x00,0x20,0x00,0x60,0x20,0x20,0x20,0x70,0x00,0x00,
	0x00,0x08,0x00,0x18,0x08,0x08,0x08,0x08,0x48,0x30,
	0x00,0x80,0x80,0x88,0x90,0xe0,0x90,0x88,0x00,0x00,
	0x00,0x60,0x20,0x20,0x20,0x20,0x20,0x70,0x00,0x00,
	0x00,0x00,0x00,0xd0,0xa8,0xa8,0xa8,0xa8,0x00,0x00,
	0x00,0x00,0x00,0xb0,0xc8,0x88,0x88,0x88,0x00,0x00,
	0x00,0x00,0x00,0x70,0x88,0x88,0x88,0x70,0x00,0x00,
	0x00,0x00,0x00,0xf0,0x88,0x88,0x88,0xf0,0x80,0x80,
	0x00,0x00,0x00,0x78,0x88,0x88,0x88,0x78,0x08,0x08,
	0x00,0x00,0x00,0xb0,0xc8,0x80,0x80,0x80,0x00,0x00,
	0x00,0x00,0x00,0x78,0x80,0x70,0x08,0xf0,0x00,0x00,
	0x00,0x20,0x20,0xf8,0x20,0x20,0x20,0x18,0x00,0x00,
	0x00,0x00,0x00,0x88,0x88,0x88,0x98,0x68,0x00,0x00,
	0x00,0x00,0x00,0x88,0x88,0x88,0x50,0x20,0x00,0x00,
	0x00,0x00,0x00,0x88,0x88,0xa8,0xa8,0x50,0x00,0x00,
	0x00,0x00,0x00,0x88,0x50,0x20,0x50,0x88,0x00,0x00,
	0x00,0x00,0x00,0x88,0x88,0x88,0x50,0x20,0x40,0x80,
	0x00,0x00,0x00,0xf8,0x10,0x20,0x40,0xf8,0x00,0x00,
	0x10,0x20,0x20,0x20,0x40,0x20,0x20,0x20,0x10,0x00,
	0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x00,
	0x40,0x20,0x20,0x20,0x10,0x20,0x20,0x20,0x40,0x00,
	0x00,0x00,0x00,0x48,0xa8,0x90,0x00,0x00,0x00,0x00,
	0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00
};

static void png_putch(png_t *png, int x0, int y0, int n)
{
	int x, y;
	const uint8_t *src = &chargen[n * FONTH];

	for (y = 0; y < FONTH; y++) {
		for (x = 0; x < FONTW; x++) {
			if (src[y] & (0x80 >> x))
				png_put_pixel(png, x0 + x, y0 + y, FONTC, 0);
		}
	}
}

static void png_printf(png_t *png, int x0, int y0, const char *fmt, ...)
{
	char buff[256];
	size_t i, len;

	va_list ap;
	va_start(ap, fmt);
	len = vsnprintf(buff, sizeof(buff), fmt, ap);
	va_end(ap);

	for (i = 0; i < len; i++)
		png_putch(png, x0 + i * FONTW, y0, buff[i]);
}

static const char *binstr(uint32_t *bitmap, int width)
{
	static char str[256+1];
	int i;

	for (i = 0; i < width; i++)
		str[i] = (bitmap[i/32] & (0x80000000 >> (i%32))) ? '#' : '.';
	str[i] = '\0';

	return str;
}


int extract_xw(uint32_t *bitmap, int *cw, alto_font_t *al, int size, int ch, int fw, int fh)
{
	int xw;
	int extent;
	int hd;
	int xh;
	int wo;
	int w;
	int y;

	/* get the XW pointer for this character */
	xw = ch + al->xw[2*ch+0] + 256 * al->xw[2*ch+1] - 256;

	/* erase all glyph bitmap bits */
	memset(bitmap, 0, BITMAPW * fh * sizeof(uint32_t));

	/* invalid XW pointer - return 0 */
	if (xw < 0 || xw > size)
		return 0;

	/* get the word at XW */
	extent = al->data[2*xw] + 256 * al->data[2*xw+1];

	/* if extent is odd, it's 2*width+1, otherwise it's 2*extent character code */
	if (extent & 1) {
		w = extent / 2;
		extent = 0;
	} else {
		w = 16;
		extent = extent / 2;
	}
	/* height of the data before xw */
	hd = al->data[2*xw+2];
	/* skip xh lines from the top of the character */
	xh = al->data[2*xw+3];

	if (w == 0 || hd == 0)
		return 0;

	*cw = w;

	for (y = 0; y < fh; y++) {
		int offs, data, lsb = 0;
		if (y < xh || y >= xh + hd)
			continue;

		offs = xw - hd + (y - xh);

		data = al->data[2*offs] + 256 * al->data[2*offs+1];

		/* least significant 1 bit */
		if (data & 0x0001) lsb = 16;
		else if (data & 0x0002) lsb = 15;
		else if (data & 0x0004) lsb = 14;
		else if (data & 0x0008) lsb = 13;
		else if (data & 0x0010) lsb = 12;
		else if (data & 0x0020) lsb = 11;
		else if (data & 0x0040) lsb = 10;
		else if (data & 0x0080) lsb = 9;
		else if (data & 0x0100) lsb = 8;
		else if (data & 0x0200) lsb = 7;
		else if (data & 0x0400) lsb = 6;
		else if (data & 0x0800) lsb = 5;
		else if (data & 0x1000) lsb = 4;
		else if (data & 0x2000) lsb = 3;
		else if (data & 0x4000) lsb = 2;
		else if (data & 0x8000) lsb = 1;
		/* lsb still inside max. width? */
		if (lsb > w && lsb <= fw) {
			/* assume wrong XW width, and report the true least significant 1 */
			*cw = lsb;
		}
		bitmap[y*BITMAPW] = data << 16;
	}

	/* no extent, just leave as it is */
	if (!extent)
		return fh;

	/* word offset = 1 */
	wo = 1;
	while (extent && wo < BITMAPW*2) {
		int ew;	/* extent width */

		/* extent is character >= 256 */
		ch = extent;

		/* get the XW pointer for this character */
		xw = ch + al->xw[2*ch+0] + 256 * al->xw[2*ch+1] - 256;

		/* get the word at XW */
		extent = al->data[2*xw] + 256 * al->data[2*xw+1];

		/* if extent is odd, it's 2*width+1, otherwise it's 2*extent character code */
		if (extent & 1) {
			/* final extent */
			ew = extent / 2;
			extent = 0;
		} else {
			/* even more extents */
			ew = 16;
			extent = extent / 2;
		}

		/* height of the data before xw */
		hd = al->data[2*xw+2];
		/* skip xh lines from the top of the character */
		xh = al->data[2*xw+3];

		for (y = 0; y < fh; y++) {
			int offs, data, lsb = 0;
			if (y < xh || y >= xh + hd)
				continue;

			offs = xw - hd + (y - xh);

			data = al->data[2*offs] + 256 * al->data[2*offs+1];

			/* least significant 1 bit */
			if (data & 0x0001) lsb = 16;
			else if (data & 0x0002) lsb = 15;
			else if (data & 0x0004) lsb = 14;
			else if (data & 0x0008) lsb = 13;
			else if (data & 0x0010) lsb = 12;
			else if (data & 0x0020) lsb = 11;
			else if (data & 0x0040) lsb = 10;
			else if (data & 0x0080) lsb = 9;
			else if (data & 0x0100) lsb = 8;
			else if (data & 0x0200) lsb = 7;
			else if (data & 0x0400) lsb = 6;
			else if (data & 0x0800) lsb = 5;
			else if (data & 0x1000) lsb = 4;
			else if (data & 0x2000) lsb = 3;
			else if (data & 0x4000) lsb = 2;
			else if (data & 0x8000) lsb = 1;
			/* w + lsb still inside max. width? */
			if (lsb > ew && lsb <= fw) {
				/* assume wrong XW width, and report the true least significant 1 */
				ew = lsb;
			}
			bitmap[y*BITMAPW+(wo/2)] |= (wo & 1) ? data : data << 16;
		}

		w += ew;
		*cw = w;

		/* next word offset */
		wo++;
	}

	return fh;
}

static int dump_font(char *filename, char *pngfile)
{
	char *basename;
	alto_font_t *al;
	FILE *font_fp;
	int fw;
	int fh;
	int fixed;
	int ascent;
	int size;
	uint32_t *bitmap;
	int rw;
	int rh;
	int w;
	int h;
	int x;
	int y;
	int i;

	basename = strrchr(filename, '/');
	if (basename)
		basename++;
	else
		basename = filename;

	al = malloc(131072);
	if (!al) {
		perror("malloc");
		return 1;
	}

	font_fp = fopen(filename, "rb");
	if (!font_fp) {
		perror(filename);
		return 2;
	}
	size = fread(al, 1, 131072, font_fp);
	fclose(font_fp);

	fh = al->fontheight[0] + 256 * al->fontheight[1];

	/* font height >= 256 is most probably byte swapped */
	if (fh >= 256) {
		unsigned char *pb = (unsigned char *)al;
		for (i = 0; i < size; i += 2) {
			unsigned char tmp = pb[i];
			pb[i] = pb[i+1];
			pb[i+1] = tmp;
		}
	}

	/* font height */
	fh = al->fontheight[0] + 256 * al->fontheight[1];

	/* font max width, ascent and type */
	fw = al->fonttype[0] + 256 * al->fonttype[1];
	fixed = (~fw >> 15) & 1;
	ascent = (fw >> 8) & 0177;
	fw = fw & 0377;

	/* BITMAPW * 32 bits per row */
	bitmap = (uint32_t *)calloc(BITMAPW * fh, sizeof(uint32_t));

	/* characters per HTML table row */
	rw = 16;
	rh = 16;

	if (pngfile) {
		png_t *png;
		FILE *png_fp;
		int pw = 5;		/* zoomed pixel width */
		int ph = 5;		/* zoomed pixel height */
		int aw = 6;		/* additional width per glyph */
		int ah = FONTH + 4;	/* additional height per glyph */
		int mw = 0;		/* max width found in the glyphs */
		int first = -1;		/* first defined glyph */
		int last = 0;		/* last defined glyph */

		if (strcmp(pngfile, "-md5")) {
			png_fp = fopen(pngfile, "wb");
			if (!png_fp) {
				fprintf(stderr, "Cannot create '%s'\n", pngfile);
				return -1;
			}
		} else {
			char filename[FILENAME_MAX];
			snprintf(filename, sizeof(filename),
				"%s.png", md5_digest((uint8_t *)al, size));
			png_fp = fopen(filename, "wb");
			if (!png_fp) {
				fprintf(stderr, "Cannot create '%s'\n", filename);
				return -1;
			}
		}

		/* scan font for first and last */
		for (i = 0; i < 256; i++) {
			h = extract_xw(bitmap, &w, al, size, i, fw, fh);
			if (0 == h)
				continue;	/* undefined character */

			/* check for true max. width */
			if (w > mw)
				mw = w;
			if (-1 == first)
				first = i - (i % rw);
			last = i;
		}
		if (mw > fw) {
			int tmp;
			if (verbose)
				fprintf(stderr, "Font '%s' has wrong header max. width:%d, found:%d\n",
					filename, fw, mw);
			tmp = fw; fw = mw; mw = tmp;
		} else if (mw < fw) {
			int tmp;
			if (verbose)
				fprintf(stderr, "Font '%s' has wrong header max. width:%d, found:%d\n",
					filename, fw, mw);
			tmp = fw; fw = mw; mw = tmp;
		}
		rh = (last + rw - first - 1) / rw;

		/* scale down zoom if the image becomes too big */
		while (pw > 1) {
			if (rw*(pw*fw+aw) > 1152 || 24+rh*(ph*fh+ah) > 1152) {
				pw--;
				ph--;
			} else break;
		}

		/* other than 8bpp is broken, so use that for now */
		png = png_create(
			(pw * fw + aw) * rw,		/* image width */
			24 + (ph * fh + ah) * rh,	/* image height */
			COLOR_PALETTE,			/* palette image */
			2,				/* 2 bits per pixel */
			png_fp,				/* output cookie */
			png_write);			/* output callback */

		if (!png) {
			fprintf(stderr, "Fatal: creation of PNG image failed (%s)\n",
				strerror(errno));
			return -1;
		}

		png_set_palette(png, 0, PNG_RGB(255,255,255));
		png_set_palette(png, 1, PNG_RGB(  0,  0,  0));
		png_set_palette(png, 2, PNG_RGB( 32, 32,160));
		png_set_palette(png, 3, PNG_RGB(128,128,128));

		png->comment = "Created by aldump";
		png->author = "Juergen Buchmueller <pullmoll@t-online.de>";

		/* draw a border around the image */
		for (x = 0; x < (pw * fw + aw) * rw; x++) {
			png_put_pixel(png, x, 0, 3, 0);
			png_put_pixel(png, x, 23, 3, 0);
			png_put_pixel(png, x, 24 + (ph * fh + ah) * rh - 1, 3, 0);
		}

		for (y = 0; y < 24 + (ph * fh + ah) * rh; y++) {
			png_put_pixel(png, 0, y, 3, 0);
			png_put_pixel(png, (pw * fw + aw) * rw - 1, y, 3, 0);
		}

		png_printf(png, 2, 2,
			"Font: %s; size: %d words; MD5: %s",
			basename, size/2, md5_digest((uint8_t *)al, size));

		if (mw != fw) {
			png_printf(png, 2, 2 + FONTH,
				"max. width: %d (header: %d); height: %d; ascent: %d; fixed: %s",
				 fw, mw, fh, ascent, fixed ? "yes" : "no");
		} else {
			png_printf(png, 2, 2 + FONTH,
				"max. width: %d; height: %d; ascent: %d; fixed: %s",
				 fw, fh, ascent, fixed ? "yes" : "no");
		}

		for (i = first; i <= last; i++) {
			int x0 = (pw * fw + aw) * (i % rw) + 2;
			int y0 = 24 + (ph * fh + ah) * ((i - first) / rw) + ah - 3;

			h = extract_xw(bitmap, &w, al, size, i, fw, fh);
			if (0 == h)
				continue;	/* undefined character */

			png_printf(png, x0, y0 - FONTH, "[%d]", i);

			if (pw > 1) {
				/* draw a grid for the character */
				for (x = 0; x < pw * w; x++) {
					for (y = 0; y <= ph * fh; y += ph) {
						png_put_pixel(png, x0 + x, y0 + y,
							y == ascent * ph ? 2 : 3, 0);
					}
				}
				for (y = 0; y < ph * fh; y++) {
					for (x = 0; x <= pw * w; x += pw) {
						png_put_pixel(png, x0 + x, y0 + y, 3, 0);
					}
				}
			} else {
				/* draw a border for the character */
				for (x = 0; x < pw * w; x++) {
					png_put_pixel(png, x0 + x, y0 - 1, 1, 0);
					png_put_pixel(png, x0 + x, y0 + ph*fh, 3, 0);
				}
				for (y = 0; y < ph * fh; y++) {
					png_put_pixel(png, x0 - 1, y0 + y, 1, 0);
					png_put_pixel(png, x0 + pw*w, y0 + y, 3, 0);
				}
			}

			for (y = 0; y < fh; y++)
				png_bits(png, x0, y0, pw, ph, w, y, &bitmap[y*BITMAPW]);
		}
		png_finish(png);

		fclose(png_fp);
	} else {

		printf("font size is %d words\n", size / 2);

		printf("max. width: %d\n", fw);
		printf("height    : %d\n", fh);
		printf("ascent    : %d\n", ascent);
		printf("fixed font: %d\n", fixed);

		for (i = 0; i < 256; i++) {
			h = extract_xw(bitmap, &w, al, size, i, fw, fh);
			if (0 == h)
				continue;
			printf("\nchar:%d (%#x, '%c') w:%d h:%d\n",
				i, i, i >= 32 && i < 127 ? i : '.',
				w, h);
			for (y = 0; y < fh; y++)
				printf("[%s]\n", binstr(&bitmap[y*BITMAPW], w));
		}
	}
	free(bitmap);
	free(al);
	return 0;
}

int main(int argc, char **argv)
{
	char *pngfile = NULL;
	char *program;
	int i;

	program = strrchr(argv[0], '/');
	if (program)
		program++;
	else
		program = argv[0];

	if (argc < 2) {
		printf("usage: %s [options] altofont.al\n", program);
		printf("options may be one of:\n");
		printf("-png filename.png\toutput PNG image of the font.\n");
		printf("-md5\t\toutput PNG image to a file <MD5 of font>.png\n");
		printf("-v\t\tverbose operation\n");
		return 1;
	}

	for (i = 1; i < argc; i++) {
		if (argv[i][0] == '-') {
			if (!strcmp(argv[i], "-png")) {
				++i;
				pngfile = argv[i];
			}
			if (!strcmp(argv[i], "-md5")) {
				pngfile = argv[i];
			}
			if (!strcmp(argv[i], "-v")) {
				verbose = 1;
			}
		} else {
			dump_font(argv[i], pngfile);
		}
	}

	return 0;
}
