static char rcsid[] = "$Header: /usr/jjc/dvitops/RCS/font.c,v 1.6 90/03/12 18:47:02 jjc Exp $";

#include "dvitops.h"
 
#define MAXFONTSIZE (20*1024) /*maximum printer VM that a bitmap font can use*/
 
#ifdef PROTO
static void load_tfm_file(int f);
static void scale_font(int i, FILE *fp);
static void find_font(char *name, FILE *fp);
#if 0
static void choose_ps_prolog(void);
#endif
static void read_f_directory(void);
static int compare(char *p1, char *p2);
static void choose_mags(void);
static int order_mag(char *p1, char *p2);
static void emit_encodings(FILE *psfp);
#else
static void load_tfm_file();
static void scale_font();
static void find_font();
#if 0
static void choose_ps_prolog();
#endif
static void read_f_directory();
static int compare();
static void choose_mags();
static int order_mag();
static void emit_encodings();
#endif

 
/* int nprolog_fonts = 2; */
#define MAGS_MAX 50

static struct postscript_f_list *postscript_fonts = NULL;
 
static struct f_info {
	int no;
	enum { BLANK, BITMAP, POSTSCRIPT, BITMAP_PROLOG } type;
	int mag;
	int same_font; /* index of next font that is the same */
	long doc_chars; /* sum over each page of the number of distinct 
			  characters from the font occurring on that page */
	integer at_size;
	integer checksum;
	long usage_index; /* the higher this is, the better it is to download 
		it in the prologue rather than on each page */
	int npages; /* how many pages is it used on */
	char *texname;
	char *psname; /* the postscript name */
	char *encoding; /* name of the encoding vector */
	struct postscript_f_list *ps;
	char used[MAXCHARS]; /* which characters are used in the document */
	integer width[MAXCHARS];
	integer rounded_width[MAXCHARS];
	char *filename;
} *font[MAXFONTS];
 
 

static int target_mag;

/* returns -1, 0, or 1 according as *p1 is better, equally good,
   or worse as a choice of magnification when the desired
   magnification is target_mag
*/

static int order_mag(p1, p2)
char *p1, *p2;
{
	int n1 = *(int *)p1;
	int n2 = *(int *)p2;
	int d1 = abs(target_mag - n1);
	int d2 = abs(target_mag - n2);
	if (d2 < d1 && d2 <= 2)
		return 1;
	if (d1 < d2 && d1 <= 2)
		return -1;
	if (d1 == d2)
		return 0;
	if (n1 > n2)
		return -1;
	return 1;
}



static void choose_mags()
{
	int i;
	int nmags;
	char *p;
	static int mag[MAGS_MAX] = {TEXMAGS,0};
	for (i = 0; i < MAGS_MAX && mag[i] != 0; i++)
		;
	nmags = i;
	if ((p = getenv("TEXMAGS")) != NULL) {
		static char sep_string[] = { AREA_LIST_SEP, ',', ':', ' ', '\t', '\0' }; /* be tolerant */
		for (i = 0,p = strtok(p, sep_string); p != NULL && i < MAGS_MAX; p = strtok((char *)NULL, sep_string))
			if ((mag[i] = atoi(p)) > 0)
				++i;
		nmags = i;
	}
	for (i = 0; i < MAXFONTS; i++)
		if (font[i] != NULL && font[i]->type != POSTSCRIPT) {
			int j;
			target_mag = font[i]->mag;
			qsort((char *)mag, nmags, sizeof(int), order_mag);
			for (j = 0; j < nmags; j++) {
				char *p = find_pk_file(font[i]->texname, mag[j]);
				if (p != NULL) {
					char buf[FILENAME_MAX + 20];
					if (abs(mag[j] - font[i]->mag) > 2)
						message(WARNING, 
								"no exact font for %s at %d dpi",
								font[i]->texname, font[i]->mag);
					font[i]->mag = mag[j];
					font[i]->type = BITMAP;
					sprintf(buf,"%s-%d", font[i]->texname, font[i]->mag);
					font[i]->psname = strsave(buf);
					font[i]->filename = strsave(p);
					break;
				}
			}
		}
}

 
static void read_f_directory()
{
	char buf[512];
	FILE *fp;
	int i, j;
	if ((fp = xfopen("dvitops.fnt", FALSE, texinputs, (char *)NULL)) == NULL)
		message(FATAL_ERROR, "can't find dvitops.fnt");
	while (fgets(buf, 512, fp) != NULL) {
		char *ptr, *name, *psname;
		char *encoding = NULL;
		if ((ptr = strchr(buf, '%')) != NULL)
			*ptr = '\0';
		ptr = buf;
		while (isspace(*ptr))
			ptr++;
		if (*ptr == '\0')
			continue;
		if ((name = strtok(ptr, " \t\n\r")) == NULL)
			continue;
		if ((psname = strtok((char *)NULL, " \t\n\r")) == NULL)
			psname = name;
		else 
			encoding = strtok((char *)NULL, "\t\n\r");
		for (i = 0; i < MAXFONTS; i++)
			if (font[i] != NULL && strcmp(font[i]->texname, name) == 0) {
				font[i]->psname = strsave(psname);
				font[i]->ps = add_postscript_font(psname);
				font[i]->encoding = strsave(encoding);
				font[i]->type = POSTSCRIPT;
			}
	}
	choose_mags();
	for (i = 0; i < MAXFONTS; i++)
		if (font[i] != NULL && font[i]->type == BLANK)
			message(WARNING, 
				"no font directory entry for %s: using blank space instead\n",
					font[i]->texname);
	fclose(fp);
	for (i = 0; i < MAXFONTS; i++)
		if (font[i] != NULL && font[i]->type == POSTSCRIPT)
			for (j = i + 1; j < MAXFONTS; j++)
				if (font[j] != NULL && font[j]->type == POSTSCRIPT
					&& strcmp(font[i]->psname, font[j]->psname) == 0) {
					font[i]->same_font = j;
					break;
				}
}
 
 
static int compare(p1, p2)
char *p1;
char *p2;
{
	return (int)((*(struct f_info **)p2)->usage_index 
			- (*(struct f_info **)p1)->usage_index);
}
 
void f_prologue(fp)
FILE *fp;
{
	struct f_info *p[MAXFONTS];
	int i, j;
	integer n;
	struct postscript_f_list *q = postscript_fonts;
#if 0
	choose_ps_prolog();
#endif
	emit_encodings(fp);
	for (q = postscript_fonts; q != NULL; q = q->next)
		if (q->file != NULL && q->in_prolog) {
			emit_ps_font(q->s, fp);
			find_font(q->s, fp);
		}
	/* determine the most used bitmapped fonts */
	for (i = j = 0; i < MAXFONTS; i++) {
		if (font[i] != NULL && font[i]->type == BITMAP)
			p[j++] = font[i];
		if (font[i] != NULL)
			font[i]->no = (unsigned char)i;
	}
	for (i = 0; i < j; i++) {
		char *ptr = p[i]->used;
		int k;
		int nused = 0;
		for (k = 0; k < MAXCHARS; k++)
			if (ptr[k])
				++nused;
		p[i]->usage_index = p[i]->doc_chars - nused;
	}
 
	qsort((char *)p, j, sizeof(struct f_info *), compare);
	n = 0;
	/* download and scale the most used bitmapped fonts */
	for (i = 0; i < j && p[i]->usage_index > 0 
				&& n + MAXFONTSIZE < f_memory; i++) {
		int k = p[i]->no;
		if (p[i]->type != BITMAP)
			continue;
		if (!load_pk_font(k, p[i]->filename,p[i]->used,p[i]->at_size,
				p[i]->checksum, p[i]->width, p[i]->rounded_width)) {
			p[i]->type = BLANK;
			continue;
		}
		n += emit_pk_font(k, p[i]->psname, p[i]->used, fp);
		free_pk_font(k);
		p[i]->type = BITMAP_PROLOG;
		find_font(p[i]->psname, fp);
		scale_font(k, fp);
	}
	/* Load the pk files for the bitmapped fonts 
		not downloaded in the prologue;
		load the tfm files for the bitmapped fonts for which no pk file
		could be found, and for the PostScript files */
	for (i = 0; i < MAXFONTS; i++) {
		if (font[i] == NULL)
			continue;
		if (font[i]->type == BITMAP) {
			if (!load_pk_font(i, font[i]->filename,
				font[i]->used, font[i]->at_size,
				font[i]->checksum, font[i]->width, font[i]->rounded_width))
				font[i]->type = BLANK;
		}
		if (font[i]->type == BLANK || font[i]->type == POSTSCRIPT) {
			load_tfm_file(i);
			memcpy((char *)font[i]->rounded_width, (char *)font[i]->width, 
						MAXCHARS*sizeof(integer));
		}
	}
	for (i = 0; i < MAXFONTS; i++)
		if (font[i] != NULL && font[i]->type == POSTSCRIPT
						&& font[i]->ps->in_prolog == TRUE)
			scale_font(i, fp);
}
 
static void scale_font(i, fp)
int i;
FILE *fp;
{
	fprintf(fp, "/F%d _%s", i, font[i]->psname);
	if (font[i]->encoding != NULL)
		fprintf(fp, "_%s", font[i]->encoding);
	putc(' ', fp);
	put_dim((long)(font[i]->at_size), fp);
	fputs(" SF\n", fp);
}
 
 
static void find_font(name, fp)
char *name;
FILE *fp;
{
	int i;
	fprintf(fp, "/_%s /%s FF\n", name, name);
	/* reencode all fonts based on it */
	for (i = 0; i < MAXFONTS; i++)
		if (font[i] != 0 && font[i]->encoding != 0
			&& strcmp(font[i]->psname, name) == 0) {
			int j;
			for (j = 0; j < i; j++)
				if (font[j] != 0 && font[j]->encoding != 0
					&& strcmp(font[i]->encoding, font[j]->encoding) == 0
					&& strcmp(font[j]->psname, name) == 0)
					break;
			if (j == i)
				/* newdictname newfontname encodingvector basefontdict */
				fprintf(fp, "/_%s_%s /%s_%s %s_encoding _%s RE\n", 
						name, font[i]->encoding, 
						name, font[i]->encoding, 
						font[i]->encoding,
						name);
		}
}
 
 
/* we might use the area argument in the future */
void f_def(fontno, name, area, checksum, at_size, design_size, magnification)
integer fontno;
char *name, *area;
integer checksum, at_size, design_size;
int magnification;
{
	int i;
	if (fontno < 0 || fontno > MAXFONTS)
		message(FATAL_ERROR, "too many fonts");
	i = (int)fontno;
	if (font[i] != NULL)
		cant_happen();
	if ((font[i] = (struct f_info *)
						calloc(1, sizeof(struct f_info))) == NULL)
		out_of_memory();
	font[i]->texname = strsave(name);
	font[i]->checksum = checksum;
	font[i]->at_size = at_size;
	font[i]->mag = (int)(.5 +
						 ((double)magnification*(double)at_size*(double)dpi)
						 /(design_size*1000.0));
	font[i]->same_font = EOF;
	font[i]->npages = 0;
	if ((page_used[i] = (char *)calloc(MAXCHARS, 1)) == NULL)
		out_of_memory();
}
 
void store_page_usage()
{
	int i, j;
	for (i = 0; i < MAXFONTS; i++) {
		char *ptr = page_used[i], *q = font[i]->used;
		int used = FALSE;
		if (ptr != NULL)
			for (j = 0; j < MAXCHARS; j++) 
				if (ptr[j]) {
					q[j] = TRUE;
					font[i]->doc_chars++;
					ptr[j] = FALSE;
					used = TRUE;
				}
		if (used)
			(font[i]->npages)++;
	}
}
 
 
 
char *strsave(s)
char *s;
{
	char *p;
	if (s == NULL)
		return NULL;
	if ((p = (char *)malloc(strlen(s) + 1)) == NULL)
		out_of_memory();
	strcpy(p, s);
	return p;
}
 
/* this code is borrowed from sections 571, 572 of TeX the Program */
 
integer scale(x, z)
integer x, z;
{
	integer sw;
	integer a, b, c, d;
	integer alpha, beta;
	alpha = 16*z; beta = 16;
	while (z >= 040000000L) {
		z /= 2; beta /= 2;
	}
	d = x & 255;
	c = (x >> 8) & 255;
	b = (x >> 16) & 255;
	a = (x >> 24) & 255;
	sw = (((((d * z) / 0400) + (c * z)) / 0400) + (b * z)) / beta;
	if ( a == 255)
		sw -= alpha;
	else if (a != 0)
		cant_happen();
	return sw;
}
 
static void load_tfm_file(f)
int f;
{
	int i;
	int lh, bc, ec, nw;
	integer ds, cs;
	FILE *tfmfp;
	unsigned char width_index[MAXCHARS];
	integer width_table[MAXCHARS];
	if ((tfmfp = xfopen(font[f]->texname, TRUE, texfonts, ".tfm")) == NULL)
		message(FATAL_ERROR, "couldn't find tfm file for %s",
				font[f]->texname);
	(void)sread2(tfmfp);
	lh = sread2(tfmfp);
	bc = sread2(tfmfp);
	ec = sread2(tfmfp);
	nw = sread2(tfmfp);
	fseek(tfmfp, 14L, 1);
	cs = sread4(tfmfp);
	if (cs != font[f]->checksum)
		message(WARNING, "font %s: checksum in tfm file doesn't match",
									font[f]->texname);
	ds = sread4(tfmfp); /* not used at the moment */
	lh -= 2;
	fseek(tfmfp, (long)lh*4, 1);
	for (i = 0; i < ec + 1 - bc; ++i) {
		width_index[i] = uread1(tfmfp);
		fseek(tfmfp, 3L, 1);
	}
	for (i = 0; i < nw; ++i)
		width_table[i] = scale(sread4(tfmfp), font[f]->at_size);
	fclose(tfmfp);
/* Microsoft C 5.0's optimizer messes up this loop */
	for (i = bc; i <= ec; i++)
		font[f]->width[i] = width_table[width_index[i - bc]];
}
 
struct postscript_f_list *add_postscript_font(font)
char *font;
{
	struct postscript_f_list *p;
	for (p = postscript_fonts;p != NULL; p = p->next)
		if (strcmp(font, p->s) == 0)
			return p;
	if ((p = (struct postscript_f_list *)malloc(
					sizeof(struct postscript_f_list)+strlen(font))
						) == NULL)
		out_of_memory();
	strcpy(p->s, font);
	p->next = postscript_fonts;
	p->file = NULL;
	p->in_prolog = FALSE;
	p->nuses = 0;
	postscript_fonts = p;
	return p;
}
 
void f_comment(psfp)
FILE *psfp;
{
	int i;
	int cols;
	struct postscript_f_list *p;
	read_f_directory();
	cols = fprintf(psfp, "%%%%DocumentFonts:");
	for (i = 0; i < MAXFONTS; i++)
		if (font[i] != NULL && font[i]->doc_chars > 0 
			&& (font[i]->type == BITMAP || font[i]->type == BITMAP_PROLOG)) {
			if (cols + strlen(font[i]->psname) > 70)
				cols = fprintf(psfp, "\n%%%%+ %s", font[i]->psname);
			else
				cols += fprintf(psfp, " %s", font[i]->psname);
		}
	for (p = postscript_fonts; p != NULL; p = p->next) {
		if (cols + strlen(p->s) > 70)
			cols = fprintf(psfp, "\n%%%%+ %s",p->s) - 1;
		else
			cols += fprintf(psfp, " %s", p->s);
	}
	putc('\n', psfp);
	cols = fprintf(psfp, "%%%%DocumentSuppliedFonts:");
	for (i = 0; i < MAXFONTS; i++)
		if (font[i] != NULL && font[i]->doc_chars > 0
			&& (font[i]->type == BITMAP || font[i]->type == BITMAP_PROLOG)) {
			if (cols + strlen(font[i]->psname) > 70)
				cols = fprintf(psfp, "\n%%%%+ %s", font[i]->psname);
			else
				cols += fprintf(psfp, " %s", font[i]->psname);
		}
	putc('\n', psfp);
	cols = fprintf(psfp, "%%%%DocumentNeededFonts:");
	for (p = postscript_fonts; p != NULL; p = p->next) {
		if (cols + strlen(p->s) > 70)
			cols = fprintf(psfp, "\n%%%%+ %s",p->s) - 1;
		else
			cols += fprintf(psfp, " %s", p->s);
	}
	putc('\n', psfp);
}
 
void f_emit(i, fp)
int i;
FILE *fp;
{
	if (font[i]->type == BITMAP) {
		emit_pk_font(i, font[i]->psname, page_used[i], fp);
		find_font(font[i]->psname, fp);
	}
	else if (font[i]->type == POSTSCRIPT && !font[i]->ps->in_prolog) {
		emit_ps_font(font[i]->psname, fp);
		find_font(font[i]->psname, fp);
	}
}
 
void f_set(i, fp)
int i;
FILE *fp;
{
	if (font[i]->type == BITMAP || 
			(font[i]->type == POSTSCRIPT && !font[i]->ps->in_prolog))
		scale_font(i, fp);
	memset(page_used[i], '\0', MAXCHARS);
	fprintf(fp, "F%d F\n", i);
}
 
void emit_ps_font(font, psfp)
char *font;
FILE *psfp;
{
	fprintf(psfp, "%%%%IncludeFont: %s\n", font);
}
 
 
int f_is_blank(f)
int f;
{
	return (font[f]->type == BLANK);
}
 
integer f_space(f)
int f;
{
	return (font[f]->at_size/6);
}
 
 
integer f_width(f, c)
int f;
unsigned char c;
{
	return font[f]->width[c];
}
 
integer f_rounded_width(f, c)
int f;
unsigned char c;
{
	return font[f]->rounded_width[c];
}
 
int same_font(f)
int f;
{
	return font[f] == NULL ? EOF : font[f]->same_font;
}
 
#if 0 
static void choose_ps_prolog()
{
	int i;
	struct postscript_f_list *p, *s;
	for (i = 0; i < MAXFONTS; ++i)
		if (font[i] != NULL && font[i]->type == POSTSCRIPT)
			font[i]->ps->nuses += font[i]->npages;
	/* do a list insertion sort */
	s = NULL;
	p = postscript_fonts;
	while (p != NULL) {
		struct postscript_f_list *ss = s, *ssp = NULL, *temp;
		while (ss != NULL && p->nuses < ss->nuses) {
			ssp = ss;
			ss = ss->next;
		}
		temp = p->next;
		if (ssp == NULL) {
			p->next = s;
			s = p;
		}
		else {
			ssp->next = p;
			p->next = ss;
		}
		p = temp;
	}
	postscript_fonts = s;
	for (p = postscript_fonts, i = 0; p != NULL && i < nprolog_fonts;
		 p = p->next, i++)
		p->in_prolog = TRUE;
}
#endif

static void emit_encodings(psfp)
FILE *psfp;
{
	int i;
	for (i = 0; i < MAXFONTS; i++)
		if (font[i] != 0 && font[i]->encoding != NULL) {
			int j;
			for (j = 0; j < i; j++)
				if (font[j] != 0 && font[j]->encoding != 0
					&& strcmp(font[j]->encoding, font[i]->encoding) == 0)
					break;
			if (j == i) {
				char name[256];
				FILE *fp = xfopen(font[i]->encoding, FALSE, texfonts, ".enc");
				if (fp == NULL) {
					message(FATAL_ERROR, "%s: no encoding file", 
							font[i]->encoding);
				}
				fprintf(psfp, "/%s_encoding [\n", 
						font[i]->encoding);
				for (j = 0; j < 256 && fscanf(fp, "%s", name) == 1; j++)
					fprintf(psfp, "/%s\n", name);
				for (; j < 256; j++)
					fputs("/.notdef\n", psfp);
				fputs("] def\n", psfp);
			}
		}
}

/*
Local Variables:
tab-width: 4
c-indent-level: 4
c-continued-statement-offset: 4
c-brace-offset: -4
c-argdecl-indent: 0
c-label-offset: -4
End:
*/