/*
 * Copyright (C) 2000, Matias Atria
 *
 * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
 */

#include <config.h>
#include <stdlib.h>
#include <stdarg.h>
#include <string.h>
#include <sys/stat.h>
#include <unistd.h>

#include "mdvi.h"
#include "private.h"

static int tfm_load_font __PROTO((DviParams *, DviFont *));
static int tfm_font_get_glyph __PROTO((DviParams *, DviFont *, int));

DviFontInfo tfm_font_info = {
	"TFM",
	0, /* scaling not supported by format */
	tfm_load_font,
	tfm_font_get_glyph,
	mdvi_shrink_box,
	mdvi_shrink_box,
	NULL,	/* free */
	NULL,	/* reset */
	NULL,	/* lookup */
	kpse_tfm_format,
	NULL
};

DviFontInfo ofm_font_info = {
	"OFM",
	0, /* scaling not supported by format */
	tfm_load_font,
	tfm_font_get_glyph,
	mdvi_shrink_box,
	mdvi_shrink_box,
	NULL,	/* free */
	NULL,	/* reset */
	NULL,	/* lookup */
	kpse_ofm_format,
	NULL
};

DviFontInfo afm_font_info = {
	"AFM",
	0, /* scaling not supported by format */
	tfm_load_font,
	tfm_font_get_glyph,
	mdvi_shrink_box,
	mdvi_shrink_box,
	NULL,	/* free */
	NULL,	/* reset */
	NULL,	/* lookup */
	kpse_afm_format,
	NULL
};

/*
 * Although it does not seem that way, this conversion is independent of the
 * shrinking factors, within roundoff (that's because `conv' and `vconv'
 * have already been scaled by hshrink and vshrink, repsectively). We
 * should really use `dviconv' and `dvivconv', but I'm not so sure those
 * should be moved to the DviParams structure.
 */
#define XCONV(x)	FROUND(params->conv * (x) * params->hshrink)
#define YCONV(y)	FROUND(params->vconv * (y) * params->vshrink)

/* this is used quite often in several places, so I made it standalone */
int	get_tfm_chars(DviParams *params, DviFont *font, TFMInfo *info, int loaded)
{
	Int32	z, alpha, beta;
	int	n;
	DviFontChar *ch;
	TFMChar	*ptr;

	n = info->hic - info->loc + 1;
	if(n != FONT_GLYPH_COUNT(font)) {
		font->chars = mdvi_realloc(font->chars,
			n * sizeof(DviFontChar));
	}
	font->loc = info->loc;
	font->hic = info->hic;
	ch = font->chars;
	ptr = info->chars;

	/* Prepare z, alpha and beta for TFM width computation */
	TFMPREPARE(font->scale, z, alpha, beta);

	/* get the character metrics */
	for(n = info->loc; n <= info->hic; ch++, ptr++, n++) {
		int	a, b, c, d;

		ch->offset = ptr->present;
		if(ch->offset == 0)
			continue;
		/* this is what we came here for */
		ch->tfmwidth    = TFMSCALE(z, ptr->advance, alpha, beta);
		/* scale all other TFM units (so they are in DVI units) */
		a = TFMSCALE(z, ptr->left, alpha, beta);
		b = TFMSCALE(z, ptr->right, alpha, beta);
		c = TFMSCALE(z, ptr->height, alpha, beta);
		d = TFMSCALE(z, ptr->depth, alpha, beta);

		/* now convert to unscaled pixels */
		ch->width = XCONV(b - a);
		ch->height = YCONV(c - d);
		if(ch->height < 0) ch->height = -ch->height;
		ch->x = XCONV(a);
		ch->y = YCONV(c);
		/*
		 * the offset is not used, but we might as well set it to
		 * something meaningful (and it MUST be non-zero)
		 */
		ch->flags       = 0;
		ch->code        = n;
		ch->glyph.data  = NULL;
		ch->grey.data   = NULL;
		ch->shrunk.data = NULL;
		ch->loaded      = loaded;
	}

	return 0;
}

/*
 * We use this function as a last resort to find the character widths in a
 * font The DVI rendering code can correctly skip over a glyph if it knows
 * its TFM width, which is what we try to find here.
 */
static int tfm_load_font(DviParams *params, DviFont *font)
{
	TFMInfo	*tfm;
	int	type;

	switch(font->search.info->kpse_type) {
	case kpse_tfm_format:
		type = DviFontTFM;
		break;
	case kpse_afm_format:
		type = DviFontAFM;
		break;
	case kpse_ofm_format:
		type = DviFontOFM;
		break;
	default:
		return -1;
	}

	/* we don't need this */
	if(font->in) {
		fclose(font->in);
		font->in = NULL;
	}
	tfm = get_font_metrics(font->fontname, type, font->filename);
	if(tfm == NULL)
		return -1;

	if(tfm->checksum && font->checksum && tfm->checksum != font->checksum) {
		mdvi_warning(_("%s: Checksum mismatch (got %u, expected %u)\n"),
			     font->fontname, (unsigned)tfm->checksum,
			     (unsigned)font->checksum);
	}
	font->checksum = tfm->checksum;
	font->design = tfm->design;
	font->loc = 0;
	font->hic = 0;
	font->chars = NULL;
	get_tfm_chars(params, font, tfm, 1);

	/* free everything */
	free_font_metrics(tfm);

	return 0;
}

static int tfm_font_get_glyph(DviParams *params, DviFont *font, int code)
{
	DviFontChar *ch;

	ch = FONTCHAR(font, code);
	if(!glyph_present(ch))
		return -1;
	ch->glyph.x = ch->x;
	ch->glyph.y = ch->y;
	ch->glyph.w = ch->width;
	ch->glyph.h = ch->height;
	/*
	 * This has two purposes: (1) avoid unnecessary calls to this function,
	 * and (2) detect when the glyph data for a TFM font is actually used
	 * (we'll get a SEGV). Any occurrence of that is a bug.
	 */
	ch->glyph.data = MDVI_GLYPH_EMPTY;

	return 0;
}
