#include <stdlib.h>
#include <stdio.h>
#include <gd.h>
#include <string.h>
#include <stdint.h>
#include <wchar.h>
#include <unistd.h>
#include <sys/mman.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <locale.h>
#include <iconv.h>
/* to determine codepoint categories */
#include <unicode/uchar.h>
/* to output UTF-32 codepoints in proper encoding for terminal */
#include <unicode/ustdio.h>

#include <sys/types.h>
#include <limits.h>

#define SEMICOLON 59

#define __INT48_TYPE__

#ifdef __INT48_TYPE__
typedef signed __INT48_TYPE__ int48_t;
typedef unsigned __INT48_TYPE__ uint48_t;
typedef int48_t int_least48_t;
typedef uint48_t uint_least48_t;
typedef int48_t int_fast48_t;
typedef uint48_t uint_fast48_t;
# define __int_least32_t int48_t
# define __uint_least32_t uint48_t
# define __int_least16_t int48_t
# define __uint_least16_t uint48_t
# define __int_least8_t int48_t
# define __uint_least8_t uint48_t
#endif /* __INT48_TYPE__ */

#ifdef __INT48_TYPE__
# ifdef __INT48_C_SUFFIX__
#  define INT48_C(v) __int_c(v, __INT48_C_SUFFIX__)
#  define UINT48_C(v) __uint_c(v, __INT48_C_SUFFIX__)
#  define __int32_c_suffix __INT48_C_SUFFIX__
#  define __int16_c_suffix __INT48_C_SUFFIX__
#  define __int8_c_suffix  __INT48_C_SUFFIX__
# else
#  define INT48_C(v) v
#  define UINT48_C(v) v ## U
#  undef __int32_c_suffix
#  undef __int16_c_suffix
#  undef  __int8_c_suffix
# endif /* __INT48_C_SUFFIX__ */
#endif /* __INT48_TYPE__ */

#ifdef __INT48_TYPE__
# define INT48_MAX           INT48_C(140737488355327)
# define INT48_MIN         (-INT48_C(140737488355327)-1)
# define UINT48_MAX         UINT48_C(281474976710655)
# define INT_LEAST48_MIN     INT48_MIN
# define INT_LEAST48_MAX     INT48_MAX
# define UINT_LEAST48_MAX   UINT48_MAX
# define INT_FAST48_MIN      INT48_MIN
# define INT_FAST48_MAX      INT48_MAX
# define UINT_FAST48_MAX    UINT48_MAX
# define __INT_LEAST32_MIN   INT48_MIN
# define __INT_LEAST32_MAX   INT48_MAX
# define __UINT_LEAST32_MAX UINT48_MAX
# define __INT_LEAST16_MIN   INT48_MIN
# define __INT_LEAST16_MAX   INT48_MAX
# define __UINT_LEAST16_MAX UINT48_MAX
# define __INT_LEAST8_MIN    INT48_MIN
# define __INT_LEAST8_MAX    INT48_MAX
# define __UINT_LEAST8_MAX  UINT48_MAX
#endif /* __INT48_TYPE__ */

struct BoundBox {
	int x;
	int y;
	int w;
	int h;
};

void Convert_Brect(int *brect, struct BoundBox *bb){
	int h_x=INT_MIN;
	int l_x=INT_MAX;
	int h_y=INT_MIN;
	int l_y=INT_MAX;

	if(brect[0]>h_x)
		h_x = brect[0];
	if(brect[2]>h_x)
		h_x = brect[2];
	if(brect[4]>h_x)
		h_x = brect[4];
	if(brect[6]>h_x)
		h_x = brect[6];

	if(brect[0]<l_x)
		l_x = brect[0];
	if(brect[2]<l_x)
		l_x = brect[2];
	if(brect[4]<l_x)
		l_x = brect[4];
	if(brect[6]<l_x)
		l_x = brect[6];

	if(brect[1]>h_y)
		h_y = brect[1];
	if(brect[3]>h_y)
		h_y = brect[3];
	if(brect[5]>h_y)
		h_y = brect[5];
	if(brect[7]>h_y)
		h_y = brect[7];

	if(brect[1]<l_y)
		l_y = brect[1];
	if(brect[3]<l_y)
		l_y = brect[3];
	if(brect[5]<l_y)
		l_y = brect[5];
	if(brect[7]<l_y)
		l_y = brect[7];

	bb->w = (h_x - l_x);
	bb->h = (h_y - l_y);

        bb->x = brect[6];
        bb->y = brect[7]-bb->h;

	return;
}

int utf8_encode(char *out, uint48_t utf)
{
  if (utf <= 0x7F) {
    // Plain ASCII
    out[0] = (char) utf;
    out[1] = '\0';
    return 1;
  } else if (utf <= 0x07FF) {
    // 2-byte unicode
    out[0] = (char) (((utf >> 6) & 0x1F) | 0xC0);
    out[1] = (char) (((utf >> 0) & 0x3F) | 0x80);
    out[2] = '\0';
    return 2;
  } else if (utf <= 0xFFFF) {
    // 3-byte unicode
    out[0] = (char) (((utf >> 12) & 0x0F) | 0xE0);
    out[1] = (char) (((utf >>  6) & 0x3F) | 0x80);
    out[2] = (char) (((utf >>  0) & 0x3F) | 0x80);
    out[3] = '\0';
    return 3;
  } else if (utf <= 0x001FFFFF) {
//  } else if (utf <= 0x10FFFF) {
    // 4-byte unicode
    out[0] = (char) (((utf >> 18) & 0x07) | 0xF0);
    out[1] = (char) (((utf >> 12) & 0x3F) | 0x80);
    out[2] = (char) (((utf >>  6) & 0x3F) | 0x80);
    out[3] = (char) (((utf >>  0) & 0x3F) | 0x80);
    out[4] = '\0';
    return 4;
  } else if (utf <= 0x03FFFFFF) {
    // 5-byte unicode
    out[0] = (char) (((utf >> 24) & 0x03) | 0xF8);
    out[1] = (char) (((utf >> 18) & 0x3F) | 0x80);
    out[2] = (char) (((utf >> 12) & 0x3F) | 0x80);
    out[3] = (char) (((utf >>  6) & 0x3F) | 0x80);
    out[4] = (char) (((utf >>  0) & 0x3F) | 0x80);
    out[5] = 0;
    return 5;
  } else if (utf <= 0x7FFFFFFF) {
    // 6-byte unicode
    out[0] = (char) (((utf >> 30) & 0x01) | 0xFC);
    out[1] = (char) (((utf >> 24) & 0x3F) | 0x80);
    out[2] = (char) (((utf >> 18) & 0x3F) | 0x80);
    out[3] = (char) (((utf >>  12) & 0x3F) | 0x80);
    out[4] = (char) (((utf >>  6) & 0x3F) | 0x80);
    out[5] = (char) (((utf >>  0) & 0x3F) | 0x80);
    out[6] = 0;
    return 6;
  }  else {
    // error - use replacement character
    out[0] = (char) 0xEF;  
    out[1] = (char) 0xBF;
    out[2] = (char) 0xBD;
    out[3] = 0;
    return 0;
  }


}



int main(int argc, char **argv){
	FILE *image_out;
	FILE *font_fnt_out;
	FILE *input_hex;
	FILE *testout;

	gdImagePtr im;
	int black;
	int white;

	int brect[8];
	int x, y;
	char *err;
	int no_fontconfig;
	int row_cur = 0;
	int row_spacing_pixels = 20;
	int col_spacing_pixels = 20;
	int bounding_box_width_max = 0;
	int bounding_box_height_max = 0;
	int bounding_box_width_cur = 0;
	int bounding_box_height_cur = 0;
	int row_width_max = 0;
	int row_width_cur = 0;
	int columns = 50;
	int column_cur = 0;
	//int row_width_cur = 0;
	int row_width_widest = 0;

	wchar_t space = ' ';
	wchar_t tab = '\t';
	wchar_t newline = '\n';
	wchar_t null = '\0';

	int image_width = 0;
	int image_height = 0;
	wint_t c;
	uint64_t total_chars_to_print = 0;
	uint32_t total_rows = 0;
	uint32_t total_cols = 0;
	char linestring[1024];
char outputsring[1024];
char *s;

char test[64];
uint48_t hexchar;
iconv_t cd;
char *pIn;
char *pOut;
size_t inbytesleft = 4;
size_t outbytesleft = 4;
char *inbuf;
char *outbuf;
int num1;


int tallest_char = 0;
int widest_char = 0;
char Tallest_char[20];
char Widest_char[20];

char *locale = setlocale(LC_ALL, "en_US.UTF-8");
wchar_t *bla;
struct BoundBox bb;
	double pixel_pt_size = 3;
	char *input_ttf = "/mnt/sdb1/pub/test/gdfont/iosevka-fixed-slab-extended.ttf";
	int aa_disable;

        int status;
	gdFTStringExtra Strex = {0,0,0,0,0,NULL,NULL};
	gdFTStringExtraPtr strex = &Strex;


strex->flags |= gdFTEX_LINESPACE;
strex->flags |= gdFTEX_CHARMAP;
strex->flags |= gdFTEX_RESOLUTION;
strex->flags |= gdFTEX_DISABLE_KERNING;
//strex->flags |= gdFTEX_XSHOW;
strex->flags |= gdFTEX_RETURNFONTPATHNAME;
//strex->flags |= gdFTEX_FONTPATHNAME;
//strex->flags |= gdFTEX_FONTCONFIG;
//strex->flags |= gdFTEX_FONTPATHNAME;


strex->linespacing = 1.00;
strex->charmap = gdFTEX_Unicode;
//FT_ENCODING_UNICODE
//strex->charmap = gdFTEX_Big5;
//strex->charmap = gdFTEX_Shift_JIS;
//strex->charmap = gdFTEX_Adobe_Custom;

//strex->hdpi=1350;
//strex->vdpi=1950;

strex->hdpi=187;
strex->vdpi=187;





//do nothing but find the widest char and the tallest char
        if( ( input_hex = fopen("hex.txt", "r" ) ) == NULL ) {
                fprintf( stderr, "Error opening %s\n", "hex.txt" );
                return 1;
        }
	memset ((void*) linestring, '\0', sizeof (linestring));
        x = row_spacing_pixels+row_spacing_pixels;
        y = col_spacing_pixels+col_spacing_pixels;

        while( fgets(linestring, sizeof(linestring), input_hex) != NULL){
                //printf("%s", linestring);
                //replace newline with null
                for(num1=0; num1<sizeof(linestring); num1++){
                        if(linestring[num1] == '\n'){
                                linestring[num1] = '\0';
                                break;
                        }
                }
                //populate brect to get font box width/height

	memset ((void*)test, '\0', sizeof(test));
	hexchar = (uint48_t)strtoul((const char *)linestring, NULL, 16);

	if(hexchar > 0xFFFF)
        	break;

	(void)utf8_encode(test, hexchar);
	err = gdImageStringFTEx(NULL,brect,0,input_ttf,pixel_pt_size,0.0,x,y,(const char *)test,strex);
                if (err) {fprintf(stderr,err); return 1;}

                bounding_box_width_cur = brect[4]-brect[0];
                bounding_box_height_cur = brect[3]-brect[7];

	//fprintf(font_fnt_out, "\"%s\"=%d,%d,%d,%d\n", test, brect[6], brect[7], bounding_box_width_cur, bounding_box_height_cur );

                row_width_cur += col_spacing_pixels + bounding_box_width_cur;
                //find tallest box in current row to know for next row's offset
                if(bounding_box_height_cur > bounding_box_height_max)
                        bounding_box_height_max = bounding_box_height_cur;

if(tallest_char < bounding_box_height_cur){
        tallest_char = bounding_box_height_cur;
	(void)strcpy((char *)Tallest_char, (const char *)test);
}
if(widest_char < bounding_box_width_cur){
        widest_char = bounding_box_width_cur;
	(void)strcpy((char *)Widest_char, (const char *)test);
}
                x += col_spacing_pixels + bounding_box_width_cur;
                //continue with current row
                if(column_cur < columns-1){
                        column_cur++;
                }else{ //max columns reached
                        y += row_spacing_pixels + bounding_box_height_max;
                        //add border space
                        row_width_cur += col_spacing_pixels;
                        //this row is widest yet, adjust image width
                        if(row_width_cur > image_width)
                                image_width = row_width_cur;
                        row_width_cur = 0;
                        //add to height for image
                        image_height += row_spacing_pixels + bounding_box_height_max;
                        bounding_box_height_max = 0;
                        //reset to first col for new row
                        column_cur = 0;
                        row_cur++;
                        x = row_spacing_pixels;
                }
        }

printf("tallest char: %d %s\n", tallest_char, Tallest_char);
printf("widest char: %d %s\n", widest_char, Widest_char);

fclose(input_hex);

col_spacing_pixels=widest_char;
row_spacing_pixels=tallest_char;



//Determine width/height of image and write font.fnt


strex->flags |= gdFTEX_LINESPACE;
strex->flags |= gdFTEX_CHARMAP;
strex->flags |= gdFTEX_RESOLUTION;
strex->flags |= gdFTEX_DISABLE_KERNING;
//strex->flags |= gdFTEX_XSHOW;
strex->flags |= gdFTEX_RETURNFONTPATHNAME;
//strex->flags |= gdFTEX_FONTPATHNAME;
//strex->flags |= gdFTEX_FONTCONFIG;
//strex->flags |= gdFTEX_FONTPATHNAME;


strex->linespacing = 1.00;
strex->charmap = gdFTEX_Unicode;
//FT_ENCODING_UNICODE
//strex->charmap = gdFTEX_Big5;
//strex->charmap = gdFTEX_Shift_JIS;
//strex->charmap = gdFTEX_Adobe_Custom;

//strex->hdpi=1350;
//strex->vdpi=1950;

strex->hdpi=187;
strex->vdpi=187;



        if( ( input_hex = fopen("hex.txt", "r" ) ) == NULL ) {
                fprintf( stderr, "Error opening %s\n", "hex.txt" );
               	return 1;
        }

        if( ( font_fnt_out = fopen("font.fnt", "w" ) ) == NULL ) {
                fprintf( stderr, "Error opening %s\n", "font.fnt" );
                return 1;
        }


	memset ((void*) linestring, '\0', sizeof (linestring));


fprintf(font_fnt_out, "%c image file\n", SEMICOLON);
fprintf(font_fnt_out, "image=font.png\n");
fprintf(font_fnt_out, "%c \"character\"=x,y,width,height\n", SEMICOLON);


        x = row_spacing_pixels+row_spacing_pixels;
        y = col_spacing_pixels+col_spacing_pixels;
        row_width_cur = 0;
        row_cur = 0;
        bounding_box_height_max = 0;
        column_cur = 0;



//memset ((void*)outputsring, '\0', sizeof(test));
(void)strcpy((char *)outputsring, (const char *)Tallest_char);
(void)strcat((char *)outputsring, (const char *)Widest_char);

	while( fgets(linestring, sizeof(linestring), input_hex) != NULL){
		//printf("%s", linestring);
		//replace newline with null
		for(num1=0; num1<sizeof(linestring); num1++){
			if(linestring[num1] == '\n'){
				linestring[num1] = '\0';
				break;
			}
		}
		//populate brect to get font box width/height

memset ((void*)test, '\0', sizeof(test));
hexchar = (uint48_t)strtoul((const char *)linestring, NULL, 16);

if(hexchar > 0xFFFF)
	break;

(void)utf8_encode(test, hexchar);

//



err = gdImageStringFTEx(NULL,brect,0,input_ttf,pixel_pt_size,0.0,x,y,(const char *)test,strex);
		if (err) {fprintf(stderr,err); return 1;}

//		bounding_box_width_cur = brect[4]-brect[0];
//		bounding_box_height_cur = brect[3]-brect[7];

//Convert_Brect(brect, &bb);
fprintf(font_fnt_out, "\"%s\"=%d,%d,%d,%d\n", test, brect[6], brect[7], bounding_box_width_cur, bounding_box_height_cur );


tallest_char
widest_char

//		row_width_cur += col_spacing_pixels + bounding_box_width_cur;
		//find tallest box in current row to know for next row's offset
//		if(bounding_box_height_cur > bounding_box_height_max)
//			bounding_box_height_max = bounding_box_height_cur;

//if(tallest_char < bounding_box_height_cur)
//	tallest_char = bounding_box_height_cur;

//if(widest_char < bounding_box_width_cur)
//	widest_char = bounding_box_width_cur;

		x += col_spacing_pixels + bounding_box_width_cur;
		//continue with current row
		if(column_cur < columns-1){
			column_cur++;

Tallest_char + Widest_char + (1 neededchar + Widest_char)*16
(void)strcat((char *)outputsring, (const char *)test);
(void)strcat((char *)outputsring, (const char *)Widest_char);

		}else{ //max columns reached

(void)strcpy((char *)outputsring, (const char *)Tallest_char);
(void)strcat((char *)outputsring, (const char *)Widest_char);
			y += row_spacing_pixels + bounding_box_height_max;
			//add border space
			row_width_cur += col_spacing_pixels;
			//this row is widest yet, adjust image width
			if(row_width_cur > image_width)
				image_width = row_width_cur;
			row_width_cur = 0;
			//add to height for image
			image_height += row_spacing_pixels + bounding_box_height_max;
			bounding_box_height_max = 0;
			//reset to first col for new row
			column_cur = 0;
			row_cur++;
			x = row_spacing_pixels;
		}
	}

//printf("tallest char: %d\n", tallest_char);
//printf("widest char: %d\n", widest_char);

fclose(input_hex);

strex->flags |= gdFTEX_LINESPACE;
strex->flags |= gdFTEX_CHARMAP;
strex->flags |= gdFTEX_RESOLUTION;
strex->flags |= gdFTEX_DISABLE_KERNING;
//strex->flags |= gdFTEX_XSHOW;
strex->flags |= gdFTEX_RETURNFONTPATHNAME;
//strex->flags |= gdFTEX_FONTPATHNAME;
//strex->flags |= gdFTEX_FONTCONFIG;
//strex->flags |= gdFTEX_FONTPATHNAME;


strex->linespacing = 1.00;
strex->charmap = gdFTEX_Unicode;
//strex->charmap = gdFTEX_Big5;
//strex->charmap = gdFTEX_Adobe_Custom;

//strex->hdpi=1350;
//strex->vdpi=1950;

strex->hdpi=187;
strex->vdpi=187;

        if( ( input_hex = fopen("hex.txt", "r" ) ) == NULL ) {
                fprintf( stderr, "Error opening %s\n", "hex.txt" );
                return 1;
        }

	if( ( testout = fopen("testout.txt", "w" ) ) == NULL ) {
                fprintf( stderr, "Error opening %s\n", "testout.txt" );
                return 1;
        }


	image_height += row_spacing_pixels+row_spacing_pixels+row_spacing_pixels;
	image_width  += col_spacing_pixels;

	//create image
	im = gdImageCreateTrueColor(image_width, image_height);
	white = gdImageColorResolve(im, 0, 0, 0);
	black = gdImageColorResolve(im, 0, 255, 0);

	x = row_spacing_pixels+row_spacing_pixels;
	y = col_spacing_pixels+col_spacing_pixels;
	row_width_cur = 0;
	row_cur = 0;
	bounding_box_height_max = 0;
	column_cur = 0;

        while( fgets(linestring, sizeof(linestring), input_hex) != NULL){
                for(num1=0; num1<sizeof(linestring); num1++){
                        if(linestring[num1] == '\n'){
                                linestring[num1] = '\0';
                                break;
                        }
                }

memset ((void*)test, '\0', sizeof(test));
hexchar = (uint48_t)strtoul((const char *)linestring, NULL, 16);
if(hexchar > 0xFFFF)
        break;
(void)utf8_encode(test, hexchar);

err = gdImageStringFTEx(im,brect,black,input_ttf,pixel_pt_size,0.0,x,y,(const char *)test,strex);
                if (err) {fprintf(stderr,err); return 1;}
                bounding_box_width_cur = brect[4]-brect[0];
                bounding_box_height_cur = brect[3]-brect[7];
                //find tallest box in current row to know for next row's offset
                if(bounding_box_height_cur > bounding_box_height_max)
                        bounding_box_height_max = bounding_box_height_cur;

                x += col_spacing_pixels + bounding_box_width_cur;
                //continue with current row
                if(column_cur < columns-1){
                        column_cur++;
                }else{ //max columns reached
fprintf(testout, "\n");
                        y += row_spacing_pixels + bounding_box_height_max;
                        //add border space
                        row_width_cur += col_spacing_pixels;
                        //this row is widest yet, adjust image width
                        bounding_box_height_max = 0;
                        //reset to first col for new row
                        column_cur = 0;
                        row_cur++;
			x = row_spacing_pixels;
                }
        }

fclose(font_fnt_out);
fclose(input_hex);
fclose(testout);

        image_out = fopen("test01.png", "w");
        gdImageInterlace(im, 1);
        gdImagePng(im, image_out);


        fclose(image_out);
        gdImageDestroy(im);

	return 0;
}
