generated html version of bdfedit.cHOME

#include "stdio.h"
#include "string.h"
#include "stdlib.h"
#include "termios.h"
#include "u8.h"

/* editor for editing my neoletters font.
Written in 2019 by Oren Watson
*/

#define ei else if

#define KEY_ALT 0x400000
#define KEY_UP 0x200001
#define KEY_DOWN 0x200002
#define KEY_LEFT 0x200003
#define KEY_RIGHT 0x200004
#define KEY_HOME 0x200005
#define KEY_END 0x200006
#define KEY_INS 0x200007
#define KEY_DEL 0x200008
#define KEY_PGUP 0x200009
#define KEY_PGDN 0x200010
#define KEY_F1 0x200011

const char * bdfeditlogo = "𝔅𝔇𝔉𝕖𝕕𝕚𝕥";

int getkey(){
	int c = getchar();
	if(c == EOF)return EOF;
	if(c == 033){
		c = getchar();
		if(c == '['){
			c = getchar();
			if(c=='A')return KEY_UP;
			if(c=='B')return KEY_DOWN;
			if(c=='C')return KEY_RIGHT;
			if(c=='D')return KEY_LEFT;
			if(c=='1') {
				c = getchar();
				if(c=='~')return KEY_HOME;
				if(c=='1')return KEY_F1;
			}
			if(c=='2') {
				c = getchar();
				if(c=='~')return KEY_INS;
			}
			if(c=='3') {
				c = getchar();
				if(c=='~')return KEY_DEL;
			}
			if(c=='4') {
				c = getchar();
				if(c=='~')return KEY_END;
			}
			if(c=='5') {
				c = getchar();
				if(c=='~')return KEY_PGUP;
			}
			if(c=='6') {
				c = getchar();
				if(c=='~')return KEY_PGDN;
			}
		}else if(c == 'O'){
			c = getchar();
			if(c=='F')return KEY_HOME;
			if(c=='H')return KEY_END;
			if(c=='P')return KEY_F1;
		}
		return c+KEY_ALT;
	}
	return c;
}

unsigned oterm_get_hex(int y, char *prompt) {
	unsigned num = 0;
	while(1) {
		printf("\033[%d;0H\033[2K%s %x",y,prompt,num);
		int c = getkey();
		if (c == EOF || c == '\r')break;
		if (c >= '0' && c <= '9')num = num * 16 + c - '0';
		if (c >= 'A' && c <= 'F')num = num * 16 + c - 'A' + 10;
		if (c >= 'a' && c <= 'f')num = num * 16 + c - 'a' + 10;
		if (c == '\b')num /= 16;
		if (c == 127)num /= 16;
	}
	return num;
}

void show_help();

struct bdfglyph {
	char name[16];
	int encoding;
	int dwidth;
	int bbox[4];
	unsigned bitmap[16];
};

struct bdfglyph *make_blank_char(unsigned gi) {
	struct bdfglyph *g = calloc(1,sizeof(struct bdfglyph));
	g->encoding = gi;
	sprintf(g->name,"uni%X",gi);
	g->dwidth = 18;
	return g;
}

unsigned leftshft(unsigned x,int i){
	if(i>0)return x << i;
	if(i<0)return x >> -i;
	return x;
}

void outputasbitmap(struct bdfglyph *g,int y, int x){
		int j;
		for(j=0;j<4;j++){
			int b;
			printf("\033[%d;%dH",y+j,x);
			for (b=0x20000;b!=0;b/=4) {
				int t =   (g->bitmap[j*4]&b	?0x01:0x00)
					| (g->bitmap[j*4]&(b/2)	?0x08:0x00)
					| (g->bitmap[j*4+1]&b  	?0x02:0x00)
					| (g->bitmap[j*4+1]&(b/2)?0x10:0x00)
					| (g->bitmap[j*4+2]&b	?0x04:0x00)
					| (g->bitmap[j*4+2]&(b/2)?0x20:0x00)
					| (g->bitmap[j*4+3]&b    ?0x40:0x00)
					| (g->bitmap[j*4+3]&(b/2)?0x80:0x00);
				printf("\342%c%c",(t>>6)|0240,(t&077)|0200);
			}
		}
}

void outputbigwithcur(struct bdfglyph *g, int y, int x){ //
	const char *const spa = "  ";
	const char *const blk = "██";
	const char *const spacur = "\033[32m▒▒\033[0m";
	const char *const blkcur = "\033[42m▒▒\033[0m";
	printf("character %x\r\n",g->encoding);// encoding number not editable directly
	printf("name: %s\r\n",g->name);
	int j;
	int pixwid = u8chrwid(g->encoding) * 9;
	for(j = 0;j < 16;j++) {
		unsigned b;
		int i;
		fputs("\e[0m║",stdout);
		for (i=0,b=0x20000;b!=0;b/=2,i++) {
			if (i == pixwid)fputs("\e[44m",stdout);
			if (i == x && j == y) {
				if(g->bitmap[j]&b) fputs(blkcur,stdout);
				else fputs(spacur,stdout);
			} else {
				if(g->bitmap[j]&b)fputs(blk,stdout);
				else fputs(spa,stdout);
			}
		}
		fputs("\e[0m║\n\r",stdout);
	}
}

char line[300];

void load_font(char * filename, struct bdfglyph **font) {
	FILE *bdfin = fopen(filename,"r");
	do {
		fgets(line,300,bdfin);
		if (!strncmp(line,"CHARS ",6)) break;
		printf("\e[7H%s",line);
	} while(1);

	int nchars;
	sscanf(line,"CHARS %d",&nchars);

	do {
		struct bdfglyph g;
		if(!fgets(line,300,bdfin))break;
		if(!strncmp(line,"ENDFONT",7))break;
		sscanf(line,"STARTCHAR %16s",g.name);
		fgets(line,300,bdfin);
		int ccode;
		sscanf(line,"ENCODING %d",&g.encoding);
		ccode = g.encoding;

		printf("\e[6H\e[Kstatus: U%05x\n\r\e[Kname: %s",ccode,g.name);
		do fgets(line,300,bdfin);
		while(strncmp(line,"DWIDTH",6));
		sscanf(line,"DWIDTH %d",&g.dwidth);
		fgets(line,300,bdfin);
		sscanf(line,"BBX %d %d %d %d",g.bbox,g.bbox+1,g.bbox+2,g.bbox+3); 
		// width, height, x offset, y offset (of lower left corner)
		fgets(line,300,bdfin);
		sscanf(line,"BITMAP");
		int bwidth = g.bbox[0]/8 + !!(g.bbox[0]%8);
		int bottom = 12 - g.bbox[3];
		int top = bottom - g.bbox[1];
		int lsb = g.bbox[2];
		int j;
		unsigned bitmap[16];
		for(j=0;j<g.bbox[1];j++){
			fgets(line,300,bdfin);
			sscanf(line,"%x",bitmap+j);
		}
		for(j=0;j<16;j++){
			g.bitmap[j]=0;
			if(j >= top && j < bottom) {
				int k = j - top;
				g.bitmap[j] = leftshft(bitmap[k],18 - bwidth * 8 - lsb);
			}
		}
		outputasbitmap(&g,2,1);
		font[ccode] = (struct bdfglyph*)malloc(sizeof(struct bdfglyph));
		*font[ccode] = g;
		do fgets(line,300,bdfin);
		while(strncmp(line,"ENDCHAR",7));

	}while(1);
	fclose(bdfin);

}

void save_font(char * filename, struct bdfglyph **font) {
	FILE *bdfout = fopen(filename,"w");
	unsigned gi;
	unsigned charcount = 0; //count the characters in font
	for (gi = 0; gi < 0x20000; gi++) {
		if(font[gi])charcount++;
	}
	fprintf(bdfout,"STARTFONT 2.1\n");
	fprintf(bdfout,"FONT neoletters\n");
	fprintf(bdfout,"SIZE 12 75 75\n");
	fprintf(bdfout,"FONTBOUNDINGBOX 18 16 0 -4\n");
	fprintf(bdfout,"STARTPROPERTIES 3\n");
	fprintf(bdfout,"PIXEL_SIZE 16\n");
	fprintf(bdfout,"FONT_ASCENT 12\n");
	fprintf(bdfout,"FONT_DESCENT 4\n");
	fprintf(bdfout,"ENDPROPERTIES\n");
	fprintf(bdfout,"CHARS %d\n",charcount);
	for (gi = 0; gi < 0x20000; gi++) {
		struct bdfglyph *g = font[gi];
		if(!g)continue;
		g->dwidth = u8chrwid(gi)*9;
		outputasbitmap(g,2,1);
		fprintf(bdfout,"STARTCHAR %s\n",g->name);
		fprintf(bdfout,"ENCODING %d\n",gi);
		fprintf(bdfout,"SWIDTH %d 0\n",g->dwidth*500/8);
		fprintf(bdfout,"DWIDTH %d 0\n",g->dwidth);
		/*calculate bounding box*/
		int j;
		unsigned orsum=0;
		int topnonzero = -1;
		int botnonzero = -1;
		for(j=0;j<16;j++){
			orsum |= g->bitmap[j];
			if(topnonzero == -1 && g->bitmap[j] != 0) topnonzero = j;
			if(g->bitmap[j] != 0) botnonzero = j;
		}
		int height = botnonzero - topnonzero + 1;
		int yoff = 11 - botnonzero;
		if (botnonzero == -1)yoff = 0;
		int i;
		int lefnonzero = -1;
		int rignonzero = -1;
		for(i=0;i<18;i++){
			int p = !!((0x20000>>i)&orsum);
			if(p) rignonzero = i;
			if(p && lefnonzero == -1) lefnonzero = i;
		}
		int width = rignonzero - lefnonzero + 1;
		int xoff = lefnonzero;
		if(lefnonzero == -1)xoff = 0;
		int bytes = width%8 ? width/8+1 : width/8;
		int zershift = 17-rignonzero;
		int byteshift = bytes*8-width;
		fprintf(bdfout,"BBX %d %d %d %d\nBITMAP\n",width,height,xoff,yoff);
		for(j=topnonzero;j<=botnonzero;j++){
			fprintf(bdfout,"%0*X\n",bytes*2,(g->bitmap[j]>>zershift)<<byteshift);
		}
		fprintf(bdfout,"ENDCHAR\n");
	}
	fprintf(bdfout,"ENDFONT\n");
	fclose(bdfout);
}


int main(int argc, char **argv){
	if(argc < 2) {
		printf("need a BDF to edit\n");
		return 0;
	}
	char filename[100];
	strcpy(filename,argv[1]);



	struct bdfglyph **font = (void*)calloc(0x20000,sizeof(void*));



	struct termios oldterm;
	struct termios rawterm;
	tcgetattr(0,&oldterm);
	cfmakeraw(&rawterm);
	tcsetattr(0,TCSANOW,&rawterm);
	printf("\033[2J\033[H\033[44m%s %s\n\r\033[0m",bdfeditlogo,filename);
	load_font(filename,font);

	/* editor state */
	int gi=60;// index of glyph being edited
	int y=0,x=0;// cursor position
	int redraw = 1;
	int i;
	struct bdfglyph copied;
	while (1) {
		if(gi >= 0x20000)gi = 0x1FFFF;
		struct bdfglyph *g = font[gi];
		if(redraw){
			printf("\033[2J\033[H\033[44;96m%s %s\n\r\033[0m",bdfeditlogo,filename);
			if (g) {
				outputasbitmap(g,2,1);
				printf("\033[3;15H◌%s",u8chtostr(gi));
				printf("\033[6;1H");
				outputbigwithcur(g,y,x);
			} else {
				printf ("no character U%x\n\r",gi);
				printf ("(^A to add blank)");
			}
		}
		redraw = 1;
		int c = getkey();
		if (c == CTRL('Q')) {
			save_font(filename,font);
			break; // quit
		}
		ei (c == CTRL('X'))break;
		ei (c == CTRL('C') && g)copied = *g;
		ei (c == CTRL('G'))gi = oterm_get_hex(2,"go to char:");
		ei (c == KEY_F1)show_help();
		ei (c == CTRL('A') && !g)font[gi] = make_blank_char(gi);
		ei (c == CTRL('V')) {
			if(!g) font[gi] = g = make_blank_char(gi);
			for(i = 0; i < 16; i++)g->bitmap[i] = copied.bitmap[i];
		}
		ei (c == 'j') {
			for(i = 0; i < 16; i++)g->bitmap[i] = (g->bitmap[i] << 1) & 0x3FFFF;
		}
		ei (c == 'l') {
			for(i = 0; i < 16; i++)g->bitmap[i] = g->bitmap[i] >> 1;
		}
		ei (c == 'i') {
			for(i = 0; i < 15; i++)g->bitmap[i] = g->bitmap[i+1];
			g->bitmap[15] = 0;
		}
		ei (c == 'k') {
			for(i = 15; i >= 1; i--)g->bitmap[i] = g->bitmap[i-1];
			g->bitmap[0] = 0;
		}
		ei (c == KEY_UP && y > 0)y--;
		ei (c == KEY_DOWN && y < 15)y++;
		ei (c == KEY_LEFT && x > 0)x--;
		ei (c == KEY_RIGHT && x < 17)x++;
		ei (c == KEY_PGUP && gi > 0)gi--;
		ei (c == KEY_PGDN && gi < 0x1FFFF)gi++;
		ei (c == KEY_HOME) {
			if (gi > 0)gi--;
			while(gi > 0 && !font[gi])gi--;
		}
		ei (c == KEY_END) {
			if (gi < 0x1FFFF)gi++;
			while (gi < 0x1FFFF && !font[gi])gi++;
		}
		ei (c == 'd' && g)g->bitmap[y] |= (0x20000 >> x);
		ei (c == 'e' && g)g->bitmap[y] &= ~(0x20000 >> x);
		ei (c == ' ' && g)g->bitmap[y] ^= (0x20000 >> x);
		else redraw = 0;
	}
	tcsetattr(0,TCSANOW,&oldterm);
	return 0;
}

void show_help() {
	printf("\033[2J\033[HBDF EDIT - Help Screen\r\n");
	printf("[^Q] exit and save\n\r");
	printf("[^X] exit and do not save\n\r");
	printf("[^G] go to character code\n\r");
	printf("[^C] copy character\n\r");
	printf("[^V] paste character\n\r");
	printf("[^A] add character\n\r");
	printf("[pgup] prev character\n\r");
	printf("[pgdn] next character\n\r");
	printf("[home] prev drawn character\n\r");
	printf("[end] next drawn character\n\r");
	printf("←↑→↓ move around the editing field\n\r");
	printf("[j|i|l|k] shift around the editing field\n\r");
	printf("[d] draw at cursor\n\r");
	printf("[e] erase at cursor\n\r");
	printf("[ ] toggle at cursor\n\r");
	printf("[F1] get help\n\r");
	printf("[enter] leave help\n\r");
	while(1) {
		int c = getkey();
		if(c == CTRL('Q'))break;
		if(c == CTRL('X'))break;
		if(c == '\r')break;
	}
}