
/*
 *    Copyright 1986, 1987 Pat Joseph Monardo. All rights reserved.
 *    Copying of this file is granted according to the provisions 
 *    specified in the file COPYING which must accompany this file.
 */


/*
 *		tfm.c
 */

#include "tex.h"
#include "cmds.h"
#include "heap.h"
#include "arith.h"
#include "eq.h"
#include "hash.h"
#include "box.h"
#include "scan.h"
#include "token.h"
#include "tokenstack.h"
#include "str.h"
#include "io.h"
#include "file.h"
#include "print.h"
#include "error.h"
#include "tfm.h"

/*
 *	Read and check the font data;
 *	If the TFM file is malformed, goto bad_tfm;
 *	If there is no room for this font, say so and goto done;
 *	otherwise, incr(font_ptr) and goto done.
 */

byte_file		tfm_file;

fourq	null_character;

ptr		fmem_ptr;
mword	font_info[FONT_MEM_SIZE];

fnt		font_ptr = FONT_BASE;
fourq	font_check[FONT_MAX];
scal	font_size[FONT_MAX];
scal	font_dsize[FONT_MAX];
hword	font_params[FONT_MAX];
str		font_name[FONT_MAX];
str		font_area[FONT_MAX];
byte	font_bc[FONT_MAX];
byte	font_ec[FONT_MAX];
ptr		font_glue[FONT_MAX];
bool	font_used[FONT_MAX];
int		hyphen_char[FONT_MAX];
int		skew_char[FONT_MAX];
int		char_base[FONT_MAX];
int		width_base[FONT_MAX];
int		height_base[FONT_MAX];
int		depth_base[FONT_MAX];
int		italic_base[FONT_MAX];
int		lig_kern_base[FONT_MAX];
int		kern_base[FONT_MAX];
int		exten_base[FONT_MAX];
int		param_base[FONT_MAX];

#define	start_font_error_message() \
	{print_err("Font "); sprint_cs(u); \
	print_char('='); print_file_name(nom, aire, null_str); \
	if (s >= 0) {print(" at "); print_scaled(s); print("pt");} \
	else if (s != -1000) {print(" scaled "); print_scaled(-s);}}

#define	get_font_byte() \
	(font_byte = getc(tfm_file))

#define	read_sixteen(X) \
	{get_font_byte(); X = font_byte; \
	if (X > 127) goto bad_tfm; \
	get_font_byte(); X = (X) * 0400 + font_byte;}

#define	store_four_quarters(Q) \
	{a = get_font_byte(); b = get_font_byte(); \
	c = get_font_byte(); d = get_font_byte(); \
	qw.b0 = qi(a); qw.b1 = qi(b); \
	qw.b2 = qi(c); qw.b3 = qi(d); Q = qw;}

#define	store_scaled(S) \
	{a = get_font_byte(); b = get_font_byte(); \
	c = get_font_byte(); d = get_font_byte(); \
	sw = ((((d * z) / 0400) + (c * z)) / 0400 + (b * z)) / beta; \
	if (a == 0) S = sw; else if (a == 255) S = sw - alpha; \
	else goto bad_tfm;}

#define	check_byte_range(C) 	{if (C < bc || C > ec) goto bad_tfm;}
#define	ccbwo					(k + bc - fmem_ptr)
#define	adjust(I)				(I[f] = qo(I[f]))

fnt
read_font_info (u, nom, aire, s)
	fnt		u;
	str		nom;
	str		aire;
	scal	s;
{
	byte	a;
	byte	b;
	byte	c;
	byte	d;
	fnt		f;
	fnt		g;
	int		k;
	scal	z;
	hword	bc;
	hword	ec;
	hword	lf;
	hword	lh;
	hword	nd;
	hword	ne;
	hword	nh;
	hword	ni;
	hword	nk;
	hword	nl;
	hword	np;
	hword	nw;
	fourq	qw;
	scal	sw;
	val		alpha;
	hword	beta;
	bool	file_opened;
	byte	font_byte;
	fourq	dev_null;
/*
 * 	Open tfm_file for input
 */
	g = NULL_FONT;
	file_opened = FALSE;
	pack_file_name(nom, aire, str_tfm);
	if ((tfm_file = b_open_in()) == NULL)
		goto bad_tfm;
	file_opened = TRUE;
/*
 *	Read the TFM size fields
 */
	read_sixteen(lf);
	read_sixteen(lh);
	read_sixteen(bc);
	read_sixteen(ec);
	if (bc > ec + 1 || ec > 255) goto bad_tfm;
	read_sixteen(nw);
	read_sixteen(nh);
	read_sixteen(nd);
	read_sixteen(ni);
	read_sixteen(nl);
	read_sixteen(nk);
	read_sixteen(ne);
	read_sixteen(np);
	if (lf != 6 + lh + (ec - bc + 1) + nw + nh + nd + ni + nl + nk + ne + np) 
		goto bad_tfm;
/*
 *	Use size fields to allocate font information
 */
	lf = lf - 6 - lh;
	if (np < 7) lf += 7 - np;
	if (font_ptr == FONT_MAX-1 || fmem_ptr + lf >= FONT_MEM_SIZE) {
		start_font_error_message();
		print(" not loaded: No more room");
		help_font();
		error();
		goto done;
	}
	f = font_ptr + 1;
	char_base[f] = fmem_ptr - bc;
	width_base[f] = char_base[f] + ec + 1;
	height_base[f] = width_base[f] + nw;
	depth_base[f] = height_base[f] + nh;
	italic_base[f] = depth_base[f] + nd;
	lig_kern_base[f] = italic_base[f] + ni;
	kern_base[f] = lig_kern_base[f] + nl;
	exten_base[f] = kern_base[f] + nk;
	param_base[f] = exten_base[f] + ne;
/*
 *	Read the TFM header
 */
	if (lh < 2) goto bad_tfm;
	store_four_quarters(font_check[f]);
	read_sixteen(z);
	get_font_byte();
	z = z * 0400 + font_byte;
	get_font_byte();
	z = z * 020 + font_byte / 020;
	if (z < UNITY) goto bad_tfm;
	while (lh > 2) {
		store_four_quarters(dev_null);
		decr(lh);
	}
	font_dsize[f] = z;
	if (s != -1000) {
		if (s >= 0)
			z = s;
		else z = xn_over_d(z, -s, 1000L);
	}
	font_size[f] = z;
/*
 *	Read  character data
 */
	for (k = fmem_ptr; k < width_base[f]; incr(k)) {
		store_four_quarters(font_info[k].qqqq);
		if (a >= nw || b / 020 >= nh || b % 020 >= nd || c / 4 > ni) 
			goto bad_tfm;
		switch (c % 4)
		{
		case LIG_TAG:
			if (d >= nl) goto bad_tfm;
			break;
		
		case EXT_TAG:
			if (d >= ne) goto bad_tfm;
			break;
		
		case LIST_TAG:
			check_byte_range(d);
			while (d < ccbwo) {
				qw = char_info(f, d);
				if (char_tag(qw) != LIST_TAG)
					goto not_found;
				d = qo(rem_byte(qw));
			}
			if (d == ccbwo) goto bad_tfm;
			break;

		not_found:
			break;
		}
	}
/*
 *	Read box dimensions
 */
	alpha = 16 * z; beta = 16;
	while (z >= 040000000) {
		z >>= 1;
		beta >>= 1;
	}
	
	for (k = width_base[f]; k < lig_kern_base[f]; incr(k))
		store_scaled(font_info[k].sc);
	if (font_info[width_base[f]].sc != 0) 
		goto bad_tfm;
	if (font_info[height_base[f]].sc != 0) 
		goto bad_tfm;
	if (font_info[depth_base[f]].sc != 0) 
		goto bad_tfm;
	if (font_info[italic_base[f]].sc != 0) 
		goto bad_tfm;
/*
 *	Read ligature/kern programs
 */
	for (k = lig_kern_base[f]; k < kern_base[f]; incr(k)) {
		store_four_quarters(font_info[k].qqqq);
		check_byte_range(b);
		if (c < KERN_FLAG) { 
			check_byte_range(d);
		} else if (d >= nk)
			goto bad_tfm;
	}
	if (nl > 0 && a < STOP_FLAG)
		goto bad_tfm;

	for (k = kern_base[f]; k < exten_base[f]; incr(k))
		store_scaled(font_info[k].sc);
/*
 *	Read extensible character recipes
 */
	for (k = exten_base[f]; k < param_base[f]; incr(k)) {
		store_four_quarters(font_info[k].qqqq);
		if (a != 0)
			check_byte_range(a);
		if (b != 0)
			check_byte_range(b);
		if (c != 0)
			check_byte_range(c);
		check_byte_range(d);
	}
/*
 *	Read font parameters
 */
	for (k = 1; k <= np; incr(k)) {
		if (k == 1) {
			get_font_byte();
			sw = font_byte;
			if (sw > 127)
				sw -= 256;
			get_font_byte();
			sw = sw * 0400 + font_byte;
			get_font_byte();
			sw = sw * 0400 + font_byte;
			get_font_byte();
			font_info[param_base[f]].sc = sw * 020 + font_byte / 020;
		} else
			store_scaled(font_info[param_base[f] + k - 1].sc);
	}
	if (feof(tfm_file))
		goto bad_tfm;

	for (k = np + 1; k <= 7; incr(k))
		font_info[param_base[f] + k - 1].sc = 0;
/*
 *	Make final adjustments and done
 */
	font_params[f] = (np >= 7) ? np : 7;
	hyphen_char[f] = default_hyphen_char;
	skew_char[f] = default_skew_char;
	font_name[f] = nom;
	font_area[f] = aire;
	font_bc[f] = bc;
	font_ec[f] = ec;
	font_glue[f] = NULL;
	adjust(char_base);
	adjust(width_base);
	adjust(lig_kern_base);
	adjust(kern_base);
	adjust(exten_base);
	decr(param_base[f]);
	fmem_ptr += lf;
	font_ptr = f;
	g = f;
	goto done;

bad_tfm:
	start_font_error_message();
	if (file_opened)
		print(" not loadable: Bad metric (TFM) file");
	else print(" not loadable: Metric (TFM) file not found");
	help_tfm();
	error();
	return NULL_FONT;

done:
	b_close(tfm_file);
	return g;
}

scan_font_ident ()
{	
	fnt		f;
	int		m;

	get_nbx_token();
	if (cur_cmd == DEF_FONT)
		f = cur_font;
	else if (cur_cmd == SET_FONT)
		f = cur_chr;
	else if (cur_cmd == DEF_FAMILY) {
		m = cur_chr;
		scan_four_bit_int();
		f = equiv(m + cur_val);
	} else {
		print_err("Missing font identifier");
		help_font_cs();
		back_error();
		f = NULL_FONT;
	}
	cur_val = f;
}

find_font_dimen (writing)
	bool	writing;
{
	fnt		f;
	int		n;

	scan_int();
	n = cur_val;
	scan_font_ident();
	f = cur_val;
	if (n <= 0)
		cur_val = fmem_ptr;
	else {
		if (writing &&
			n <= SPACE_SHRINK_CODE &&
			n >= SPACE_CODE &&
			font_glue[f] != NULL) {
			delete_glue_ref(font_glue[f]);
			font_glue[f] = NULL;
		}
		if (n > font_params[f]) {
			if (f < font_ptr)
				cur_val = fmem_ptr;
			else {
				do {
					if (fmem_ptr == FONT_MEM_SIZE)
						overflow("font_memory", FONT_MEM_SIZE);
					font_info[fmem_ptr].sc = 0;
					incr(fmem_ptr);
					incr(font_params[f]);
				} while (n != font_params[f]);
				cur_val = fmem_ptr - 1;
			}
		} else
			cur_val = n + param_base[f];
	}
	if (cur_val == fmem_ptr) {
		print_nl("! Font ");
		print_esc("");
		print_str(font_id_text(f));
		print(" has only ");
		print_int(font_params[f]);
		print(" fontdimen parameters");
		help_font_param();
		error();
	}
}

char_warning (f, c)
	fnt		f;
	byte	c;
{
	if (tracing_lost_chars > 0) {
		begin_diagnostic();
		print("Missing character: There is no ");
		print_ASCII(c);
		print(" in font ");
		print_str(font_name[f]);
		print_char('!');
		end_diagnostic(FALSE);
	}
}

ptr
new_character (f, c)
	fnt		f;
	byte	c;
{
	ptr		p;

	if (font_bc[f] <= c && font_ec[f] >= c) {
		if (char_exists(char_info(f, qi(c)))) {
			p = get_avail();
			font(p) = f;
			character(p) = qi(c);
			return p;
		}
	}
	char_warning(f, c);
	return NULL;
}

init_tfm ()
{
	fmem_ptr = 7;
	font_params[0] = 7;
	font_bc[0] = 1;
	font_glue[0] = zero_glue;
	hyphen_char[0] = '-';
	skew_char[0] = -1;
	param_base[0] = -1;
}

/*
 *	Help text
 */

help_font ()
{
	help4("I'm afraid I won't be able to make use of this font,",
	"because my memory for character-size data is too small.",
	"If you're really stuck, ask a wizard to enlarge me.",
	"Or maybe try `I\\font<same font id>=<name of loaded font>'.");
}

help_tfm ()
{
	help5("I wasn't able to read the size data for this font,",
	"so I will ignore the font specification.",
	"[Wizards can fix TFM files using TFtoPL/PLtoTF.]",
	"You might try inserting a different font spec;",
	"e.g., type `I\\font<same font id>=<substitute font name>'.");
}

help_font_param ()
{
	help2("To increase the number of font parameters, you must",
	"use \\fontdimen immediately after the font code is defined.");
}

help_font_cs ()
{
	help2("I was looking for a control sequence whose",
	"current meaning has been defined by \\font.");
}
