/*
 * virtual font
 */

#include	"defs.h"
#include	"dconv.h"
#include	"set.h"
#include	"global.h"
#include	"virfont.h"
#ifdef KPATHSEA
#include	<kpathsea/tex-file.h>
#endif

int vftype_access();
void init_vf_fontinfo();
void init_jvf_fontinfo();
struct fontop vfop = {
    "vf",
    pathtype_init,
    vftype_access,
    init_vf_fontinfo,
};
struct fontop jvfop = {
    "jvf",
    pathtype_init,
    vftype_access,
    init_jvf_fontinfo,
};

vftype_access(proto, fe, acca)
char *proto;
struct font_entry *fe;
struct accarg *acca;
{
    char *filename;
    BOOLEAN ok;

#ifdef KPATHSEA
    if (ok = ((filename = kpsearch_make(proto, fe->n, kpse_vf_format,
					acca, fe->name)) != NULL))
	strcpy(fe->name, filename);
#else
    pave(fe->name, proto, acca);
    ok = access(fe->name, R_OK) == 0;
#endif
#ifdef DEBUG
    if (Debuguser)
	(void)fprintf(stderr, "trying to access %s\n", fe->name);
#endif
    return ok;
}

void
init_vf_fontinfo(fe)
struct font_entry *fe;
{
    int vf_markchar();
    void read_vf_fontinfo();

    read_vf_dviinfo(fe);
    fe->fnt_type = FT_VF;
    fe->fnt_markchar = vf_markchar;
    fe->fnt_readfontinfo = read_vf_fontinfo;
}

void
init_jvf_fontinfo(fe)
struct font_entry *fe;
{
    int jvf_markchar();
    void read_jvf_fontinfo();

    read_jvf_dviinfo(fe);
    fe->fnt_type = FT_VF;
    fe->fnt_markchar = jvf_markchar;
    fe->fnt_readfontinfo = read_jvf_fontinfo;
}

#define	CODE_NORMAL	0
#define	CODE_JIS	1

#define	NJISCHARS	(94*94+1)
#define	JISDEFAULT	(94*94)

read_vf_dviinfo(fe)
struct font_entry *fe;
{
    read_vf_di(fe, CODE_NORMAL);
}

read_jvf_dviinfo(fe)
struct font_entry *fe;
{
    read_vf_di(fe, CODE_JIS);
}

read_vf_di(fe, coding)
struct font_entry *fe;
int coding;
{
    FILE *fntfp;
    int cmd, t;
    int ds;
    int	k, c, s, d, a, l;
    byte n[STRSIZE];
    struct font_index *fip;
    struct vfchar_entry *ce;
    int pl, cc, idx, tfm;
    int misschar = 0;
    struct virfntinfo *alloc_virfinfo();

    openfontfile(fe);
    fntfp = fe->openfile;
    if (getuint(fntfp,1) != VF_PRE || getuint(fntfp,1) != VF_ID)
	Fatal("bad vf file %s\n", fe->name);
    (void)fseek(fntfp, (long)getuint(fntfp, 1), SEEK_CUR);	/* comment */
    if ((t = getuint(fntfp, 4)) && fe->c && t != fe->c)
	Warning("font = \"%s\",\n-->font checksum = %d,\n-->dvi checksum = %d",
		fe->name, fe->c, t);
    ds = getuint(fntfp, 4);	/* design size */
    virfinfo(fe) = alloc_virfinfo((coding == CODE_NORMAL)
				  ? MAXVFCHAR+1 : NJISCHARS);

    for (; (cmd = getuint(fntfp,1)) >= VF_FNT_DEF1 && cmd <= VF_FNT_DEF4; ) {
	k = getuint(fntfp, cmd-VF_FNT_DEF1+1);
	c = getuint(fntfp, 4);	/* checksum */
	s = scale(fe->s, getuint(fntfp, 4));	/* scaled size */
	d = getuint(fntfp, 4) >> 4;		/* design size (pt -> sp) */
	a = getuint(fntfp, 1);	/* area length for font name */
	l = getuint(fntfp, 1);	/* device length */
	getbytes(fntfp, n, a+l);
	n[a+l] = '\0';
	readfontdef(k, c, s, d, a, l, (char *)n, &(virfinfo(fe)->vf_fontidx));
    }			

    if (virfinfo(fe)->vf_fontidx == NULL) {
	virfinfo(fe)->vf_default_fent = NULL;
    } else {
	for (fip = virfinfo(fe)->vf_fontidx; fip->next != NULL;
	     fip = fip->next)
	    ;
	virfinfo(fe)->vf_default_fent = fip->fent;
    }

    for (;; cmd = getuint(fntfp, 1)) {
	if (cmd <= VF_LONG_CHAR) {
	    if (cmd == VF_LONG_CHAR) {
		pl = getuint(fntfp, 4);
		cc = getuint(fntfp, 4);
		tfm = getuint(fntfp, 4);
	    } else {
		pl = cmd;
		cc = getuint(fntfp, 1);
		tfm = getuint(fntfp, 3);
	    }
	    if (coding == CODE_NORMAL) {
		if (cc > MAXVFCHAR) {
		    misschar++;
#ifdef DEBUG
		    if (Debug)
			(void)fprintf(stderr,
				      "cc 0x%x in vf file %s (skipped)\n",
				      cc, fe->name);
#endif
		    (void)fseek(fntfp, (long)pl, SEEK_CUR);
		    continue;
		} else
		    ce = &virfinfo(fe)->ch[cc];
	    } else {
		if (cc == 0)
		    ce = &virfinfo(fe)->ch[JISDEFAULT];
		else if ((idx = jis_to_idx94(cc)) < 0) {
		    misschar++;
#ifdef DEBUG
		    if (Debug)
			(void)fprintf(stderr,
				      "cc 0x%x in vf file %s (skipped)\n",
				      cc, fe->name);
#endif
		    (void)fseek(fntfp, (long)pl, SEEK_CUR);
		    continue;
		} else
		    ce = &virfinfo(fe)->ch[idx];
	    }
	    ce->tfmw = scale(tfm, fe->s);
	    if (pl == 0)
		ce->vfdstat = VFD_NULL;
	    else {
		ce->vfdstat = VFD_NOTLD;
		ce->vfd.dvi.dvilen = pl;
		ce->vfd.dvi.where.fileoffset = ftell(fntfp);
		(void)fseek(fntfp, (long)pl, SEEK_CUR);
	    }
	} else if (cmd == VF_POST) {
	    break;
	} else
	    Fatal("illegal vf command %d in %s\n", cmd, fe->name);
    }
    if (misschar > 0) {
	Warning("char(s) in vf file %s skipped", fe->name);
	/* There is no ground for the number NJISCHARS/2. */
	if (coding == CODE_NORMAL && misschar >= NJISCHARS/2)
	    Warning("\t\"font vf\" may be the mistake of \"font jvf\"");
	else if (coding == CODE_JIS)
	    Warning("\t\"font jvf\" may be the mistake of \"font vf\"");
    }
}

struct virfntinfo *
alloc_virfinfo(nchars)
int nchars;
{
    struct virfntinfo *vfi;
    struct vfchar_entry *ce;
    int i;

    vfi = (struct virfntinfo *)
	  alloc_check(malloc((unsigned)sizeof(struct virfntinfo)+
			     (nchars-1)*sizeof(struct vfchar_entry)),
		      "virfont info");
    vfi->vf_fontidx = NULL;
    for (i = 0; i < nchars; i++) {
	ce = &(vfi->ch[i]);
	ce->vfdstat = VFD_UNDEF;
	ce->tfmw = 0;
    }
    return vfi;
}

vf_markchar(fe, c)
struct font_entry *fe;
int c;
{
    if (c > MAXVFCHAR) {
#ifdef DEBUG
	if (Debuguser)
	    (void)fprintf(stderr, "char 0x%x (font %s) too big\n", c, fe->n);
#endif
    } else
	vf_mc(fe, &virfinfo(fe)->ch[c], c);
}

jvf_markchar(fe, c)
struct font_entry *fe;
int c;
{
    int idx;

    if ((idx = jis_to_idx94(c)) < 0) {
#ifdef DEBUG
	if (Debuguser)
	    (void)fprintf(stderr, "char 0x%x (font %s) invalid\n", c, fe->n);
#endif
    } else
	vf_mc(fe, &virfinfo(fe)->ch[idx], c);
}

byte *
getvfdvi(fe, ce)
struct font_entry *fe;
struct vfchar_entry *ce;
{
    int dvilen;
    byte *dvip, *dp;
    FILE *fntfp;

    openfontfile(fe);
    fntfp = fe->openfile;
    (void)fseek(fntfp, (long)ce->vfd.dvi.where.fileoffset, SEEK_SET);
    dvilen = ce->vfd.dvi.dvilen;
    if ((dvip = (byte *)malloc((unsigned)dvilen)) == NULL)
	Fatal("Unable to allocate memory for vf char\n");
    (void)fread((char *)dvip, 1, dvilen, fntfp);
    return dvip;
}

vf_mc(fe, ce, c)
struct font_entry *fe;
struct vfchar_entry *ce;
int c;
{
    int cmd, dvilen, k;
    struct vfchar_entry *defce;
    byte *dvip, *dp;
    struct font_index *fi;
    struct dconv save_curdconv, *save_curdconvp;
    struct font_entry *save_curfontent;
    int save_dir;

    if (ce->vfdstat >= VFD_NULL)
	return;
    if (ce->vfdstat == VFD_UNDEF) {
	if ((defce = &virfinfo(fe)->ch[JISDEFAULT])->vfdstat == VFD_UNDEF)
	    Fatal("char %d in vf file %s is not defined", c, fe->n);
	if (defce->vfdstat == VFD_NOTLD) {
	    defce->vfd.dvi.where.dviptr = getvfdvi(fe, defce);
	    defce->vfdstat = VFD_NULL;
	}
	dvilen = ce->vfd.dvi.dvilen = defce->vfd.dvi.dvilen;
	if ((dvip = (byte *)malloc((unsigned)dvilen)) == NULL)
	    Fatal("Unable to allocate memory for vf char\n");
	/* Hack. depend on the fact that default char is of the form
	   ... SET2 0x2121. */
	memcpy(dvip, defce->vfd.dvi.where.dviptr, dvilen);
	*(dvip+dvilen-2) = c>>8;
	*(dvip+dvilen-1) = c&0xff;
	ce->tfmw = defce->tfmw;
    } else {
	dvip = getvfdvi(fe, ce);
	dvilen = ce->vfd.dvi.dvilen;
    }

    /* short cut for simple cases */
    ce->vfdstat = VFD_DVI;
    if (setc_com(cmd = *dvip, dvip, dvilen, ce)) {
	ce->vfdstat = VFD_SETFC;
	ce->vfd.set.f = virfinfo(fe)->vf_default_fent;
    } else if (FONT_00 <= cmd && cmd <= FNT4) {
	if (cmd <= FONT_63) {
	    --dvilen;
	    dp = dvip+1;
	} else {
	    dvilen -= cmd-FNT1+2;
	    dp = dvip+cmd-FNT1+2;
	}
	if (setc_com(*dp, dp, dvilen, ce)) {
	    ce->vfdstat = VFD_SETFC;
	    k = (cmd <= FONT_63) ? cmd-FONT_00
		: makeuint(dvip+1, cmd-FNT1+1);
	    for (fi = virfinfo(fe)->vf_fontidx;
		 fi != NULL && fi->k != k; fi = fi->next)
		;
	    ce->vfd.set.f = fi->fent;
	}
    }

    if (ce->vfdstat == VFD_SETFC) {
	save_curfontent = curfontent;
	setcurfont(ce->vfd.set.f);
	MarkChar(ce->vfd.set.c);
	setcurfont(save_curfontent);
	free((char *)dvip);
	return;
    }
    ce->vfd.dvi.where.dviptr = dvip;

    /* save */
    save_curdconvp = curdconvp;
    curdconvp->dc_bufbeg = dc_bufbeg;
    save_curdconv = *curdconvp;
    vfd_dconv_templ.dc_bufbeg = dvip;
    vfd_dconv_templ.dc_bufend = dvip+ce->vfd.dvi.dvilen;
    /*vfd_dconv_templ.dc_scale = fe->s;*/
    setcurdconv(&vfd_dconv_templ);
    save_curfontent = curfontent;
    setcurfont(virfinfo(fe)->vf_default_fent);
    save_dir = dir;

    scanfont(TRUE, &(virfinfo(fe)->vf_fontidx));

    /* restore */
    *save_curdconvp = save_curdconv;
    setcurdconv(save_curdconvp);
    setcurfont(save_curfontent);
    dir = save_dir;
}

setc_com(cmd, dvip, dvilen, ce)
byte cmd;
byte *dvip;
int dvilen;
struct vfchar_entry *ce;
{
    if (cmd <= SETC_127 && dvilen <= 1) {
	ce->vfd.set.c = cmd;
	return TRUE;
    } else if (SET1 <= cmd && cmd <= SET4 && dvilen == cmd-SET1+2) {
	ce->vfd.set.c = makeuint(dvip+1, dvilen-1);
	return TRUE;
    }
    return FALSE;
}

void
read_vf_fontinfo(fe)
struct font_entry *fe;
{
    DEV_FONT vf_fontdict();
    int vf_setchar(), vf_setstring();

    fe->rvf_setchar = virf_setchar;
    fe->rvf_setstring = virf_setstring;
    fe->dev_fontdict = vf_fontdict;
    fe->dev_setchar = vf_setchar;
    fe->dev_setstring = vf_setstring;
}

void
read_jvf_fontinfo(fe)
struct font_entry *fe;
{
    DEV_FONT vf_fontdict();
    int jvf_setchar(), jvf_setstring();

    fe->rvf_setchar = virf_setchar;
    fe->rvf_setstring = virf_setstring;
    fe->dev_fontdict = vf_fontdict;
    fe->dev_setchar = jvf_setchar;
    fe->dev_setstring = jvf_setstring;
}

/* ARGSUSED */
DEV_FONT
vf_fontdict(fe, c)
struct font_entry *fe;
int c;
{
    Fatal("%s implementation error: vf_fontdict", G_progname);
}

vf_setchar(fe, c)
struct font_entry *fe;
int c;
{
    if (c > MAXVFCHAR)
	return 0;
    else
	return vf_sc(fe, &virfinfo(fe)->ch[c], c);
}

jvf_setchar(fe, c)
struct font_entry *fe;
int c;
{
    int idx;

    if ((idx = jis_to_idx94(c)) < 0)
	return 0;
    else
	return vf_sc(fe, &virfinfo(fe)->ch[idx], c);
}

/* ARGSUSED */
vf_sc(fe, ce, c)
struct font_entry *fe;
struct vfchar_entry *ce;
int c;
{
    struct dconv save_curdconv, *save_curdconvp;
    struct font_entry *save_curfontent;
    int save_h, save_v, save_dir;
    BOOLEAN save_chmove;

    if (ce->vfdstat == VFD_SETFC) {
	if (ce->vfd.set.f->fnt_type == FT_TFM) {
	    dev_vftfm_char(ce->vfd.set.f, ce->vfd.set.c, ce->tfmw);
	} else if (ce->vfd.set.f->fnt_type == FT_JFM) {
	    dev_vfjfm_char(ce->vfd.set.f, ce->vfd.set.c, ce->tfmw);
	} else {
	    save_curfontent = curfontent;
	    setcurfont(ce->vfd.set.f);
	    save_chmove = chmove;
	    SetChar(ce->vfd.set.c, FALSE);
	    setcurfont(save_curfontent);
	    chmove = save_chmove;
	    if (chmove)
		dev_makemoveover(ce->tfmw);
	}
	return ce->tfmw;
    }

    if (ce->vfdstat == VFD_NULL) {
	if (chmove)
	    dev_makemoveover(ce->tfmw);
	return ce->tfmw;
    }

    /* save */
    save_curdconvp = curdconvp;
    curdconvp->dc_bufbeg = dc_bufbeg;
    save_curdconv = *curdconvp;
    vfd_dconv_templ.dc_bufbeg = ce->vfd.dvi.where.dviptr;
    vfd_dconv_templ.dc_bufend = ce->vfd.dvi.where.dviptr+ce->vfd.dvi.dvilen;
    vfd_dconv_templ.dc_scale = fe->s;
    setcurdconv(&vfd_dconv_templ);
    save_curfontent = curfontent;
    setcurfont(virfinfo(fe)->vf_default_fent);
    save_h = h;
    save_v = v;
    save_dir = dir;
    save_chmove = chmove;

    dev_push();
    dviconv(virfinfo(fe)->vf_fontidx);
    dev_pop();

    /* restore */
    *save_curdconvp = save_curdconv;
    setcurdconv(save_curdconvp);
    setcurfont(save_curfontent);
    h = save_h;
    v = save_v;
    setdir(save_dir, TRUE);
    chmove = save_chmove;
    if (chmove)
	dev_makemoveover(ce->tfmw);

    return ce->tfmw;
}

vf_setstring(fe, s, len)
struct font_entry *fe;
unsigned char *s;
int len;
{
    unsigned char *sp;

    /* should be optimized */
    for (sp = s; sp < s+len; sp++)
	*move += vf_setchar(fe, *sp);
}

jvf_setstring(fe, s, len)
struct font_entry *fe;
unsigned char *s;
int len;
{
    unsigned char *sp;

    /* should be optimized */
    for (sp = s; sp < s+len; sp++)
	*move += jvf_setchar(fe, *sp);
}
