/*
 * The font parser using the FreeType library version 2.
 *
 * see COPYRIGHT
 *
 */

#ifdef USE_FREETYPE

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <ctype.h>
#include <sys/types.h>

#include <ft2build.h>
#include FT_FREETYPE_H
#include FT_TRUETYPE_TABLES_H
#include FT_BBOX_H
#include FT_GLYPH_H

#include FT_CONFIG_CONFIG_H
#include FT_CONFIG_OPTIONS_H
#include FT_ERRORS_H
#include FT_SYSTEM_H
#include FT_IMAGE_H
#include FT_TYPES_H
#include FT_OUTLINE_H
#include FT_MODULE_H
#include FT_RENDER_H
#include FT_TYPE1_TABLES_H
#include FT_TRUETYPE_IDS_H
#include FT_TRUETYPE_TAGS_H
#include FT_MULTIPLE_MASTERS_H
#include FT_SFNT_NAMES_H

#ifdef XP_PSTEXT
#include "os.h"
#include "Xproto.h"
#include "font.h"
#include "fontstruct.h"
#include "fntfilst.h"
#include "fontutil.h"
#include "fontenc.h"
#include "ft.h"
#define NOT_IN_FTFUNCS
#include "ftfuncs.h"
#endif /* XP_PSTEXT */

#include "pt1.h"
#include "global.h"

/* prototypes of call entries */
static void openfont(char *fname, char *arg);
static void closefont( void);
static int getnglyphs ( void);
static int glnames( GLYPH *glyph_list);
static void glmetrics( GLYPH *glyph_list);
static int glenc( GLYPH *glyph_list, int *encoding, int *unimap);
static void fnmetrics( struct font_metrics *fm);
static void glpath( int glyphno, GLYPH *glyph_list);
static void kerning( GLYPH *glyph_list);

/* globals */

/* front-end descriptor */
struct frontsw freetype_sw = {
	/*name*/       "ft",
	/*descr*/      "based on the FreeType2 library",
	/*suffix*/     { "ttf", "ttc", "otf", "otc", "pfa", "pfb" },
	/*open*/       openfont,
	/*close*/      closefont,
	/*nglyphs*/    getnglyphs,
	/*glnames*/    glnames,
	/*glmetrics*/  glmetrics,
	/*glenc*/      glenc,
	/*fnmetrics*/  fnmetrics,
	/*glpath*/     glpath,
	/*kerning*/    kerning,
};

/* statics */

static char * dupcnstring( unsigned char *s, int len);

#ifndef XP_PSTEXT
static FT_Library library;
#endif /* !XP_PSTEXT */
static FT_Face face;

static int enc_type, enc_found;

/* SFNT functions do not seem to be included by default in FT2beta8 */
#define ENABLE_SFNT

/*
 * Open font and prepare to return information to the main driver.
 * May print error and warning messages.
 * Exit on error.
 */

static void
openfont(
	char *fname,
	char *arg /* unused now */
)
{
	FT_Error error;

#ifdef XP_PSTEXT
    extern FT_Face    xp_pstext_ft_face;
    extern FT_Library ftypeLibrary; /* defined in xc/lib/font/FreeType/ftfuncs.c */

    face = xp_pstext_ft_face;
#else
	if( FT_Init_FreeType( &library ) ) {
		fprintf(stderr, "** FreeType initialization failed\n");
		exit(1);
	}

	if( error = FT_New_Face( library, fname, 0, &face ) ) {
		if ( error == FT_Err_Unknown_File_Format )
			fprintf(stderr, "**** %s has format unknown to FreeType\n", fname);
		else
			fprintf(stderr, "**** Cannot access %s ****\n", fname);
		exit(1);
	}
#endif /* XP_PSTEXT */

	if(FT_HAS_FIXED_SIZES(face)) {
		WARNING_1 fprintf(stderr, "Font contains bitmaps\n");
	}
	if(FT_HAS_MULTIPLE_MASTERS(face)) {
		WARNING_1 fprintf(stderr, "Font contains multiple masters, using default\n");
	}

	if(ISDBG(FT)) fprintf(stderr," %d units per EM\n", face->units_per_EM);

	enc_found = 0;
}

/*
 * Close font.
 * Exit on error.
 */

static void
closefont(
	void
)
{
#ifdef XP_PSTEXT
        /* NOP */
#else
	if( FT_Done_Face(face) ) {
		WARNING_1 fprintf(stderr, "Errors when closing the font file, ignored\n");
	}
	if( FT_Done_FreeType(library) ) {
		WARNING_1 fprintf(stderr, "Errors when stopping FreeType, ignored\n");
	}
#endif /* XP_PSTEXT */
}

/*
 * Get the number of glyphs in font.
 */

static int
getnglyphs (
	void
)
{
	if(ISDBG(FT)) fprintf(stderr, "%d glyphs in font\n", face->num_glyphs);
	return (int)face->num_glyphs;
}

/*
 * Get the names of the glyphs.
 * Returns 0 if the names were assigned, non-zero if the font
 * provides no glyph names.
 */

static int
glnames(
	GLYPH *glyph_list
)
{
#define MAX_NAMELEN	1024

#ifdef XP_PSTEXT
	char          buf[1024];
	long          i;
    FT_Error      error;

#ifdef XP_ONLY_BLOCKS
    extern unsigned long xp_font_block_offset;
    extern FTFontPtr     xp_xtf;
    int                  bc; /* block counter */
    
    
    /* FixMe: This code should use PsOut_Get_FreeType_Glyph_Name() instead of
     * duplicating the code
     */
    for( bc = xp_font_block_offset ; bc < (xp_font_block_offset+256) ; bc++ ) {
        /* Remap X11 font index to FreeType font index */
        i = FTRemap(face, &xp_xtf->mapping, bc);
        
        if( i >= face->num_glyphs )
          continue;
#else
	for(i=0; i < face->num_glyphs; i++) {
#endif /* XP_ONLY_BLOCKS */
        if( FT_Has_PS_Glyph_Names(face) ) {
          error = FT_Get_Glyph_Name(face, i, buf, MAX_NAMELEN);
        }
        else
        {
          error = -1;
        }
    
		if( error ) {
          /* Check for unicode mapping
           * See Adobe document "Unicode and Glyph Names"
           * (http://partners.adobe.com/asn/tech/type/unicodegn.jsp)
           */
          if( (xp_xtf->mapping.mapping->type == FONT_ENCODING_UNICODE) &&
              (i < 0xFFFE) )
          {
            sprintf(buf, "uni%04lx", i);
          }
          else
          {
            sprintf(buf, "ch%02lx", i);
          }
		}
		glyph_list[i].name = strdup(buf);
		if(ISDBG(FT)) fprintf(stderr, "%d has name %s\n", i, buf);
		if (glyph_list[i].name == NULL) {
			fprintf (stderr, "****malloc failed %s line %d\n", __FILE__, __LINE__);
			exit(255);
		}
	}

	return 0;
#else
	char bf[1024];
	long i;

	if( ! FT_HAS_GLYPH_NAMES(face) ) {
		WARNING_1 fprintf(stderr, "Font has no glyph names\n");
		return 1;
	}

	for(i=0; i < face->num_glyphs; i++) {
		if( FT_Get_Glyph_Name(face, i, bf, MAX_NAMELEN) || bf[0]==0 ) {
			sprintf(bf, "_g_%d", i);
			WARNING_2 fprintf(stderr,
				"Glyph No. %d has no postscript name, becomes %s\n", i, bf);
		}
		glyph_list[i].name = strdup(bf);
		if(ISDBG(FT)) fprintf(stderr, "%d has name %s\n", i, bf);
		if (glyph_list[i].name == NULL) {
			fprintf (stderr, "****malloc failed %s line %d\n", __FILE__, __LINE__);
			exit(255);
		}
	}

	return 0;
#endif /* XP_PSTEXT */
}

/*
 * Get the metrics of the glyphs.
 */

static void
glmetrics(
	GLYPH *glyph_list
)
{
	GLYPH          *g;
	int i;
	FT_Glyph_Metrics *met;
	FT_BBox bbox;
	FT_Glyph gly;

#ifdef XP_ONLY_BLOCKS
    extern unsigned long xp_font_block_offset;
    extern FTFontPtr     xp_xtf;
    int                  bc; /* block counter */
    
    for( bc = xp_font_block_offset ; bc < (xp_font_block_offset+256) ; bc++ ) {
        /* Remap X11 font index to FreeType font index */
        i = FTRemap(face, &xp_xtf->mapping, bc);
        
        if( i >= face->num_glyphs )
          continue;
        
#else
	for(i=0; i < face->num_glyphs; i++) {
#endif /* XP_ONLY_BLOCKS */
		g = &(glyph_list[i]);

		if( FT_Load_Glyph(face, i, FT_LOAD_NO_BITMAP|FT_LOAD_NO_SCALE) ) {
			fprintf(stderr, "Can't load glyph %s, skipped\n", g->name);
			continue;
		}

		met = &face->glyph->metrics;

		if(FT_HAS_HORIZONTAL(face)) {
			g->width = met->horiAdvance;
			g->lsb = met->horiBearingX;
		} else {
			WARNING_2 fprintf(stderr, "Glyph %s has no horizontal metrics, guessed them\n", g->name);
			g->width = met->width;
			g->lsb = 0;
		}

		if( FT_Get_Glyph(face->glyph, &gly) ) {
			fprintf(stderr, "Can't access glyph %s bbox, skipped\n", g->name);
			continue;
		}

		FT_Glyph_Get_CBox(gly, ft_glyph_bbox_unscaled, &bbox);
		g->xMin = bbox.xMin;
		g->yMin = bbox.yMin;
		g->xMax = bbox.xMax;
		g->yMax = bbox.yMax;

		g->ttf_pathlen = face->glyph->outline.n_points;
	}
}

/*
 * Get the original encoding of the font. 
 * Returns 1 for if the original encoding is Unicode, 2 if the
 * original encoding is other 16-bit, 0 if 8-bit.
 */

static int
glenc(
	GLYPH *glyph_list,
	int *encoding,
	int *unimap
)
{
#ifdef XP_PSTEXT
	int                  i,
                         e;
	unsigned             code;
    extern FTFontPtr     xp_xtf;
    extern unsigned long xp_font_block_offset;
        
    enc_found = 1;
    enc_type  = 0;
    
	for(i=0; i<ENCTABSZ; i++) {
		if(encoding[i] != -1)
			continue;

        /* Remap X11 font index to FreeType font index */
        code = FTRemap(face, &xp_xtf->mapping, xp_font_block_offset+i);

		if(code == 0)
			continue; /* .notdef */

		encoding[i] = code;
	}
        
	return enc_type;
#else
	int i, e;
	unsigned code;

	if(ISDBG(FT)) 
		for(e=0; e < face->num_charmaps; e++) {
			fprintf(stderr, "found encoding pid=%d eid=%d\n", 
				face->charmaps[e]->platform_id,
				face->charmaps[e]->encoding_id);
		}

	if(enc_found)
		goto populate_map;

	enc_type = 0;

	/* first check for an explicit PID/EID */

	if(force_pid != -1) {
		for(e=0; e < face->num_charmaps; e++) {
			if(face->charmaps[e]->platform_id == force_pid
			&& face->charmaps[e]->encoding_id == force_eid) {
				WARNING_1 fprintf(stderr, "Found Encoding PID=%d/EID=%d\n", 
					force_pid, force_eid);
				if( FT_Set_Charmap(face, face->charmaps[e]) ) {
					fprintf(stderr, "**** Cannot set charmap in FreeType ****\n");
					exit(1);
				}
				enc_type = 1;
				goto populate_map;
			}
		}
		fprintf(stderr, "*** TTF encoding table PID=%d/EID=%d not found\n", 
			force_pid, force_eid);
		exit(1);
	}

	/* next check for a direct Adobe mapping */

	if(!forcemap) {
		for(e=0; e < face->num_charmaps; e++) {
			if(face->charmaps[e]->encoding == ft_encoding_adobe_custom) {
				WARNING_1 fputs("Found Adobe Custom Encoding\n", stderr);
				if( FT_Set_Charmap(face, face->charmaps[e]) ) {
					fprintf(stderr, "**** Cannot set charmap in FreeType ****\n");
					exit(1);
				}
				goto populate_map;
			}
		}
	}

	for(e=0; e < face->num_charmaps; e++) {
		if(face->charmaps[e]->platform_id == 3) {
			switch(face->charmaps[e]->encoding_id) {
			case 0:
				WARNING_1 fputs("Found Symbol Encoding\n", stderr);
				break;
			case 1:
				WARNING_1 fputs("Found Unicode Encoding\n", stderr);
				enc_type = 1;
				break;
			default:
				WARNING_1 {
					fprintf(stderr,
					"****MS Encoding ID %d not supported****\n",
						face->charmaps[e]->encoding_id);
					fputs("Treating it like Symbol encoding\n", stderr);
				}
				break;
			}
			break;
		}
	}
	if(e >= face->num_charmaps) {
		WARNING_1 fputs("No Microsoft encoding, using first encoding available\n", stderr);
		e = 0;
	}
	
	if( FT_Set_Charmap(face, face->charmaps[e]) ) {
		fprintf(stderr, "**** Cannot set charmap in FreeType ****\n");
		exit(1);
	}

populate_map:
	enc_found = 1;
	for(i=0; i<ENCTABSZ; i++) {
		if(encoding[i] != -1)
			continue;
		if(enc_type == 1 || forcemap) {
			code = unimap[i];
			if(code == (unsigned) -1)
				continue;
		} else
			code = i;

		code = FT_Get_Char_Index(face, code);
		if(0 && ISDBG(FT)) fprintf(stderr, "code of %3d is %3d\n", i, code);
		if(code == 0)
			continue; /* .notdef */
		encoding[i] = code;
	}

	return enc_type;
#endif /* XP_PSTEXT */
}

/* duplicate a string with counter to a 0-terminated string */
static char *
dupcnstring(
	unsigned char *s,
	int len
)
{
	char *res, *out;
	int i, c;
	static int warned=0;

	if(( res = malloc(len+1) )==NULL) {
		fprintf (stderr, "****malloc failed %s line %d\n", __FILE__, __LINE__);
		exit(255);
	}

	out = res;
	for(i=0; i<len; i++) {
		if(( c=s[i] )>=' ' && c!=127)
			*out++ = c;
		else if(!warned) {
			warned=1;
			WARNING_1 fprintf(stderr, "Some font name strings are in Unicode, may not show properly\n");
		}
	}
	*out = 0;
	return res;
}

/*
 * Get the font metrics
 */
static void 
fnmetrics(
	struct font_metrics *fm
)
{
	char *str;
	static char *fieldstocheck[3];
#ifdef ENABLE_SFNT
	FT_SfntName sn;
#endif /* ENABLE_SFNT */
	int i;

	fm->italic_angle = 0.0; /* FreeType hides the angle */
	fm->underline_position = face->underline_position;
	fm->underline_thickness = face->underline_thickness;
	fm->is_fixed_pitch = FT_IS_FIXED_WIDTH(face);

	fm->ascender = face->ascender;
	fm->descender = face->descender;

	fm->units_per_em =  face->units_per_EM;

	fm->bbox[0] = face->bbox.xMin;
	fm->bbox[1] = face->bbox.yMin;
	fm->bbox[2] = face->bbox.xMax;
	fm->bbox[3] = face->bbox.yMax;

#ifdef ENABLE_SFNT
	if( FT_Get_Sfnt_Name(face, TT_NAME_ID_COPYRIGHT, &sn) )
#endif /* ENABLE_SFNT */
		fm->name_copyright = "";
#ifdef ENABLE_SFNT
	else
		fm->name_copyright = dupcnstring(sn.string, sn.string_len);
#endif /* ENABLE_SFNT */

	fm->name_family = face->family_name;

	fm->name_style = face->style_name;
	if(fm->name_style == NULL)
		fm->name_style = "";

#ifdef ENABLE_SFNT
	if( FT_Get_Sfnt_Name(face, TT_NAME_ID_FULL_NAME, &sn) ) 
#endif /* ENABLE_SFNT */
	{
		int len;

		len = strlen(fm->name_family) + strlen(fm->name_style) + 2;
		if(( fm->name_full = malloc(len) )==NULL) {
			fprintf (stderr, "****malloc failed %s line %d\n", __FILE__, __LINE__);
			exit(255);
		}
		strcpy(fm->name_full, fm->name_family);
		if(strlen(fm->name_style) != 0) {
			strcat(fm->name_full, " ");
			strcat(fm->name_full, fm->name_style);
		}
	} 
#ifdef ENABLE_SFNT
	else
		fm->name_full = dupcnstring(sn.string, sn.string_len);
#endif /* ENABLE_SFNT */

#ifdef ENABLE_SFNT
	if( FT_Get_Sfnt_Name(face, TT_NAME_ID_VERSION_STRING, &sn) )
#endif /* ENABLE_SFNT */
		fm->name_version = "1.0";
#ifdef ENABLE_SFNT
	else
		fm->name_version = dupcnstring(sn.string, sn.string_len);
#endif /* ENABLE_SFNT */

#ifdef XP_PSTEXT
    {
      extern const char *xp_psfontname;
      
      fm->name_ps = strdup(xp_psfontname);

      /* Handle the rare case if a family name was not provided by the TTF
       * font (like Solaris TTF fonts in /usr/openwin/lib/locale/ko.UTF-8/X11/fonts/TrueType,
       * /usr/openwin/lib/locale/ko/X11/fonts/TrueType) - in this case we
       * have to generate a family name somehow... */
      if(fm->name_family == NULL)
        fm->name_family = fm->name_ps;
    }
#else

#ifdef ENABLE_SFNT
	if( FT_Get_Sfnt_Name(face, TT_NAME_ID_PS_NAME , &sn) ) {
#endif /* ENABLE_SFNT */
		if(( fm->name_ps = strdup(fm->name_full) )==NULL) {
			fprintf (stderr, "****malloc failed %s line %d\n", __FILE__, __LINE__);
			exit(255);
		}
#ifdef ENABLE_SFNT
	} else
		fm->name_ps = dupcnstring(sn.string, sn.string_len);
#endif /* ENABLE_SFNT */

#endif /* XP_PSTEXT */

	for(i=0; fm->name_ps[i]!=0; i++)
		if(fm->name_ps[i] == ' ')
			fm->name_ps[i] = '_'; /* no spaces in the Postscript name *m */

	/* guess the boldness from the font names */
	fm->force_bold=0;

	fieldstocheck[0] = fm->name_style;
	fieldstocheck[1] = fm->name_full;
	fieldstocheck[2] = fm->name_ps;

	for(i=0; !fm->force_bold && i<sizeof fieldstocheck /sizeof(fieldstocheck[0]); i++) {
		str=fieldstocheck[i];
		for(i=0; str[i]!=0; i++) {
			if( (str[i]=='B'
				|| str[i]=='b' 
					&& ( i==0 || !isalpha(str[i-1]) )
				)
			&& !strncmp("old",&str[i+1],3)
			&& !islower(str[i+4])
			) {
				fm->force_bold=1;
				break;
			}
		}
	}
}

/*
 * Functions to decompose the outlines
 */

static GLYPH *curg;
static double lastx, lasty;

static int
outl_moveto(
	FT_Vector *to,
	void *unused
)
{
	double tox, toy;

	tox = fscale((double)to->x); toy = fscale((double)to->y);

	/* FreeType does not do explicit closepath() */
	if(curg->lastentry) {
		g_closepath(curg);
	}
	fg_rmoveto(curg, tox, toy);
	lastx = tox; lasty = toy;

	return 0;
}

static int
outl_lineto(
	FT_Vector *to,
	void *unused
)
{
	double tox, toy;

	tox = fscale((double)to->x); toy = fscale((double)to->y);

	fg_rlineto(curg, tox, toy);
	lastx = tox; lasty = toy;

	return 0;
}

static int
outl_conicto(
	FT_Vector *control1,
	FT_Vector *to,
	void *unused
)
{
	double c1x, c1y, tox, toy;

	c1x = fscale((double)control1->x); c1y = fscale((double)control1->y);
	tox = fscale((double)to->x); toy = fscale((double)to->y);

	fg_rrcurveto(curg,
		(lastx + 2.0 * c1x) / 3.0, (lasty + 2.0 * c1y) / 3.0,
		(2.0 * c1x + tox) / 3.0, (2.0 * c1y + toy) / 3.0,
		tox, toy );
	lastx = tox; lasty = toy;

	return 0;
}

static int
outl_cubicto(
	FT_Vector *control1,
	FT_Vector *control2,
	FT_Vector *to,
	void *unused
)
{
	double c1x, c1y, c2x, c2y, tox, toy;

	c1x = fscale((double)control1->x); c1y = fscale((double)control1->y);
	c2x = fscale((double)control2->x); c2y = fscale((double)control2->y);
	tox = fscale((double)to->x); toy = fscale((double)to->y);

	fg_rrcurveto(curg, c1x, c1y, c2x, c2y, tox, toy);
	lastx = tox; lasty = toy;

	return 0;
}

static FT_Outline_Funcs ft_outl_funcs = {
	outl_moveto,
	outl_lineto,
	outl_conicto,
	outl_cubicto,
	0,
	0
};

/*
 * Get the path of contrours for a glyph.
 */

static void
glpath(
	int glyphno,
	GLYPH *glyf_list
)
{
	FT_Outline *ol;

	curg = &glyf_list[glyphno];

	if( FT_Load_Glyph(face, glyphno, FT_LOAD_NO_BITMAP|FT_LOAD_NO_SCALE|FT_LOAD_NO_HINTING) 
	|| face->glyph->format != ft_glyph_format_outline ) {
		fprintf(stderr, "Can't load glyph %s, skipped\n", curg->name);
		return;
	}

	ol = &face->glyph->outline;
	lastx = 0.0; lasty = 0.0;

	if( FT_Outline_Decompose(ol, &ft_outl_funcs, NULL) ) {
		fprintf(stderr, "Can't decompose outline of glyph %s, skipped\n", curg->name);
		return;
	}

	/* FreeType does not do explicit closepath() */
	if(curg->lastentry) {
		g_closepath(curg);
	}

	if(ol->flags & ft_outline_reverse_fill) {
		assertpath(curg->entries, __FILE__, __LINE__, curg->name);
		reversepaths(curg);
	}
}

/*
 * Get the kerning data.
 */

static void
kerning(
	GLYPH *glyph_list
)
{
	int	i, j, n;
	int	nglyphs = face->num_glyphs;
	FT_Vector k;
	GLYPH *gl;

	if( nglyphs == 0 || !FT_HAS_KERNING(face) ) {
        WARNING_1 fputs("No Kerning data\n", stderr);
		return;
	}

	for(i=0; i<nglyphs; i++)  {
		if( (glyph_list[i].flags & GF_USED) ==0)
			continue;
		for(j=0; j<nglyphs; j++) {
			if( (glyph_list[j].flags & GF_USED) ==0)
				continue;
			if( FT_Get_Kerning(face, i, j, ft_kerning_unscaled, &k) )
				continue;
			if( k.x == 0 )
				continue;

			addkernpair(i, j, k.x);
		}
	}
}

#endif