/*
 * built-in font
 */

#include	"defs.h"
#include	"global.h"
#include	"bifont.h"
#include	"jsub.h"
#ifdef KPATHSEA
#include	<kpathsea/pathsearch.h>
#include	<kpathsea/tex-file.h>
#endif

/*
    There are three kinds of tfm files, e.g.
	Times-Roman.tfm, Courier.tfm, ...
	XXjhira10.tfm, XXja10.tfm, ... (XX: japanese font)
	XX.tfm, ... (jfm format, XX: japanese font)
    The first one: (TYPE tfm)
	easy.
    The second one: (TYPE jstfm)
	dev_font: assgined when XX first appears
	dev_ku, dev_ten: calculated from j?? and charcode
    The third one: (TYPE jfm)
	easy, but tfmw information can be stored efficiently
	by using jfm structure
*/

/* tfm_access, jstfm_access, jfm_access may be device dependent,
   because not all devices allow arbitrary magnification. */

int tfm_access();
int jstfm_access();
int jfm_access();
void init_tfm_fontinfo();
void init_jstfm_fontinfo();
void init_jfm_fontinfo();

struct fontop tfmop = {
    "tfm",
    pathtype_init,
    tfm_access,
    init_tfm_fontinfo,
};

#ifdef UNDEF
struct fontop jstfmop = {
    "jstfm",
    pathtype_init,
    jstfm_access,
    init_jstfm_fontinfo,
};
#endif

struct fontop jfmop = {
    "jfm",
    pathtype_init,
    tfm_access,
    init_jfm_fontinfo,
};

struct biaccessinfo biainfo;

/* set by readtfm, readjfm */
static byte width[4*256];
static int id, nt, lh, bc, ec, nw;
#define	JSTFMHEAD	4*2
static byte header[JSTFMHEAD];

/* TYPE: tfm
 */
tfm_access(proto, fe, acca)
char *proto;
struct font_entry *fe;
struct accarg *acca;
{
    char *filename;
    BOOLEAN ok;

#ifdef KPATHSEA
    filename = kpsearch_make(proto, fe->n, kpse_tfm_format, NULL, acca,
			     fe->name);
    if (ok = (filename != 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
    if (ok) {
	fe->finfo = (struct finfo *)&biainfo;
	return TRUE;
    }
    return FALSE;
}

void
init_tfm_fontinfo(fe)
struct font_entry *fe;
{
    int	null_markchar();
    void read_tfm_fontinfo(), read_jfm_fontinfo();

    bfinfo(fe) = biaccinfo(fe)->bf;
    openfontfile(fe);
    if (checkjfm(fe->openfile)) {
	fe->fnt_type = FT_JFM;
	/*fe->base_fe->fnt_use = FU_USED;*/
	fe->fnt_markchar = null_markchar;
	fe->fnt_readfontinfo = read_jfm_fontinfo;
    } else {
	fe->fnt_type = FT_TFM;
	/*fe->base_fe->fnt_use = FU_USED;*/
	fe->fnt_markchar = null_markchar;
	fe->fnt_readfontinfo = read_tfm_fontinfo;
    }
}

void
read_tfm_finfo(fe)
struct font_entry *fe;
{
    FILE *fntfp;
    struct tfmchar_entry *ce;
    int i;
    SCALED s;

    openfontfile(fe);
    readtfm(fntfp = fe->openfile);
    tfmfinfo(fe)->lastfntchar = ec;
    s = (SCALED)fe->s;
    for (i = bc, ce = (tfmfinfo(fe)->ch)+bc; i <= ec; i++, ce++) {
	/* ce->dev_font = fe->k;	/* Not used */
	ce->dev_char = i;
	ce->tfmw = scale(makeuint(width+4*getuint(fntfp,1),4), s);
	(void)fseek(fntfp, 3L, SEEK_CUR);
    }
}

void
read_tfm_fontinfo(fe)
struct font_entry *fe;
{
    struct tfmfntinfo *tfmfi;

    tfmfi = NEW(struct tfmfntinfo, "tfmfont info");
    tfmfi->tfm_bf = bfinfo(fe);
    tfmfinfo(fe) = tfmfi;
    read_tfm_finfo(fe);
    dev_tfm_initfe(fe);
    dev_tfm_initfontdict(fe);
}

#ifdef UNDEF
/* ARGSUSED */
DEV_FONT
tfm_fontdict(fe, c)
struct font_entry *fe;
int c;
{
    return fe->k;
}
#endif

#ifdef UNDEF
/* TYPE: jstfm
 */
struct jsubshare *jsubshares = NULL;
struct jsubshare **nextjss = &jsubshares;

struct jsubshare *
getjsubshare(fe)
struct font_entry *fe;
{
    int s;
    struct jsubshare *jss;
    struct bifont *jbf;

    s = fe->s;
    jbf = biaccinfo(fe)->bf;
    for (jss = jsubshares; jss != NULL; jss = jss->jss_next)
	if (jss->jss_s == s && jss->jss_bf == jbf) {
	    return jss;
	}
    jss = NEW(struct jsubshare, "jsubshare");
    jss->jss_s = s;
    jss->jss_bf = jbf;
    jss->jss_stat = JSS_CLOSED;
    jss->jss_parent = strsave(dev_jstfm_parent(jbf, fe->n));
    jss->jss_next = NULL;
    *nextjss = jss;
    nextjss = &(jss->jss_next);
    return jss;
}

jstfm_access(proto, fe, acca)
char *proto;
struct font_entry *fe;
struct accarg *acca;
{
    int dev_is_jstfm();

    return gentfm_access(proto, fe, acca, dev_is_jstfm, "jstfm");
}

void
init_jstfm_fontinfo(fe)
struct font_entry *fe;
{
    struct jstfmfntinfo *jsfi;
    int	null_markchar();
    void read_jstfm_fontinfo();
    void init_jswl_fontinfo(), init_jsft_fontinfo(), init_jsvfl_fontinfo();

    jsfi = NEW(struct jstfmfntinfo, "jstfmfont info");
    jsfi->jsubfont = biaccinfo(fe)->jsubf;
    jsfi->js_bf = biaccinfo(fe)->bf;
    jsfi->js_share = getjsubshare(fe);
    jstfmfinfo(fe) = jsfi;
    switch (dev_jstfm_kind(fe)) {
    case MF_KIND_PRINTER:
	fe->fnt_markchar = null_markchar;
	fe->fnt_readfontinfo = read_jstfm_fontinfo;
	break;
    case MF_KIND_TYPE1:
	init_jswl_fontinfo(fe);
	break;
    case MF_KIND_FT:
    case MF_KIND_FTO:
	init_jsft_fontinfo(fe);
	break;
    case MF_KIND_VFLIB:
    case MF_KIND_VFLIBO:
	init_jsvfl_fontinfo(fe);
	break;
    default:
	Fatal("%s implementation error: init_jstfm_fontinfo", G_progname);
    }
}

void
read_jstfm_finfo(fe)
struct font_entry *fe;
{
    FILE *fntfp;
    struct jstfmchar_entry *ce;
    int i;
    SCALED s;
    int subfont;

    openfontfile(fe);
    readtfm(fntfp = fe->openfile);
    if (lh <= 2 || makeuint(header, 4) != JSTFM_MAGIC)
	Fatal("%s is not japanese subfont", fe->name);
    subfont = header[JSTFMHEAD-1];
    if (subfont != jstfmfinfo(fe)->jsubfont)
	Warning("%s has subfont number %d", fe->name, subfont);
    jstfmfinfo(fe)->lastfntchar = ec;
    s = (SCALED)fe->s;
    jstfmfinfo(fe)->em = scale(makeuint(param+4*5,4), s);
    for (i = bc, ce = (jstfmfinfo(fe)->ch)+bc; i <= ec; i++, ce++) {
	jsub_to_jis(subfont, i, &(ce->dev_ku), &(ce->dev_ten));
	ce->tfmw = scale(makeuint(width+4*getuint(fntfp,1),4), s);
	(void)fseek(fntfp, 3L, SEEK_CUR);
    }
    /* hack for space */
    if (subfont == 1 && bc == 2) {
	ce = (jstfmfinfo(fe)->ch)+bc-1;
	jsub_to_jis(subfont, bc-1, &(ce->dev_ku), &(ce->dev_ten));
	ce->tfmw = (ce+1)->tfmw;
    }
}

void
read_jstfm_fontinfo(fe)
struct font_entry *fe;
{
    struct jsubshare *jss;

    read_jstfm_finfo(fe);
    dev_jstfm_initfe(fe);
    jss = jstfmfinfo(fe)->js_share;
    if (jss->jss_stat < JSS_READ) {
	dev_jstfm_initfontdict(fe);
	jss->jss_stat = JSS_READ;
    }
    jstfmfinfo(fe)->dev_font = jss->jss_dev_font;
}

/* ARGSUSED */
DEV_FONT
jstfm_fontdict(fe, c)
struct font_entry *fe;
int c;
{
    return jstfmfinfo(fe)->dev_font;
}
#endif

/* TYPE: jfm
 */
#ifdef UNDEF
jfm_access(proto, fe, acca)
char *proto;
struct font_entry *fe;
struct accarg *acca;
{
    int dev_is_jfm();

    return gentfm_access(proto, fe, acca, dev_is_jfm, "jfm");
}
#endif

void
init_jfm_fontinfo(fe)
struct font_entry *fe;
{
    int	null_markchar();
    void read_jfm_fontinfo();

    bfinfo(fe) = biaccinfo(fe)->bf;
    fe->fnt_type = FT_JFM;
    /*fe->base_fe->fnt_use = FU_USED;*/
    fe->fnt_markchar = null_markchar;
    fe->fnt_readfontinfo = read_jfm_fontinfo;
}

void
read_jfm_finfo(fe)
struct font_entry *fe;
{
    FILE *fntfp;
    struct jfmtype_entry *te;
    struct jfmchar_entry *ce;
    int i;
    SCALED s;

    openfontfile(fe);
    if (!readjfm(fntfp = fe->openfile))
	Fatal("%s is not jfm file", fe->name);
    jfmfinfo(fe)->nctype = nt;
    jfmfinfo(fe)->ctype = NEWTAB(struct jfmtype_entry, nt, "jfmtype table");
    for (i = 0, te = jfmfinfo(fe)->ctype; i < nt; i++, te++) {
	te->jfm_code = getuint(fntfp, 2);
	te->jfm_type = getuint(fntfp, 2);
    }
    jfmfinfo(fe)->lasttypecode = (te-1)->jfm_code;
    jfmfinfo(fe)->ch = NEWTAB(struct jfmchar_entry, ec+1, "jfmchar table");
    s = (SCALED)fe->s;
    for (i = bc, ce = (jfmfinfo(fe)->ch)+bc; i <= ec; i++, ce++) {
	ce->tfmw = scale(makeuint(width+4*getuint(fntfp,1),4), s);
	(void)fseek(fntfp, 3L, SEEK_CUR);
    }
}

void
read_jfm_fontinfo(fe)
struct font_entry *fe;
{
    struct jfmfntinfo *jfmfi;

    jfmfi = NEW(struct jfmfntinfo, "jfmfont info");
    jfmfi->jfm_bf = bfinfo(fe);
    jfmfinfo(fe) = jfmfi;
    read_jfm_finfo(fe);
    dev_jfm_initfe(fe, id);
    dev_jfm_initfontdict(fe);
}

getctype(c, jfmfi)
int c;
struct jfmfntinfo *jfmfi;
{
    int m;
    int left, right;
    struct jfmtype_entry *te;
    int code;

    if (c > jfmfi->lasttypecode)
	return 0;
    for (left = 0, right = jfmfi->nctype, te = jfmfi->ctype; left <= right;) {
	m = (left+right)/2;
	if (c < (code = (te+m)->jfm_code))
	    right = m-1;
	else if (c > code)
	    left = m+1;
	else if (c == code)
	    return (te+m)->jfm_type;
    }
    return 0;
}


/* utilities
 */
readtfm(fntfp)
FILE *fntfp;
{
    (void)fseek(fntfp, 2L, SEEK_SET);	/* lf */
    lh = getuint(fntfp, 2);
    bc = getuint(fntfp, 2);
    ec = getuint(fntfp, 2);
    nw = getuint(fntfp, 2);
    (void)fseek(fntfp, 8*4L, SEEK_SET);
    getbytes(fntfp, header, (lh-2)*4 > JSTFMHEAD ? JSTFMHEAD : (lh-2)*4);
    (void)fseek(fntfp, (6+lh+(ec-bc)+1)*4L, SEEK_SET);
    getbytes(fntfp, width, 4*nw);
    (void)fseek(fntfp, (6+lh)*4L, SEEK_SET);	/* ready to read char_info */
}

checkjfm(fntfp)
FILE *fntfp;
{
    int id;

    if ((id=getuint(fntfp, 2)) != JFM_ID && id != TJFM_ID)
	return FALSE;
}

readjfm(fntfp)
FILE *fntfp;
{
    if ((id=getuint(fntfp, 2)) != JFM_ID && id != TJFM_ID)
	return FALSE;
    nt = getuint(fntfp, 2);
    (void)fseek(fntfp, 2L, SEEK_CUR);	/* lf */
    lh = getuint(fntfp, 2);
    bc = getuint(fntfp, 2);	/* bc should be 0 */
    ec = getuint(fntfp, 2);
    nw = getuint(fntfp, 2);
    (void)fseek(fntfp, (7+lh+nt+(ec-bc)+1)*4L, SEEK_SET);
    getbytes(fntfp, width, 4*nw);
    (void)fseek(fntfp, (7+lh)*4L, SEEK_SET);	/* ready to read char_type and char_info */
    return TRUE;
}
