/*
 *			Dump embedded fonts in a DVI file (utility for dviout)
 *					Aug. 2001, written by SHIMA
 */

#include <stdio.h>
#include <stdlib.h>
#include <io.h>
#include <fcntl.h>
#include <string.h>

#define	PK_FONT			4
#define	CMTTF			11
#define	WINTT_FONT		12
#define	VIRTUAL_FONT	13
#define	TT_FONT			17
#define	WINJTT_FONT		18

#define	ETF_COMPOSITE	0x40000000
#define	F_ON			0x80000000

#define	ETT_FONT	struct ETT_FONT_REC

struct ETT_FONT_REC {
	int tfm_width;
	int tfm_height;
	int tfm_depth;

	short int unitsPerEm;
	short int xMin;
	short int yMin;

	short int extra;
	short int yoffset;

	short int width_adj;
	short int height_adj;

	short int f_goth;

	short int long_wide;
	short int slant;
	short int draw_sw;
	short int threshold;
	short int thin;
	short int _xfat;
	short int _yfat;
	short int rotation;
};

struct	FONT_INDEX
{
	int name;		// name of the fonts (to be called)
	int resol;		// resolution  (resol | (base_resol << 16))
	int type;		// font_type (CMTTF, TT_FONT, PK_FONT, etc.)
	int num;		// number of characters
	int Hoffset;	// offset to the Header (Glyph Base)
	int Coffset;	// offset of table : code -> glyph (Glyph Base)
};

#define	GetInt(x)		(*(int *)(buf+(x)))
#define	GetShort(x)		(*(short int *)(buf+(x)))
unsigned char *buf;
int glyph;
int	hoffset;
int coffset;
int	etf_version;
int f_init;
int f_etf;
int	f_dump;
int	f_fnum;
int	f_cnum;
int	f_short;

#define	FALSE	0
#define	TRUE	1

void PrintFont(int offset, int mode);

int CheckETF(void)
{
	if(buf[0] != 'E' || buf[1] != 'F'){
		printf("Not a ETF file\n");
		return FALSE;
	}
	etf_version = (buf[3]<<16) + buf[2];
	return TRUE;
}

int DumpGlyph(int fnum, int cnum)
{
	struct FONT_INDEX *font_index;
	int i, j, pos, size, maxcnum, count;

	if(!CheckETF())
		return FALSE;
	if(fnum < 0 || fnum >= GetInt(16)){
		printf("No font #%d\n", fnum);
		return FALSE;
	}
	if(!f_short || (f_short && cnum < 0)){
		i = f_short;
		f_short |= F_ON;
		printf("\n\n  %d:", fnum);
		PrintFont(GetInt(20) + fnum*24, 1);
		if(i)
			return TRUE;
		f_short = 0;
	}
	if(etf_version <= 3){
		printf("ETF Version %d.%2x is too old for this command\n"
			"Use Ver.0.03 or later\n", buf[3], buf[2]);
		return FALSE;
	}
	glyph = GetInt(12);
	font_index = (struct FONT_INDEX *)(buf + GetInt(20)) + fnum;
	hoffset = font_index->Hoffset + glyph;
	coffset = font_index->Coffset + glyph;
	maxcnum = font_index->num;

	pos = i = -1;
	switch(font_index->type){
		case PK_FONT:
		case CMTTF:
		case TT_FONT:
		case VIRTUAL_FONT:
		case WINTT_FONT:
			if(cnum == -1){
				if(f_short)
					printf("\n%3d:%s", f_fnum, buf + font_index->name);
rep:			if(++i >= maxcnum || i < 0)
					break;
				j = GetInt(coffset + 8*i);
				goto getpos;
			}else{
				for(i = 0; i < maxcnum; i++){
					if(GetInt(coffset + 8*i) == cnum){
						j = cnum;
getpos:					printf("\n\nCode : %04X", j & ~ETF_COMPOSITE);
						if(j & ETF_COMPOSITE)
							printf("* ");
						if(j >= 0x21 && j < 0x7f)
							printf("%c", j);
						else if(j > 0x2121){
							j = jistojms(j);
							printf(" %c%c", j>>8, j&0xff);
						}
						printf("\n");
						pos = glyph + GetInt(coffset + 8*i + 4);
						break;
					}
				}
				if(pos < 0){
					printf("Cannot find the character of the code %x\n",
						cnum & ~ETF_COMPOSITE);
					return FALSE;
				}
			}
			if(font_index->type == WINTT_FONT){
				printf("tfm width: %08x\n", pos);
				if(cnum == -1)
					goto rep;
				break;
			}

			if(font_index->type == VIRTUAL_FONT)
				size = GetInt(pos - 8);
			else{
				size = GetInt(coffset + 8*i + 12);
				if(i < maxcnum - 1)
					size -= (pos - glyph +  buf[coffset+8*maxcnum+7]);
				else
					size = size & 0xffff;
			}
			if(font_index->type == CMTTF)
				printf("lsb  : %4d\n", GetShort(pos - 6));
			else if(font_index->type == PK_FONT)
				printf("flag :   %02X\n", buf[pos-1]);
dump:		printf("Size : %04X\nAt   : %04X-%04X", size, pos, pos+size-1);
			for(j = 0; j < size; j++){
				switch(j & 15){
					case 8:
						printf("  ");
						break;
					case 0:
						printf("\n%05X: ", j);
						break;
					default:
						printf(" ");
						break;
				}
				printf("%02X", buf[pos++]);
			}
			if(cnum == -1)
				goto rep;
			break;

		case WINJTT_FONT:	// jfm
			pos = coffset;
			size = GetShort(coffset + 4) << 2;
			cnum = 0;
			goto dump;
	}
	return TRUE;
}

int PrintHeader(int size)
{
	int pos, num, tmp;

	if(!CheckETF())
		return FALSE;
	printf("\nETF Version %x.%02x\n", buf[3], buf[2]);
	etf_version = (buf[3]<<16) + buf[2];
	if( (num = GetInt(4)) != size ){
		printf("File size information %d is not correct(%d)\n", num, size);
		return FALSE;
	}
	printf("File size:\t%7d\nDefault dpi:\t", size);
	pos = (int)GetShort(8);
	if(!(pos & 0x8000))
		printf("%7d dpi\n",pos);
	else{
		pos &= 0x4fff;
		while((tmp = GetShort(pos)) != 0){
			printf("%7d ", tmp);
			pos += 2;
		}
		printf("dpi\n");
	}

	printf("Glyph Base:\t   %04d\n", GetInt(12));
	printf("Font count:\t%7d\n", GetInt(16));
	pos = GetShort(10);
	printf("Font type:");
	switch(pos & 3){
		case 1:
			printf(" TrueType(1 byte code)");
			break;
		case 2:
			printf(" TrueType(Japanese)");
			break;
		case 3:
			printf(" TrueType(1 byte code and Japanese)");
		break;
	}
	if(pos & 4)
		printf(" PK");
	if(pos & 8)
		printf(" Virtual");
	if(pos & 0x10)
		printf(" TFM");
	if(pos & 0x20)
		printf(" JFM");
	printf("\n");
	return TRUE;
}

int PrintFontTable(void)
{
	int i, pos;
	int fnum = GetInt(16);
	struct FONT_INDEX *font_index;

	for(i = 0; i < fnum; i++){
		font_index = (struct FONT_INDEX *)(buf + GetInt(20)) + i;
		printf("\n%3d:%s\n", i, buf + font_index->name);
		if((font_index->resol >> 16) != 0)
			printf("Resolution: %d/%d\n", font_index->resol & 0xffff, 
				font_index->resol >> 16);
		else
			printf("Resolution:\t    Any\n");
		printf("Font Type:\t%7d\n", font_index->type);
		printf("Characters:\t%7d\n", font_index->num);
		hoffset = font_index->Hoffset + glyph;
		printf("Header offset:\t  %05x(%05x)\n", hoffset, hoffset - glyph);
		coffset = font_index->Coffset + glyph;
		printf("Code Table offset:%05x(%05x)\n", coffset, coffset - glyph);
	}
ext:
	return fnum;
}

#define	get_ubyte(x)	(unsigned char)(*x++)
#define	get_long(pk)	get_nbyte(&pk, 4)

int get_nbyte(unsigned char **pk, int count)
{
	int	num;
	unsigned char *pt;

	*pk = (pt = *pk) + count;
	for(num = 0; count-- > 0; )
		num = (num << 8) + *pt++;
	return num;
}

void PrintLocalFont(unsigned char *pk)
{
	int	cmd;
#define	FNT_DEF_1	243
#define	PRE			247

	for (cmd = get_ubyte(pk); cmd >= FNT_DEF_1 && cmd < PRE;) {
		printf(" Font Code:\t%8d\n", get_nbyte(&pk, cmd - FNT_DEF_1 + 1));
		printf(" c para:\t%8d\n", get_long(pk));
		printf(" size para:\t%8d\n", get_long(pk));
		printf(" design para:\t%8d\n", get_long(pk));
		cmd = get_ubyte(pk);
		cmd += get_ubyte(pk);
		printf(" name:\t\t");
		while(cmd-- > 0)
			printf("%c", get_ubyte(pk));
		printf("\n\n");
		cmd = get_ubyte(pk);
	}
}

int jistojms(int c)
{
	int hi, lo;

	hi = (c >> 8) & 0xFF;
	lo = c & 0xFF;
	if (hi & 1)
		lo += 0x1f;
	else
		lo += 0x7d;
	hi = ((hi - 0x21) >> 1) + 0x81;
	if (lo >= 0x7f)
		lo++;
	if (hi > 0x9f)
		hi += 0x40;
	return ((hi << 8) | lo);
}

void PrintFont(int offset, int mode)
{
	struct FONT_INDEX *font_index;
	ETT_FONT *ett;
	int cnum, i, type, ix, code, pos, num, extra, *tfw, pos2, size, comp;

	font_index = (struct FONT_INDEX *)(buf+offset);
	cnum = font_index->num;
	hoffset = font_index->Hoffset + glyph;
	coffset = font_index->Coffset + glyph;
	printf("%s - ", buf + font_index->name);
	extra = 0;
	switch(font_index->type){
		case PK_FONT:
			printf("PK\n");
			break;

		case CMTTF:
			printf("TrueType(1 byte)\n");
			printf("UnitsPerEM:\t%7d\n", GetInt(hoffset));
			if(etf_version >= 3){
				extra = GetInt(hoffset+4);
exttfm:		if(etf_version >= 5)
				printf("file:\t%15s\n", buf + coffset + cnum*8 + 8);
			if(extra){
					printf("Extra tfm:\t%7d\n", num = extra);
					tfw = (int *)(buf+hoffset-8*num);
					if(!f_short){
						while(--num >= 0){
							printf(" %04x:\t\t%7d\n", *tfw, tfw[1]);
							tfw += 2;
						}
					}
				}
			}
			break;

		case WINTT_FONT:
			printf("TFM\n");
			break;

		case VIRTUAL_FONT:
			printf("Virtual\n");
			PrintLocalFont(buf+hoffset);	// show font list
			break;

		case TT_FONT:
			printf("JFM\n");
			ett = (ETT_FONT *)(buf+hoffset);
			printf("tfm width:\t%7d\n",		ett->tfm_width);
			printf("tfm depth:\t%7d\n",		ett->tfm_depth);
			printf("tfm height:\t%7d\n",	ett->tfm_height);
			printf("unitsPerEm:\t%7d\n",	ett->unitsPerEm);
			printf("xMin:\t\t%7d\n",		ett->xMin);
			printf("yMin:\t\t%7d\n",		ett->yMin);
			printf("extra tfm:\t%7d\n",		ett->extra);
			printf("y offset:\t%7d\n",		ett->yoffset);
			printf("width_adj:\t%7d\n",		ett->width_adj);
			printf("height_adj:\t%7d\n",	ett->height_adj);
			printf("gothic:\t\t%7d\n",		ett->f_goth);
			printf("long/wide:\t%7d\n",		ett->long_wide);
			printf("slant:\t\t%7d\n",		ett->slant);
			printf("draw switch:\t%7d\n",	ett->draw_sw);
			printf("threshold:\t%7d\n",		ett->threshold);
			printf("thin:\t\t%7d\n",		ett->thin);
			printf("_x fat:\t\t%7d\n",		ett->_xfat);
			printf("_y fat:\t\t%7d\n",		ett->_yfat);
			printf("rotation:\t%7d\n",		ett->rotation);
			extra = ett->extra;
			goto exttfm;

		case WINJTT_FONT:
			printf("TrueType(Japanese)\n");
			printf("name:\t %s\n", buf + hoffset);
			break;

	}
	if(font_index->type != WINJTT_FONT)
		printf("Characters:\t%7d\n", cnum);
	printf("Header offset:\t  %05x(%05x)\n", hoffset, hoffset - glyph);
	if(font_index->type != WINJTT_FONT)
		printf("Code table offset:");
	else
		printf("JFM offset:\t  ");
	printf("%05x(%05x)\n", coffset, coffset - glyph);
	if(f_short || (!mode && coffset == font_index[1].Coffset + glyph))
		return;
	for(i = 0; i < cnum; i++){
		code = GetInt(coffset + 8*i);
		if(code & ETF_COMPOSITE){
			code &= ~ETF_COMPOSITE;
			comp = '*';
		}else
			comp = ':';
		pos = GetInt(coffset + 8*i + 4) + glyph;
		if(font_index->type == WINTT_FONT)
			printf("%04x%c\twidth(%7d)", code, comp, pos - glyph);
		else
			printf("%04x%c\t%05x(%05x)", code, comp, pos, pos - glyph);

		if(etf_version > 3){
			switch(font_index->type){
				case  CMTTF:
				case  TT_FONT:
				case  PK_FONT:
					pos2 = GetInt(coffset + 8*i + 12);
					if(i < cnum - 1)
						pos2 += glyph -  buf[coffset+8*cnum+7];
					else
						pos2 = pos + (pos2 & 0xffff) - 1;
					printf("-%05x", pos2);
			}
		}

		switch(font_index->type){
			case PK_FONT:
				printf(" flag(%02x)", buf[glyph+pos-1]);
				goto prtcode;

			case CMTTF:
				size = GetInt(pos - 4);
				if(extra){
					tfw = (int *)(glyph+hoffset-4);
					while(--num >= 0){
						if(*tfw <= code){
							if(*tfw == code)
								size = tfw[1];
							break;
						}
						tfw -= 2;
					}
				}
				printf(" lsb(%4d) width(%7d)", GetShort(pos - 6), size);

			case WINTT_FONT:
prtcode:		if(code >= 0x21 && code < 0x7f && comp == ':')
					printf(" %c", code);
				break;

			case VIRTUAL_FONT:
				size = GetInt(pos-8);
				printf("-%05x  size(%04x)  width(%7d)", 
					pos + size - 1, size, GetInt(pos - 4));
				break;

			case TT_FONT:
				code = jistojms(code);
				printf(" %c%c", code>>8, code&0xff);

		}
		printf("\n");
	}
}


void dump(int size)
{
	int num, fnum;

	glyph = GetInt(12);
	if(f_dump)
		DumpGlyph(f_fnum, f_cnum);
	else if(PrintHeader(size)){
		fnum = PrintFontTable();
		for(num = 0; num < fnum; num++){
			printf("\n\n  %d:", num);
			PrintFont(GetInt(20) + num*24, (num == fnum - 1)?1:0);
		}
	}
}

#define	read_byte(x)	getc(x)

int read_long(FILE *fp)
{
	int i;

	i  = read_byte(fp) << 24;
	i += read_byte(fp) << 16;
	i += read_byte(fp) << 8;
	return i + read_byte(fp);
}

int read_post(FILE *fp)
	/* read POSTAMBLE */
{
#define	MAX_INCL 2048
#define	ID			2
#define	IDP			3
#define	END_DVI		223
#define POST        248
#define EOP         140
#define AdID		(('A'<<24)+('d'<<16)+('O'<<8)+EOP)

	int code, i, j, size, endofs, pt_post, post, last_bop;
	int num_add, bofn, eofn, top_add;
	char *s;
	int	pt_bod[MAX_INCL], lod[MAX_INCL];
	char *pt_name[MAX_INCL];

	if ( read_byte(fp) != PRE ||
		((code = read_byte(fp)) != ID && code != IDP) )
err:	return -1;

	for (endofs = -3L; fseek(fp, endofs, SEEK_END),
		 (code = read_byte(fp)) != ID && code != IDP; endofs--)
		/* Search id number */
		if (code == EOF || code != END_DVI)
			goto err;
	fseek(fp, endofs - 4L, SEEK_END);
	pt_post = ftell(fp);
	if ((top_add = post = read_long(fp)) <= 0)
		goto err;
	/* Read the position of POSTAMBLE */

	fseek(fp, post - 4, SEEK_SET);
	/* Set file-ptr at POSTAMBLE */

	num_add = (AdID == read_long(fp))?1:0;
	if((code = read_byte(fp)) != POST)
		goto err;
	if((last_bop = read_long(fp)) <= 0)
		goto err;
	fseek(fp, post - 20, SEEK_SET);

	if(num_add){
		bofn = read_long(fp);
		eofn = read_long(fp);
		num_add = read_long(fp);
		top_add = read_long(fp);
		fseek(fp, post - 20 - 8*num_add, SEEK_SET);
		for(i = 0; i < num_add; i++){
			pt_bod[i] = read_long(fp);
			lod[i] = read_long(fp);
		}
		fseek(fp, bofn, SEEK_SET);
		size = eofn - bofn;
		s = malloc(size);
		for(i = 0; i < size; i++)
			s[i] = read_byte(fp);
		pt_name[0] = s;
		for(i = j = 0; i < size; ){
			if(!s[i++])
				pt_name[++j] = s + i;
		}
	}
	if(!f_etf){
		for(i = 0; i < num_add; i++){
			if(!strcmp(pt_name[i], "initial.par")){
				fseek(fp, pt_bod[i], SEEK_SET);
				buf = malloc(lod[i]+1);
				buf[lod[i]] = 0;
				fread(buf, lod[i], 1, fp);
				if(f_init)
					printf("%s", buf);
				else
					printf("\ninitial.par:\t%7d byte\n%s\n\n", lod[i], buf);
				if(buf != NULL)
					free(buf);
				buf = NULL;
				break;
			}
		}
		if(i >= num_add && f_init)
			printf("# initial.par does not exists\n");
	}
	if(!f_init){
		for(i = 0; i < num_add; i++){
			if(!strcmp(pt_name[i], "dviout.etf")){
				fseek(fp, pt_bod[i], SEEK_SET);
				buf = malloc(lod[i]);
				fread(buf, lod[i], 1, fp);
				free(pt_name[0]);
				return lod[i];
			}
		}
	}
	if(num_add)
		free(pt_name[0]);
	return 0;
}

void usage(void)
{
	printf(
	"\t\tDump Embedded Fonts in a DVI file for dviout (Ver.0.3)\n"
	"\t\t\tAug. 2001, written by SHIMA\n\n"
	"Usage: etfdump [-[i][e][s]] [-d<fnum>[:<cnum>[*]]] <foo>[.dvi]|dviout.etf\n"
	"-i   : initial.par\n"
	"-e   : embedded fonts\n"
	"-s   : short\n"
	"-d   : dump data with font <fnum> and code <cnum>(* composite glyph)\n"
	"       <fnum> and <cnum> are numbers shown by etfdump\n\n"
	"example:etfdump foo\n"
	"\tetfdump -d4:2422 test_b5x\n"
	"\tetfdump -d4 test_b5x\n"
	"\tetfdump -sd4 test_b5x\n"
	"\tetfdump -i test_b5x\n"
	);
	exit(2);
}

int hex2i(int c)
{
	if(c >= '0' && c <='9')
		return c - '0';
	c |= 0x20;
	if(c >= 'a' && c <= 'f')
		return c - ('a' - 10);
	return -1;
}

void main(int argc, char **argv)
{
	FILE *fp;
	int i, j, ch, length, res;
	char fname[0x200];

	if(argc < 2)
		usage();

	strcpy(fname, argv[argc-1]);
	for(i = 1; i < argc-1; i++){
		if(argv[i][0] == '-'){
			for(j = 1; argv[i][j]; ){
				switch(argv[i][j++]){
					case 'i':
						f_init |= F_ON;
						break;

					case 'e':
						f_etf |= F_ON;
						break;

					case 's':
						f_short |= F_ON;
						break;

					case 'd':
						if(argv[i][j] < '0' || argv[i][j] > '9')
							break;
						f_dump |= F_ON;
						f_etf  |= F_ON;
						f_fnum = atoi(argv[i] + j);
						while(argv[i][j] >= '0' && argv[i][j] <= '9')
							j++;
						if(argv[i][j] == 0){
							f_cnum = -1;
							break;
						}
						if(argv[i][j] != ':'){
							f_dump = 0;
							break;
						}
						j++;
						while((ch = hex2i(argv[i][j])) >= 0){
							f_cnum = f_cnum*16 + ch;
							j++;
						}
						if(argv[i][j] == '*')
							f_cnum |= ETF_COMPOSITE;
				}
			}
		}
	}
	if((fp = fopen(fname, "rb")) == NULL){
		length = strlen(fname);
		if(length <= 4 || strcmp(fname + length - 4, ".dvi"))
			strcpy(fname+length, ".dvi");
		fp = fopen(fname, "rb");
		if(fp == NULL){
			printf("Cannot find %s\n", fname);
			return;
		}
	}
	length = filelength(fileno(fp));
	if(length <= 0){
		printf("%s is NULL file\n", fname);
		return;
	}
	res = read_post(fp);
	if(f_init)
		return;
	if(res == 0){
		printf("No embedded font in %s\n", fname);
		exit(1);
	}else if(res > 0)
		length = res;
	else{
		buf = malloc(length);
		if(buf == NULL)
			return;
		fseek(fp, 0, SEEK_SET);
		fread(buf, length, 1, fp);
	}
	dump(length);
	free(buf);
}
