/*
 * TrueType font (FreeType2 lib)
 */
#include	"defs.h"
#include	"global.h"
#include	"bifont.h"
#ifdef KPATHSEA
#undef STREQ
#include	<kpathsea/config.h>
#include	<kpathsea/tex-file.h>
#endif

#ifdef FREETYPE

#include	"freetype.h"
#include	FT_TRUETYPE_IDS_H

#if FREETYPE_MAJOR==2
#if (FREETYPE_MINOR==2) || ((FREETYPE_MINOR==1) && (FREETYPE_PATCH>=10))
#define	EMBOLDEN
#endif
#endif

/* misc. functions for FreeType library
 */

static BOOLEAN ft_init = FALSE;
static FT_Library library;
static FT_Face face;
static FT_Matrix oscale = {(1<<16)/64, 0, 0, (1<<16)/64};
int embolden;

ft_initialize()
{
    if (!ft_init) {
#ifdef DEBUG
	if (Debuguser)
	    fprintf(stderr, "FreeType initializing\n");
#endif
	if (FT_Init_FreeType(&library))
	    Fatal("FreeType cannot be initialized");
	ft_init = TRUE;
    }
}

ft_glyph(filename)
char *filename;
{
    if (FT_New_Face(library, filename, 0, &face)) {
	Warning("FreeType font %s cannot be opened", filename);
	return FALSE;
    }
    return TRUE;
}

ft_size(filename, fsize)
char *filename;
int fsize;
{
    /* fsize = char size in sp (e.g. 9.15...pt*2^16) */
    if (FT_Set_Char_Size(face, 0, fsize/1024, resolution, resolution)) {
	Warning("FreeType font %s cannot set charsize", filename);
	return FALSE;
    }
    return TRUE;
}

ft_char(i, c)
int i, c;
{
    if (FT_Load_Glyph(face, i, FT_LOAD_TARGET_MONO))
	Fatal("FreeType cannot load glyph for char %d", c);
#ifdef EMBOLDEN
    if (embolden >= 0)
	FT_Outline_Embolden(&(face->glyph->outline), embolden);
#endif
    if (FT_Render_Glyph(face->glyph, FT_RENDER_MODE_MONO))
	Fatal("FreeType cannot render glyph for char %d", c);
}

fto_size1000(filename)
char *filename;
{
    /* make 1000x1000 coordinate:
       charwidth = 120*64 = 120 pt = 120/72 inch = (120/72)*600 pixel
                 = 1000 pixel
     */
    if (FT_Set_Char_Size(face, 0, 120*64, 600, 600)) {
	Warning("FreeType font %s cannot set charsize", filename);
	return FALSE;
    }
    return TRUE;
}

fto_char(c, i)
int c, i;
{
    if (FT_Load_Glyph(face, c, FT_LOAD_NO_BITMAP|FT_LOAD_NO_HINTING))
	/*FT_LOAD_NO_BITMAP|FT_LOAD_NO_SCALE|FT_LOAD_NO_HINTING))*/
	Fatal("FreeType cannot load glyph for char %d", i);
    FT_Outline_Transform(&(face->glyph->outline), &oscale);
#ifdef EMBOLDEN
    if (embolden >= 0)
	FT_Outline_Embolden(&(face->glyph->outline), embolden);
#endif
}

/* char width in 1000x1000 coordinate */
#define	cwid(cw,fw)	(int)((float)(cw)*1000/(float)(fw))


/* code encoding and conversion map
 */
#define	CS_TYPE1	1
#define	CS_TYPE2	2

struct unimap {
    unsigned short *map;
    char *name;
    char type;
    struct unimap *next;
};
static struct unimap *unimaps = NULL;
static char *unitab = "2uni";
static unsigned short *cconv_map;

set_unimap(enc)
char *enc;
{
    struct unimap *m, *um;
    char tabname[STRSIZE], file[PATHLEN];
    FILE *f;
    int sz, map_type, map_size, i, c1, c2;
    unsigned short *map;

    if (*enc == '\0')
	return FALSE;
    for (m = unimaps; m != NULL; m = m->next)
	if (strcmp(enc, m->name) == 0) {
	    cconv_map = m->map;
	    return TRUE;
	}
#ifdef DEBUG
    if (Debuguser)
	fprintf(stderr, "trying to find cconv map for %s\n", enc);
#endif
    strcpy(tabname, enc);
    strcat(tabname, unitab);
    if (findcconvmap(tabname, file)) {
	if ((f = BINARYOPEN(file)) == NULL) {
#ifdef DEBUG
	    if (Debuguser)
		fprintf(stderr, "-- cconv map %s cannot be opened\n", file);
#endif
	    return FALSE;
	}
	fseek(f, 0L, SEEK_END);
	sz = ftell(f);
	if (sz <= 512) {
	    map_type = CS_TYPE1;
	    map_size = 256;
	} else {
	    map_type = CS_TYPE2;
	    map_size = 94*94;
	}
	if ((map = (unsigned short *)malloc(map_size*sizeof(short))) == NULL)
	    Fatal("Unable to allocate memory for cconv\n");
	fseek(f, 0L, SEEK_SET);
	for (i = 0; i < map_size; i++) {
	    c1 = getc(f);
	    c2 = getc(f);
	    if (c1 == EOF || c2 == EOF)
		Fatal("cconv map %s incomplete\n", file);
	    map[i] = (c1<<8)|c2;
	}
	um = NEW(struct unimap, "unimap");
	um->map = map;
	um->name = enc;
	um->type = map_type;
	um->next = unimaps;
	unimaps = um;
	cconv_map = map;
#ifdef DEBUG
	if (Debuguser)
	    fprintf(stderr, "-- cconv map %s found\n", file);
#endif
	return TRUE;
    } else {
#ifdef DEBUG
	if (Debuguser)
	    fprintf(stderr, "-- cconv map not found\n");
#endif
	return FALSE;
    }
}

noconv(idx)
int idx;
{
    return idx;
}

iso1_to_uni(c)
int c;
{
    return (int)cconv_map[c];
}

idx94_to_uni(idx)
int idx;
{
    return (int)cconv_map[idx];
}


/* tfm
 */
void
init_ft_fontinfo(fe)
struct font_entry *fe;
{
    struct font_entry *repfe;
    int	mm_markchar();
    void read_ft_fontinfo();

    if (dev_mf_kind(tfmfinfo(fe)->tfm_bf) == MF_KIND_FT)
	biinifinfo(fe) = alloc_biinif(biaccinfo(fe)->bf);
    else if ((repfe = dev_get_repfe(biaccinfo(fe)->bf)) == NULL) {
	biinifinfo(fe) = alloc_biinif(biaccinfo(fe)->bf);
	dev_set_repfe(biinifinfo(fe)->bf, fe);
    } else
	biinifinfo(fe) = biinifinfo(repfe);
    fe->fnt_markchar = mm_markchar;
    fe->fnt_readfontinfo = read_ft_fontinfo;
}

void
t1_reencode(bii, remap)
struct biinitfontinfo *bii;
unsigned char remap[];
{
    char *enc;
    int i;
    char *glyphs[NTFMCHARS];

    if ((enc = enc_read(bii->bf)) == NULL) {
	for (i = 0; i < NTFMCHARS; i++)
	    remap[i] = i;
	return;
    }
    t1_get_encode(enc, glyphs);
    reencode(glyphs, bii->mark, bii->maxc, remap);
    t1_free_encode(glyphs);
}

void
read_ft_fontinfo(fe)
struct font_entry *fe;
{
    struct biinitfontinfo *bii;
    struct tfmfntinfo *tfmfi;
    char *fn, *filename;
    void read_tfm_finfo();
    BOOLEAN raster;
    int cmcount, cmid;
    FT_CharMap charmap;
    FT_UShort pid, eid;
    unsigned char remap[NTFMCHARS];
    int (*codeconv)();
    int tfmw;
    int nwidth, nheight, nxoff, ndepth;
    char *npixel;
    int i;
    extern int hconv, vconv;

    ft_initialize();

    bii = biinifinfo(fe);
    tfmfi = NEW(struct tfmfntinfo, "tfmfont info");
    tfmfi->tfm_bf = bii->bf;
    tfmfinfo(fe) = tfmfi;
    read_tfm_finfo(fe);
    dev_tfm_initfe(fe);

    raster = dev_mf_kind(tfmfi->tfm_bf) == MF_KIND_FT;
    if (!raster && dev_get_repfe(bii->bf) == NULL) {
	dev_fto_refontdict(fe);
	return;
    }
    fn = dev_fontpath(tfmfinfo(fe)->tfm_bf);
#ifdef KPATHSEA
    if ((filename = kpsearch_file(fn, fe->n, kpse_truetype_format)) == NULL) {
	Warning("FreeType font file %s not found", fn);
	read_null_fontinfo(fe);
	return;
    }
#else
    filename = fn;
#endif
#ifdef DEBUG
    if (Debuguser)
	fprintf(stderr, "FreeType openfont %s (for %s, %s)\n", filename, fe->n,
		raster ? "raster" : "outline");
#endif
    if (!ft_glyph(filename)) {
	read_null_fontinfo(fe);
	return;
    }
    cmcount = face->num_charmaps;
    for (cmid = 0; cmid < cmcount; cmid++) {
	charmap = face->charmaps[cmid];
	pid = charmap->platform_id;
	eid = charmap->encoding_id;
	/* TODO */
	if (pid == TT_PLATFORM_MICROSOFT) {
	    if (eid == TT_MS_ID_UNICODE_CS) {
		if (set_unimap(dev_cs_enc(tfmfi->tfm_bf)))
		    codeconv = iso1_to_uni;
		else
		    codeconv = noconv;
		break;
	    }
	}
	/*if (pid == TT_PLATFORM_MACINTOSH && eid == TT_MAC_ID_ROMAN)
	    break;*/
    }
    if (cmid == cmcount) {
	Warning("FreeType font %s does not have charmap", filename);
	read_null_fontinfo(fe);
	return;
    }
    if (FT_Set_Charmap(face, charmap)) {
	Warning("FreeType font %s cannot select charmap", filename);
	read_null_fontinfo(fe);
	return;
    }

    t1_reencode(bii, remap);
    if (raster) {
	tfmw = dev_ft_begfontdict(fe, bii->maxc);
	if (!ft_size(filename, tfmw)) {
	    read_null_fontinfo(fe);
	    return;
	}
	for (i = 0; i <= bii->maxc; i++) {
	    if (!(bii->mark[i]))
		continue;
	    ft_char(FT_Get_Char_Index(face, codeconv(remap[i])), i);
/*
	    trim_bitmap(bitmap.width, bitmap.cols, bitmap.rows,
			(-chxmin)/64, (-chymin)/64, bitmap.bitmap,
			&nwidth, &nheight, &nxoff, &ndepth, &npixel);
*/
	    npixel = face->glyph->bitmap.buffer;
	    nwidth = face->glyph->bitmap.pitch*8;
	    nheight = face->glyph->bitmap.rows;
	    nxoff = -face->glyph->bitmap_left;
	    ndepth = nheight - face->glyph->bitmap_top;
	    dev_ft_initfontdict(fe, tfmfi, i,
				nwidth, nheight, nxoff, ndepth, npixel);
	}
    } else {
	if (!fto_size1000(filename)) {
	    read_null_fontinfo(fe);
	    return;
	}
	tfmw = dev_fto_begfontdict(fe, face->family_name,
				   bii->mark, bii->maxc, remap);
	for (i = 0; i <= bii->maxc; i++) {
	    if (!(bii->mark[i]))
		continue;
	    fto_char(FT_Get_Char_Index(face, codeconv(remap[i])), i);
	    dev_fto_initfontdict(remap[i], &(face->glyph->outline),
				 cwid((tfmfi->ch+i)->tfmw,tfmw));
	}
	dev_fto_endfontdict(fe);
	dev_set_repfe(bii->bf, NULL);
    }

    FT_Done_Face(face);
}


/* jfm
 */
void
init_jft_fontinfo(fe)
struct font_entry *fe;
{
    int	jmm_markchar();
    void read_jft_fontinfo();

    biinifinfo(fe) = alloc_jbiinif(biaccinfo(fe)->bf);
    fe->fnt_markchar = jmm_markchar;
    fe->fnt_readfontinfo = read_jft_fontinfo;
}

struct jftfntinfo *
read_jft_finfo(fe, dname, bii, tfmw, settfmw)
struct font_entry *fe;
char *dname;
struct biinitfontinfo *bii;
int tfmw;
BOOLEAN settfmw;
{
    struct jftfntinfo *jftfi;
    char *fn, *filename;
    BOOLEAN raster;
    int cmcount, cmid;
    FT_CharMap charmap;
    FT_UShort pid, eid;
    int (*codeconv)();
    int nwidth, nheight, nxoff, ndepth;
    char *npixel;
    int nchars, i, jis;
    extern int idx94_to_sjis();
    extern int idx94_to_std();

    jftfi = (struct jftfntinfo *)
	alloc_check(malloc((unsigned)sizeof(struct jftfntinfo)+
			   bii->maxc*sizeof(struct jftchar_entry)),
		    "jftfont info");
    jftfi->jft_bf = bii->bf;

    for (i = 0; i <= bii->maxc; i++)
	jftfi->ch[i].dev_font = DEV_NULLFONT;

    raster = dev_mf_kind(jftfi->jft_bf) == MF_KIND_FT;
    fn = dev_fontpath(jftfi->jft_bf);
#ifdef KPATHSEA
    if ((filename = kpsearch_file(fn, fe->n, kpse_truetype_format)) == NULL) {
	Warning("FreeType font file %s not found", fn);
	read_null_fontinfo(fe);
	return;
    }
#else
    filename = fn;
#endif
#ifdef DEBUG
    if (Debuguser)
	fprintf(stderr, "FreeType openfont %s (for %s, %s)\n", filename, fe->n,
		raster ? "raster" : "outline");
#endif
    if (!ft_glyph(filename))
	return jftfi;
    cmcount = face->num_charmaps;
    for (cmid = 0; cmid < cmcount; cmid++) {
	charmap = face->charmaps[cmid];
	pid = charmap->platform_id;
	eid = charmap->encoding_id;
	/* TODO */
	if (pid == TT_PLATFORM_MICROSOFT) {
	    if (eid == TT_MS_ID_UNICODE_CS) {
		if (set_unimap(dev_cs_enc(jftfi->jft_bf))) {
		    codeconv = idx94_to_uni;
		    break;
		}
	    } else if (eid == TT_MS_ID_SJIS) {
		codeconv = idx94_to_sjis;
		break;
	    } else if (eid == TT_MS_ID_GB2312) {
		codeconv = idx94_to_std;
		break;
	    }
	}
    }
    if (cmid == cmcount) {
	Warning("FreeType font %s does not have charmap", filename);
	return jftfi;
    }
    if (FT_Set_Charmap(face, charmap)) {
	Warning("FreeType font %s cannot select charmap", filename);
	return jftfi;
    }

    embolden = dev_bold(jftfi->jft_bf);
#ifndef EMBOLDEN
    if (embolden >= 0)
	Warning("Embolden is not supported in this FreeType library");
#endif

    if (raster) {
	if (!ft_size(filename, tfmw))
	    return jftfi;
	for (i = 0; i <= bii->maxc; i++) {
	    if (!(bii->mark[i]))
		continue;
	    if (settfmw)
		(jftfi->ch+i)->tfmw = tfmw;
	    ft_char(FT_Get_Char_Index(face, codeconv(i)), jis = idx94_to_std(i));
/*
	    trim_bitmap(bitmap.width, bitmap.cols, bitmap.rows,
			(-chxmin)/64, (-chymin)/64, bitmap.bitmap,
			&nwidth, &nheight, &nxoff, &ndepth, &npixel);
*/
	    npixel = face->glyph->bitmap.buffer;
	    nwidth = face->glyph->bitmap.pitch*8;
	    nheight = face->glyph->bitmap.rows;
	    nxoff = -face->glyph->bitmap_left;
	    ndepth = nheight - face->glyph->bitmap_top;
	    dev_jft_initfontdict(fe, jftfi, i, jis, tfmw, 
				 nwidth, nheight, nxoff, ndepth, npixel);
	}
    } else {
	if (!fto_size1000(filename))
	    return jftfi;
	for (nchars = 0, i = 0; i <= bii->maxc; i++)
	    if (bii->mark[i])
		nchars++;
	dev_jfto_begfontdict(dname, face->family_name);
	for (i = 0; i <= bii->maxc; i++) {
	    if (!(bii->mark[i]))
		continue;
	    if (settfmw)
		(jftfi->ch+i)->tfmw = tfmw;
	    fto_char(FT_Get_Char_Index(face, codeconv(i)), idx94_to_std(i));
	    dev_jfto_initfontdict(jftfi, i, nchars--,
				  &(face->glyph->outline), 1000);
	}
	dev_jfto_endfontdict();
    }

    FT_Done_Face(face);
    return jftfi;
}

void
read_jft_fontinfo(fe)
struct font_entry *fe;
{
    struct biinitfontinfo *bii;
    struct jfmfntinfo *jfmfi;
    void read_jfm_finfo();
    int tfmw;

    ft_initialize();

    bii = biinifinfo(fe);
    jfmfi = NEW(struct jfmfntinfo, "jfmfont info");
    jfmfi->jfm_bf = bii->bf;
    jfmfinfo(fe) = jfmfi;
    read_jfm_finfo(fe);

    dev_jft_initfe(fe);
    tfmw = dev_jft_begfontdict(fe);
    jftfinfo(fe) = read_jft_finfo(fe, fe->n, bii, tfmw, TRUE);

    free((char *)bii);
    free((char *)jfmfi->ctype);
    free((char *)jfmfi->ch);
    free((char *)jfmfi);
}

#endif
#if defined(FREETYPE) || defined(VFLIB)

DEV_FONT
jft_fontdict(fe, c)
struct font_entry *fe;
int c;
{
    return jftfinfo(fe)->ch[jis_to_idx94(c)].dev_font;
}

#endif

#ifdef FREETYPE

/* jstfm
 */
void
init_jsft_fontinfo(fe)
struct font_entry *fe;
{
    struct jsubshare *jss;
    int	jsmm_markchar();
    void read_jsft_fontinfo();

    jss = jstfmfinfo(fe)->js_share;
    if (jss->jss_stat < JSS_INIT) {
	jss->jss_info = (struct jssinfo *)alloc_jbiinif(jss->jss_bf);
	jss->jss_stat = JSS_INIT;
    }
    jstfmfinfo(fe)->js_info = jss->jss_info;
    fe->fnt_markchar = jsmm_markchar;
    fe->fnt_readfontinfo = read_jsft_fontinfo;
}

void
read_jsft_fontinfo(fe)
struct font_entry *fe;
{
    struct biinitfontinfo *bii;
    struct jsubshare *jss;
    struct jstfmfntinfo *jstfmfi;
    void read_jstfm_finfo();
    int tfmw;

    ft_initialize();

    read_jstfm_finfo(fe);

    dev_jsft_initfe(fe);
    jss = jstfmfinfo(fe)->js_share;
    if (jss->jss_stat < JSS_READ) {
	bii = (struct biinitfontinfo *)jss->jss_info;
	jstfmfi = jstfmfinfo(fe);
	tfmw = dev_jsft_begfontdict(fe);
	jss->jss_info = (struct jssinfo *)
	    read_jft_finfo(fe, jss->jss_parent, bii, tfmw, FALSE);
	jss->jss_stat = JSS_READ;
	free((char *)bii);
    }
    jstfmfinfo(fe)->js_info = jss->jss_info;
}

#endif
#if defined(FREETYPE) || defined(VFLIB)

DEV_FONT
jsft_fontdict(fe, c)
struct font_entry *fe;
int c;
{
    struct jstfmfntinfo *jsfi = jstfmfinfo(fe);

    return jsftfinfo(jsfi)->ch[jsub_to_idx94(jsfi->jsubfont,c)].dev_font;
}


/*
 */
trim_bitmap(wid, bwid, hgt, xo, dep, pixel, nwid, nhgt, nxo, ndep, npixel)
int wid, bwid, hgt, xo, dep;
char *pixel;
int *nwid, *nhgt, *nxo, *ndep;
char **npixel;
{
    int ht, dt, lwt, rwt, i, j, nbwid;
    char *pixelend, *npix, *p0, *p1, *q;

    for (ht = 0; ht < hgt; ht++)
	for (i = ht*bwid; i < (ht+1)*bwid; i++)
	    if (*(pixel+i))
		goto htend;
 htend:
    hgt -= ht;
    npix = pixel+ht*bwid;

    for (dt = 0, pixelend = npix+hgt*bwid-1; dt < hgt ; dt++)
	for (i = dt*bwid; i < (dt+1)*bwid; i++)
	    if (*(pixelend-i))
		goto dtend;
 dtend:
    dep -= dt;
    hgt -= dt;

    for (lwt = 0; lwt < bwid; lwt++)
	for (i = lwt; i < hgt*bwid; i += bwid)
	    if (*(npix+i))
		goto lwtend;
 lwtend:
    for (rwt = 0; rwt < bwid; rwt++)
	for (i = bwid-1-rwt; i < hgt*bwid; i += bwid)
	    if (*(npix+i))
		goto rwtend;
 rwtend:
    npix += lwt;
    nbwid = bwid-(lwt+rwt);
    if (lwt+rwt > 0)
	for (p0 = npix+nbwid, p1 = npix+bwid, i = 1; i < hgt; p1 += bwid, i++)
	    for (q = p1, j = 0; j < nbwid; j++)
		*p0++ = *q++;

    *nwid = wid-((lwt+rwt)<<3);
    *nhgt = hgt;
    *nxo = xo-(lwt<<3);
    *ndep = dep;
    *npixel = npix;
}

#endif

#ifndef FREETYPE

void
init_ft_fontinfo(fe)
struct font_entry *fe;
{
    int null_markchar();
    void read_null_fontinfo();

    Warning("This %s is not compiled with FreeType option.\nFont %s is ignored",
	    G_progname, fe->n);
    fe->fnt_markchar = null_markchar;
    fe->fnt_readfontinfo = read_null_fontinfo;
}

void
init_jft_fontinfo(fe)
struct font_entry *fe;
{
    init_ft_fontinfo(fe);
}

void
init_jsft_fontinfo(fe)
struct font_entry *fe;
{
    init_ft_fontinfo(fe);
}

#endif
