/*
 * This program is incomplete and broken. If it gets finished some day it 
 * can be used to generate linux console fonts with freetype from fonts 
 * known to fontconfig. 
 * Add '-lfontconfig `freetype-config --libs`' to command line if you 
 * decide to try it, but don't bother sending me bug reports :)
 *
 * Hacked up by Jani Jaakkola <jjaakkol@cs.helsinki.fi>
 *
 *    This program is free software; you can redistribute it and/or modify
 *    it under the terms of the GNU General Public License as published by
 *    the Free Software Foundation; either version 2 of the License, or
 *    (at your option) any later version.
 *                
 *    This program is distributed in the hope that it will be useful,
 *    but WITHOUT ANY WARRANTY; without even the implied warranty of
 *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *    GNU General Public License for more details.
 */
 


#include <limits.h>
#include <ft2build.h> 
#include FT_FREETYPE_H 
#include<stdio.h>
#include<stdlib.h>
#include<fontconfig/fontconfig.h>
#include<fontconfig/fcfreetype.h>
#include<string.h>

#define PSF2_MAGIC0     0x72
#define PSF2_MAGIC1     0xb5
#define PSF2_MAGIC2     0x4a
#define PSF2_MAGIC3     0x86

/* bits used in flags */
#define PSF2_HAS_UNICODE_TABLE 0x01

/* max version recognized so far */
#define PSF2_MAXVERSION 0

/* UTF8 separators */
#define PSF2_SEPARATOR  0xFF
#define PSF2_STARTSEQ   0xFE

int min_left=INT_MAX;
int max_right=INT_MIN;
int max_bottom=INT_MIN;
int min_top=INT_MAX;
int max_width=32;

struct psf2_header {
        unsigned char magic[4];
        unsigned int version;
        unsigned int headersize;    /* offset of bitmaps in file */
        unsigned int flags;
        unsigned int length;        /* number of glyphs */
        unsigned int charsize;      /* number of bytes for each character */
        unsigned int height, width; /* max dimensions of glyphs */
        /* charsize = height * ((width + 7) / 8) */
};

unsigned char **buf=NULL;
int pixels_x=0;
int pixels_y=0;

FT_Face load_face(const char *file, int index, double size) {
	FT_Library library; /* handle to library */  
	FT_Face face; /* handle to face object */  
	int error;

	error = FT_Init_FreeType( &library ); 
	if ( error ) {
		fprintf(stderr,"Could not init freetype\n");
		exit(1);
	}
	error = FT_New_Face( library,  file, index, &face);
	if ( error == FT_Err_Unknown_File_Format ) {
		fprintf(stderr, "Unknown file format\n");
		exit(1);
	} else if (error) {
		fprintf(stderr, "Something opening ft file\n");
		exit(1);
	}	
	FT_Set_Char_Size(face,0,size*64,96,96);
	return face;
}

void print_bitmap(unsigned long c) {
	int i,j;
	if (isprint(c)) { printf(" %c  ",(int)c); }
	else printf(" ?? ");
	for(j=0;j<pixels_x;j++) printf("-");
	printf("\n");
	for(i=0;i<pixels_y; i++) {
		printf("%3d:",i);
		for(j=0;j<pixels_x; j++) {
			if (buf[i][j]) printf("#");
			else printf(" ");
		}
		printf(":\n");
	}
	printf("%-4lx",c);
	for(j=0;j<pixels_x;j++) printf("-");
	printf("\n");
}

void draw_bitmap(FT_Bitmap *b, int left, int top, int check_sizes) {
	int i,j;
	int print=0;
	/* 
	printf("bitmap left=%d top=%d width=%d height=%d\n",
	left,top,b->width, b->rows); */
	if (b->pixel_mode!=FT_PIXEL_MODE_GRAY &&
		b->pixel_mode!=FT_PIXEL_MODE_MONO) {
		printf("Wrong pixel mode\n");
		return;
	}
	if(b->pitch<=0) {
		printf("pitch<0\n");
		/* b->pitch=-b->pitch; */
		return;
	}
	for (i=0; i<b->rows; i++) {
		for(j=0;j<b->width;j++) {
			int y=pixels_y-(top-i);
			int x=left+j;
			if (x>=0 && y>=0 && x<pixels_x && y<pixels_y) {
				switch (b->pixel_mode) {
				case FT_PIXEL_MODE_GRAY:
					buf[y][x]=(unsigned char)b->buffer[i*b->pitch+j];
					break;
				case FT_PIXEL_MODE_MONO:
					if ((unsigned char)b->buffer[i*b->pitch+(j/8)] &
					    (1<< (7-(j%8)))) {
						buf[y][x]=0xff;
					}
					break;
				}
				if (buf[y][x] && check_sizes) {
					if (x< min_left)  { min_left=x; }
					if (x> max_right) { max_right=x; }
					if (y> max_bottom) { max_bottom=y; }
					if (y< min_top) { min_top=y; }
				}					
			}
		}
	}
	if (print) print_bitmap('x');

}



void write_1byte_bitmap(FILE *out) {
	int i,j;
	unsigned int byte;
	for(i=min_top; i<=max_bottom; i++) {
		int b=7;
		byte=0;
		j=min_left;
		while(j<=max_right) {
			if (buf[i][j]) {
				byte |= (1<<b);
			}
			j++;
			b--;
			if (b<0) {
				fputc(byte,out);
				b=7;
				byte=0;
			}
		}
		if (b!=7) {fputc(byte,out); }
	}
}

int render_glyph(FT_Face face, int glyph_index, int check_sizes) {
	int pen_x,pen_y,use_kerning;
	pen_x = 0;
	pen_y = (face->bbox.yMax-face->bbox.yMin)/32;
	unsigned long previous=0;
	int error;
	FT_GlyphSlot slot = face->glyph;
    
	pen_x=-face->bbox.xMin*face->size->metrics.x_scale/0xffff/64;
	pen_y=-face->size->metrics.descender/64;/* /face->units_per_EM; */
#if 0
	printf("descender=%ld\n",face->size->metrics.descender);
	printf("size=%lu units_per_EM=%d yMin=%d xMin=%d pen_y=%d\n",
	       face->size, face->units_per_EM, face->bbox.yMin, face->bbox.xMin, pen_y);
#endif
	use_kerning = FT_HAS_KERNING( face ); 
	/* if (use_kerning) printf("Using kerning\n");
	   printf("Rendering\n"); */
	previous = 0; 

#if 0	

	/* retrieve kerning distance and move pen position */ 
	if ( use_kerning && previous && glyph_index ) { 
		FT_Vector delta; 
		FT_Get_Kerning( face, previous, glyph_index, 
				FT_KERNING_DEFAULT, &delta );
		pen_x += delta.x >> 6; 
	} 
#endif
	/* load glyph image into the slot (erase previous one) */
	error = FT_Load_Glyph( face, glyph_index, FT_LOAD_RENDER|FT_LOAD_TARGET_MONO); 
	if ( error ) return -1;

	draw_bitmap( &slot->bitmap, 
		     pen_x + slot->bitmap_left,
		     pen_y + slot->bitmap_top, check_sizes);
	/* record current glyph index */ 
	previous = glyph_index;
	
	return 0;
}	


void clear_bitmap() {
	int i;
	for(i=0;i<pixels_y; i++) {
		memset(buf[i],0,pixels_x);
	}
}

int render_char(FT_Face face, unsigned long unicode) {
	/* convert character code to glyph index */
	int r;
	int glyph_index = FT_Get_Char_Index( face, unicode ); 
	if (glyph_index==0) {
		printf("No glyph for %lx\n",unicode);
	}
	r=render_glyph(face,glyph_index,0);
	if (r>=0) print_bitmap(unicode);
	return r;
}

void out_unicode(FILE *stream, unsigned long code) {
	if (code<=0x7f) {
		fputc(code,stream);
	} else if (code<=0x7ff) {
		fputc(0xc0 | (code >> 6),stream);
		fputc(0x80 | (code & 0x3f),stream);
	} else if (code<=0xffff) {
		fputc(0xe0 | (code >> 12),stream);
		fputc(0x80 | ((code >> 6) & 0x3f),stream);
		fputc(0x80 | (code & 0x3f),stream);
	} else {
		fprintf(stderr,"Can't handle unicode > 0xffff\n");
	}
}

struct glyph_map_struct {
	unsigned long charcode;
	struct glyph_map_struct *next;
};
typedef struct glyph_map_struct glyph_map;
glyph_map **map=NULL;

void index_face(FT_Face face) {
	FT_ULong  charcode;
	FT_UInt   gindex;
	int chars=0;
	int duplicates=0;

	map=calloc(sizeof(*map)*face->num_glyphs,1);
	if (!map) { perror("malloc"); exit(1); }
	charcode = FT_Get_First_Char( face, &gindex );
	while ( gindex != 0 ) {
		glyph_map *m=malloc(sizeof(glyph_map));
		int glyph=FT_Get_Char_Index( face, charcode );

		if (!m) { perror("malloc"); exit(1); }
		m->charcode=charcode;
		
		if (map[glyph]) duplicates++;
		m->next=map[glyph];
		map[glyph]=m;

		chars++;
		/* printf("glyph #%4x is char \\x%lx\n", glyph, charcode); */
		
		charcode = FT_Get_Next_Char( face, charcode, &gindex );
	}
	printf("Font has %d characters, %d duplicate glyphs\n",chars,duplicates);
}

void write_psf2_font(FT_Face face) {
	FILE *out=fopen("foo.psf","w+");
	struct psf2_header header;
	int i;

	header.length=256;
/* 	header.length=(face->num_glyphs>512)?512:face->num_glyphs;*/
	printf("Counting maximal glyph sizes\n");
	for(i=0;i<header.length;i++) {
		int glyph_index = FT_Get_Char_Index( face, i ); 
		if (glyph_index>0) render_glyph(face,glyph_index,1);
		clear_bitmap();
	}
	printf("Font minimal bbox size: %dx%d\n",
	       max_right-min_left,
	       max_bottom-min_top);
/*	max_right++;
	min_left--;
	max_bottom++;
	min_top--; */

   if (max_right-min_left+1>max_width) 
     {
	printf("Font too wide, setting size\n");
	max_right=max_width-1-min_left;
     }
   
	header.magic[0]=PSF2_MAGIC0;
	header.magic[1]=PSF2_MAGIC1;
	header.magic[2]=PSF2_MAGIC2;
	header.magic[3]=PSF2_MAGIC3;
	header.version=0;
	header.headersize=sizeof(header);
	header.flags= PSF2_HAS_UNICODE_TABLE;
	header.height=max_bottom-min_top+1;
	header.width=max_right-min_left+1;
	header.charsize = header.height * ((header.width + 7) / 8);
	fwrite(&header,sizeof(header),1,out);

	printf("Output font final size: %dx%d\n",
	       header.width,
	       header.height);
	
	printf("Writing font of %d glyphs\n",header.length);
	for(i=0;i<header.length;i++) {
		int glyph_index = FT_Get_Char_Index( face, i ); 
		clear_bitmap();
		if (glyph_index>0) render_glyph(face,glyph_index,0);
		write_1byte_bitmap(out);
	}

	printf("Writing unicode map\n");
	for(i=0;i<header.length;i++) {
		int glyph_index = FT_Get_Char_Index( face, i ); 
		glyph_map *m=map[glyph_index];
		while(m) {
			out_unicode(out,m->charcode);
			m=m->next;
		}
		fputc(PSF2_SEPARATOR,out);
	}
	fclose(out);
}

int main(int argc, char **argv) {
	FcConfig *config;
	FcPattern *pattern,*font;
	FcResult result;	FcChar8 *file;
	int index;
	double size;
	FT_Face face;
	
	config=FcInitLoadConfigAndFonts();
	if (!config) {
		fprintf(stderr,"Could not init fontconfig\n");
		exit(1);
	}
	
	pattern=FcNameParse(argv[1]);
	if (!pattern) {
		fprintf(stderr,"Could not parse pattern\n");
	}
	if (!FcConfigSubstitute(config, pattern, FcMatchPattern)) {
		fprintf(stderr,"FcConfigSubstitute failed\n");
		exit(1);
	}
	font=FcFontMatch(config, pattern, &result);
	if (!font || result==FcResultNoMatch) {
		fprintf(stderr,"Could not find matching font\n");
		exit(1);
	}
	FcPatternGetDouble(font, FC_SIZE, 0, &size);
	FcPatternGetString(font, FC_FILE, 0, &file);
	FcPatternGetInteger(font, FC_INDEX, 0, &index);

	if (size<1) size=12;
	index=10;
	fprintf(stderr,"File: %s index: %d size:%lf\n",file,index,size);
	face=load_face(file,index,size);
	if (!face) {
		fprintf(stderr,"Could not load face\n");
		exit(1);
	}
	
	pixels_x=(face->bbox.xMax-face->bbox.xMin)*
		face->size->metrics.x_scale/0xffff/64;
	pixels_y=(face->bbox.yMax-face->bbox.yMin)*
		face->size->metrics.y_scale/0xffff/64;

	printf("Bounding box size %dx%d\n",pixels_x, pixels_y);
	{
		int i;
		int eset=0;
		for(i=0;i<face->num_charmaps;i++) {
			unsigned int encoding=face->charmaps[i]->encoding;
			int j;
			if (encoding==FT_ENCODING_UNICODE && !eset) {
				printf("Using unicode encoding\n");
				FT_Set_Charmap(face,face->charmaps[i]);
				eset=1;
			}
			printf("Encoding #%d:",i);
			for(j=3;j>=0;j--) {
				int x= (encoding >> (j*8)) & 0xff;
				if (isprint(x)) printf("%c",x);
				else printf("\\x%2x",x);
			}
			printf("\n");

		}
	}
	printf("Font has %ld glyphs\n",face->num_glyphs);

	buf=malloc(sizeof(char *)*pixels_y);
	{
		int i=0;
		
		for (i=0; i<pixels_y; i++) {
			buf[i]=calloc(sizeof(char)*pixels_x,1);
		}
	}
	index_face(face);
	write_psf2_font(face);

	return 0;
}

