/***************************************************************************** font.c vt200 font generator v1.0 copywrite: Harold Z. Bencowitz Beaumont, Texas 21-mar-87 ****************************************************************************** description: font is a program written in whitesmith's C to allow one to easily create or alter downloadable fonts/character sets for vt200 series terminals. it will only run on a vt2**. it has only been tested on rt11 v5.3 and tsx+ v6.01 using a vt220. it can be used to edit a previous character set (stored as a disk file in a format which can be "typed" to download the font). the vt200 built-in dec character sets (ascii, special graphics, and multinational) are included as disk files to allow one to alter any or all of these characters to create new characters or character sets. one character at a time is edited while each pixel change is observed both at the normal size and double high/double wide. operating instructions: delete exit program F17 open a font file F18 close the current font file, save (exit) F20 close the current font file, discard (quit) PF1 open a new active character PF2 close the active character, save (exit) PF4 close the active character, discard (quit) up arrow move up one space down arrow move down one space right arrow move right one space left arrow move left one space select mark/unmark a cell remove unmark all cells only these keys are usable while the program is running unless the user is answering a prompt for a file name, a character, or a y/n answer in which case the entire keyboard is usable. while the program is running, there is always a current character set, either the default blank set, or a set loaded from a disk file. for comparison, the current set is displayed with the dec ascii, dec multinational, and dec special graphics sets. the operator must open an active character for editing. that character is then loaded (from the current set) into a grid. using the arrow keys (to move about the grid), the select key (to mark or unmark a dot), and the remove key (to remove all of the dots) one can create whatever character one wishes within the limits of a 8 by 10 cell. note that the top row, the bottom two rows, and the right column of dots are generally reserved for intercharacter spacing (the bottom ones are also used for descenders). each time a change is made, the active character is redisplayed in normal and double high/wide formats. the sixel string descibing the character is also displayed. when the editing of a character is complete, the active character must be closed by quitting (discard the changes) or exiting (save the character into the current set) before working on another character. at any time (unless an active character is open) the current character set can be closed by quitting (discard the changes) or exiting (save to a disk file). it can not be closed unless the active character is closed first. another current set can be loaded by specifying the name of an appropriate disk file whenever there is no current character set (only a null set). the disk file format includes escape sequences at the beginning and end so that the file can be downloaded to the terminal using a type command. the default (carriage return without entry) response to all prompts is abort command. thus there is no default input or output file name. however both the input and output files have the default file extension ".fnt". if one wishes to include use of downloaded character sets into a program, he may benefit from examining the c subroutines for downloading, designating, and selecting character sets which are included in vt.c my video routine library. limitations: the characters must be built manually by marking each dot. when answering prompts, the return or tab keys must be used not the enter key. the program has not been tested on a vt240 or vt241 or on any other versions of rt11 or tsx+. revision history: v1.0 completed march 21, 1987 installation/building: the distribution kit consists of font.c (source code, including this text, the only documentation), font.h (header file), vt.(c, obj) (video terminal library), hclib (c, obj) (my c library), ascii.fnt (font file for the dec ascii character set), decmul.fnt (font file for the dec multinational character set), and decspc.fnt (font file for the dec special graphics, ie vt100 line drawing set). implementation notes: character sets are designated as: g0 ascii g1 current downloaded set g2 multinational g3 dec special cfont[] is an array ([95][16]) containing the ascii version of the current font/character set. each of the 95 downlaodable characters is represented by a 16 character string which is the sixel code for a 8 wide by 10 high character cell. the active character is stored internally as an array ccells[8][10] where each represents one dot in the character of value YES or NO. ttt *****************************************************************************/ #include #include #include #define VERSION "v1.0" char achar[2] = { 0, 0 }; /* active character */ char ccells[8][10] = { 0 }; /* grid flags (dot off/on) */ char cfont[95][16] = { 0 }; /* sixel strings current set */ char save[18] = { 0 }; /* active character, last sixel */ char set1[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz"; char set2[] = "0123456789!@#$%^&*()`~-_=+{}[];:'\"\\|,.?/<>"; int err_flag = NO; /* YES if error message displayed */ int cset_flag = NO; /* YES if a character set is open */ int xcur = 1; /* x and y grid coordiates of */ int ycur = 1; /* current position */ _main() { register int k, i = 0; int process(), vtclr(), screen(), istsx(), call_emt(), enter(); int ttflush(), vtscs(), move_cur(), vtttid(), vtesc(); unsigned int ijob; extern char cfont[][]; /* * set TSX singlechar mode, find terminal type */ if(istsx()) /* if running TSX+ */ i = call_emt(0152, 0, 'S'); /* set singlechar mode */ if(i < 0) { putstr(STDERR,"\nfont - unable to set singlechar mode\n",NULL); exit(); } i = vtttid(); /* get termianl type */ if(i != 200) { putstr(STDERR,"\nfont - illegal terminal type, not vt200\n",NULL); exit(); } /* * set terminal special mode, arrows to application mode */ ijob = JSW; JSW = ijob | 010000; vtesc("[?1h"); /* arrow key application mode */ /* * initialize current font */ for(i = 0; i < 95; i++) { for(k = 0; k < 16; k++) { cfont[i][k] = '?'; } } /* * initialize screen */ vtclr(2); /* clear screen */ screen(); /* make display */ move_cur(0, 0); /* home cursor */ ttflush(); /* * await keyboard input */ while(enter(&process)) /* respond to keyboard input */ ; /* * reset terminal and JSW */ JSW = ijob; /* reset JSW */ vtesc("[?1l"); /* arrow key movement mode */ vtclr(2); /* clear screen */ } /****************************************************************************/ int k_ocf() /* open a character set from a disk file */ { char fname[15]; register int c, i, k; int down_load(), getc(), vtmova(), vtmode(), vtmbox(); int vterln(), deftype(), dis_error(), vtebox(), move_cur(); FIO *fopen(), *fclose(), fpi; extern char achar[], set1[], set2[], cfont[][]; extern int cset_flag; /* * tests, active character or current set open? */ if(achar[0] != '\0' || cset_flag) { if(achar[0] != '\0') /* yes, active character */ dis_error("must close active character first"); else /* set currently open */ dis_error("must close current character set first"); return; } /* * get file name */ vtmova(6, 28); putstr(STDERR, "enter the input file name ", NULL); vtmode(8); vtmbox(6, 54, 14, ' '); i = vtebox(6, 54, 14, ' ', "s", fname, ""); vtmode(0); if(i < -1 || fname[0] == '\0') { dis_error("command aborted"); return; } deftype(fname, ".fnt"); /* * open file */ if(!fopen(&fpi, fname, READ)) { dis_error("unable to open file"); return; } /* * read in file */ vtmova(1, 55); putstr(STDERR, fname, NULL); i = getc(&fpi); c = getc(&fpi); if(i != '\033' || c != 'P') { /* is this the correct type of file */ dis_error("not a proper font file"); fclose(&fpi); return; } cset_flag = YES; while((c = getc(&fpi)) != '\n') ; /* burn up introducer string */ for(i = 0; i < 95; i++) { for(k = 0; k < 8; k++) cfont[i][k] = getc(&fpi); getc(&fpi); /* burn up / */ for(k = 8; k < 16; k++) cfont[i][k] = getc(&fpi); getc(&fpi); /* burn up ; */ getc(&fpi); /* burn up \n */ } fclose(&fpi); /* * download the character set */ down_load(); /* * erase prompt box, home cursor */ vtmova(6, 28); vterln(0); move_cur(0, 0); } /****************************************************************************/ int k_ccf(n) /* close (with/without saving to disk) the current font */ int n; { char nswer[2], fname[15]; static char s1[] = "P1;0;1;0;0;0\( @"; static char s2[] = "\\"; /* $\\ */ register int i, k; /* $P1;0;1;0;0;0\( @ */ int del_fnt(), move_cur(), vtmova(), vtmode(), vtmbox(), vtebox(); int ttflush(), vterln(), deftype(), dis_error(); FIO *fcreate(), *fclose(), fpo; extern char cfont[][], achar[]; extern int cset_flag; /* * tests, active char? current set open? */ if(achar[0] != '\0' || !cset_flag) { if(achar[0] != '\0') /* yes, active character */ dis_error("must close active character first"); else /* set currently open */ dis_error("no character set is open"); return; } /* * write font to disk */ if(n) { vtmova(6, 28); putstr(STDERR, "enter the output file name ", NULL); vtmode(8); vtmbox(6, 55, 14, ' '); i = vtebox(6, 55, 14, ' ', "s", fname, ""); vtmode(0); if(i < -1 || fname[0] == '\0') { dis_error("command aborted"); return; } deftype(fname, ".fnt"); if(fopen(&fpo, fname, READ)) { /* file with the same name? */ fclose(&fpo); vtmova(6, 28); vterln(0); putstr(STDERR,"do you wish to overwrite an existing file [y]?",NULL); vtmode(8); vtmbox(6, 76, 1, ' '); ttflush(); i = vtebox(6, 76, 1, ' ', "s", nswer, "y"); vtmode(0); if(i < -1 || nswer[0] == 'n' || nswer[0] == 'N') { dis_error("command aborted"); return; } } if(fcreate(&fpo, fname, WRITE)) { putf(&fpo, "%p", s1); putc(&fpo, '\n'); /**/ for(i = 0; i < 95; i++) { for(k = 0; k < 8; k++) { putc(&fpo, cfont[i][k]); } putc(&fpo, '/'); for(k = 8; k < 16; k++) { putc(&fpo, cfont[i][k]); } putc(&fpo, ';'); putc(&fpo, '\n'); /**/ } putf(&fpo, "%p", s2); fclose(&fpo); } } else { /* quit, discard set */ vtmova(6, 28); vterln(0); putstr(STDERR, "are you sure [y] ?", NULL); vtmode(8); vtmbox(6, 47, 1, ' '); ttflush(); i = vtebox(6, 47, 1, ' ', "s", nswer, "y"); vtmode(0); if(i < -1 || nswer[0] == 'n' || nswer[0] == 'N') { dis_error("command aborted"); return; } } cset_flag = NO; /* * initialize current font */ for(i = 0; i < 95; i++) { for(k = 0; k < 16; k++) { cfont[i][k] = '?'; } } /* * erase display of current font name, error line */ vtmova(1, 55); putstr(STDERR, " ", NULL); vtmova(6, 28); /* input/error line */ vterln(0); /* erase to end of line */ /* * download null font, home cursor */ del_fnt(); move_cur(0, 0); } /****************************************************************************/ int del_fnt() /* down load null font */ { static char s1[] = "P1;0;2;0;0;0\( @"; static char s2[] = ";\\"; static char s3[] = ";;;;;;;;;;;;;;;;;;;;;;;;"; /* $P1;0;1;0;0;0\( @ ;$\\ */ putstr(STDERR, s1, s3, s3, s3, s3, s2, NULL); } /****************************************************************************/ int down_load() /* download the entire current set */ { register int i, k; /* $P1;0;1;0;0;0\( @ */ static char s1[] = "P1;0;1;0;0;0\( @"; static char s2[] = "\\"; /* $\\ */ int putch(); extern char cfont[][]; putstr(STDERR, s1, NULL); for(i = 0; i < 95; i++) { for(k = 0; k < 8; k++) { putch(cfont[i][k]); } putch('/'); for(k = 8; k < 16; k++) { putch(cfont[i][k]); } putch(';'); } putch(-1); putstr(STDERR, s2, NULL); } /****************************************************************************/ int screen() /* initialize the screen */ { int del_fnt(), vtgrid(), vtdcs(), vtscs(), putstr(), vtmova(); extern char set1[], set2[], achar[]; /* * download null font */ del_fnt(); /* * designate character sets */ vtdcs(0, 'B'); vtdcs(1, '~'); vtdcs(2, '<'); vtdcs(3, '0'); /* * display program name and version number */ vtscs(0); /* ascii */ vtmova(1, 71); putstr(STDERR, "font", NULL); vtmova(1, 77); putstr(STDERR, VERSION, NULL); /* * make grid */ vtgrid(1, 0, 2, 1, 8, 10); /* * display character sets */ vtmova(13, 28); putstr(STDERR, set1, NULL); vtmova(14, 28); putstr(STDERR, set2, NULL); vtscs(1); /* downloaded character set */ vtmova(10, 28); putstr(STDERR, set1, NULL); vtmova(11, 28); putstr(STDERR, set2, NULL); vtscs(2); /* dec multinational */ vtmova(16, 28); putstr(STDERR, set1, NULL); vtmova(17, 28); putstr(STDERR, set2, NULL); vtscs(3); /* dec special graphics */ vtmova(19, 28); putstr(STDERR, set1, NULL); vtmova(20, 28); putstr(STDERR, set2, NULL); vtscs(0); /* * legend for double sized display of the active character */ vtmova(22, 0); putstr(STDERR, "current ascii multinat dec spc", NULL); } /****************************************************************************/ int k_cchar(n) /* close active character (with/without saving) */ int n; { char s0[18]; register int i, k, t; int dis_error(), move_cur(), vtdown(), c_to_s(), vtmova(), vterln(); extern char save[], set1[], set2[], cfont[][], achar[]; extern int cset_flag; /* * test is a character is active */ if(achar[0] == '\0') { dis_error("no active character is open"); return; } /* * erase display of current character */ vtmova(8, 28); vterln(0); /* erase to end of line */ vtmova(22, 60); /* erase sixel string */ vtclr(0); /* and double characters */ /* * if saving active character, download to current set */ if(n) { t = achar[0] - 32; c_to_s(s0); vtdown(achar[0], s0); /* download character */ cset_flag = YES; /* flag char set is open */ for(k = 0, i = 0; i < 8; i++) cfont[t][k++] = s0[i]; for(i = 9; i < 17; i++) cfont[t][k++] = s0[i]; } else /* restore the character */ vtdown(achar[0], save); /* download character */ /* * declare that no character is active */ achar[0] = '\0'; /* * erase character cell array */ for(i = 0; i < 8; i++) { for(k = 0; k < 10; k++) { if(ccells[i][k] == YES) { move_cur(i, k); putstr(STDERR, " ", NULL); ccells[i][k] = NO; } } } /* * home cursor */ move_cur(0, 0); } /****************************************************************************/ int k_ochar() /* select (open) an active character */ { char s0[18], si[3]; register int i, k, mask; int x, t; int vterln(), vtmode(), vtebox(), vtdouble(), vtscs(), move_cur(); int c_to_s(), dis_error(), vtmbox(), vtmova(); extern char save[], ccells[][], cfont[][], achar[]; /* * test if a character is already open */ if(achar[0] != '\0') { dis_error("must close active character first"); return; } /* * get character */ vtmova(6, 28); putstr(STDERR, "enter a character ", NULL); vtmode(8); vtmbox(6, 46, 2, ' '); i = vtebox(6, 46, 2, ' ', "s", si, ""); vtmode(0); if(i < -1 || si[0] < 32 || si[0] > 126) { dis_error("command aborted"); return; } achar[0] = si[0]; t = achar[0] - 32; /* * save sixel string */ for(i = 0; i < 8; i++) save[i] = cfont[t][i]; save[8] = '/'; for(i = 8; i < 16; i++) save[i + 1] = cfont[t][i]; save[17] = '\0'; /* * read sixel string into ccells and mark dots on grid */ for(i = 0; i < 8; i++) { x = cfont[t][i] - 63; for(k = 0; k < 6; k++) { mask = 1 << k; if(x & mask) { ccells[i][k] = YES; vtscs(3); move_cur(i, k); putstr(STDERR, "aa", NULL); vtscs(0); } else ccells[i][k] = NO; } } for(i = 0; i < 8; i++) { x = cfont[t][i + 8] - 63; for(k = 6; k < 10; k++) { mask = 1 << (k - 6); if(x & mask) { ccells[i][k] = YES; vtscs(3); move_cur(i, k); putstr(STDERR, "aa", NULL); vtscs(0); } else ccells[i][k] = NO; } } /* * display the character sixel string */ c_to_s(s0); vtmova(22, 60); putstr(STDERR, s0, NULL); /* * display active character */ vtmova(6, 28); /* input/error line */ vterln(0); /* erase to end of line */ vtmova(8, 28); errfmt("the active character is %oi octal %i decimal ",\ achar[0], achar[0]); /* * display the character */ vtmode(8); vtscs(1); putstr(STDERR, achar, NULL); movr(); vtscs(0); putstr(STDERR, achar, NULL); movr(); vtscs(2); putstr(STDERR, achar, NULL); movr(); vtscs(3); putstr(STDERR, achar, NULL); vtmode(0); /* * display active character, double high and wide */ i = 5; vtscs(1); vtdouble(23, i++, 0, achar); /* current font */ vtscs(0); vtdouble(23, i++, 0, achar); /* ASCII */ vtscs(2); vtdouble(23, i++, 0, achar); /* multinational */ vtscs(3); vtdouble(23, i, 0, achar); /* DEC special */ vtscs(0); move_cur(0, 0); } /****************************************************************************/ int c_to_s(s) /* convert the cell array into sixel string */ char *s; { int x; register int i, k, mask; extern char ccells[][]; for(i = 0; i < 8; i++) { x = 0; for(k = 0; k < 6; k++) { if(ccells[i][k] == YES) { mask = 1 << k; x |= mask; } } s[i] = x + 63; } s[8] = '/'; for(i = 0; i < 8; i++) { x = 0; for(k = 6; k < 10; k++) { if(ccells[i][k] == YES) { mask = 1 << (k - 6); x |= mask; } } s[i + 9] = x + 63; } s[17] = '\0'; } /****************************************************************************/ int update() /* update active character */ { char s0[18]; int c_to_s(), vtdown(), vtmova(), move_cur(); extern char achar[]; extern int xcur, ycur; /* * convert ccell to ascii, put it out ie download */ c_to_s(s0); /* * display the character */ vtdown(achar[0], s0); /* * display the character sixel string */ vtmova(22, 60); putstr(STDERR, s0, NULL); move_cur(xcur, ycur); } /****************************************************************************/ int k_remove() /* blank out the entire character */ { register int i, k; int tx, ty; int dis_error(), update(), move_cur(); extern char achar[], ccells[][]; extern int xcur, ycur; /* * test for active character */ if(achar[0] == '\0') { dis_error("no active character is open"); return; } /* * */ tx = xcur; ty = ycur; for(i = 0; i < 8; i++) { for(k = 0; k < 10; k++) { if(ccells[i][k] == YES) { ccells[i][k] = NO; move_cur(i, k); putstr(STDERR, " ", NULL); } } } update(); move_cur(tx, ty); } /****************************************************************************/ int k_mark() { int dis_error(), update(), vtscs(); extern char achar[], ccells[][]; extern int xcur, ycur; if(achar[0] == '\0') { dis_error("no active character is open"); return; } if(ccells[xcur][ycur] == NO) { ccells[xcur][ycur] = YES; vtscs(3); putstr(STDERR, "aa", NULL); vtscs(0); } else { ccells[xcur][ycur] = NO; putstr(STDERR, " ", NULL); } update(); } /****************************************************************************/ int move_cur(x, y) /* move the cursor to the absolute x-y coordinate of a box in the grid */ int x, y; { int vtmova(); extern int xcur, ycur; xcur = x; ycur = y; vtmova(2 * ++y, 3 * ++x); } /****************************************************************************/ int k_down() /* move the cursor down */ { int dis_error(), update(), move_cur(); extern char achar[]; extern int xcur, ycur; if(achar[0] == '\0') { dis_error("no active character is open"); return; } if(ycur >= 9) return; move_cur(xcur, ++ycur); update(); } /****************************************************************************/ int k_up() /* move the cursor up */ { int dis_error(), update(), move_cur(); extern int xcur, ycur; extern char achar[]; if(achar[0] == '\0') { dis_error("no active character is open"); return; } if(ycur <= 0) return; move_cur(xcur, --ycur); update(); } /****************************************************************************/ int k_right() /* move the cursor right */ { int dis_error(), update(), move_cur(); extern int ycur, xcur; extern char achar[]; if(achar[0] == '\0') { dis_error("no active character is open"); return; } if(xcur >= 7) return; move_cur(++xcur, ycur); update(); } /****************************************************************************/ int k_left() /* move the cursor left */ { int dis_error(), update(), move_cur(); extern int ycur, xcur; extern char achar[]; if(achar[0] == '\0') { dis_error("no active character is open"); return; } if(xcur <= 0) return; move_cur(--xcur, ycur); update(); } /****************************************************************************/ int vtesc(s) /* output string s to STDERR preceded by . */ char *s; { int putstr(); putstr(STDERR, "", s, NULL); } /****************************************************************************/ int key() /* get input from one keyboard key. returns code value. */ { char c, cc[2], cget(); int code; /* * */ if((c = cget()) != ESCAPE) switch (c) { case '\177': code = DELETE; break; default: return(ERROR); } else if((c = cget()) == 'O') { /* SS3 introducer */ c = cget(); switch (c) { case 'A': /* arrows */ code = UP; break; case 'B': code = DOWN; break; case 'C': code = RIGHT; break; case 'D': code = LEFT; break; case 'P': code = PF1; break; case 'Q': code = PF2; break; case 'S': code = PF4; break; default: return(ERROR); } } else if(c == '[') { /* CSI introducer */ cc[0] = cget(); if((cc[1] = cget()) == '~') /* editing keys */ switch (cc[0]) { /* editing keys (VT200) */ case '3': code = REMOVE; break; case '4': code = SELECT; break; default: return(ERROR); } else { /* function keys */ c = cget(); if(cc[0] == '3') { switch (cc[1]) { case '1': code = F17; break; case '2': code = F18; break; case '4': code = F20; break; default: return(ERROR); } } } } return(code); } /****************************************************************************/ int dis_error(s) /* display error message */ char *s; { int vterln(), vtmova(), vtmode(); extern int ycur, xcur, err_flag; err_flag = YES; vtmova(6, 28); vterln(0); vtmode(8); putstr(STDERR, "", s, NULL); vtmode(0); move_cur(xcur, ycur); } /****************************************************************************/ int deftype(file_name, file_ext) /* if a filename string does not contain '.' (no file extension is present) a file extension (file_ext) is added. returns 0 if no change, +2 if file extension added. */ char file_name[], file_ext[]; { char *cpystr(); register int ret = 0, i; /* * add file extension */ for(i = 0; file_name[i] != '\0'; i++) { if(file_name[i] == '.') /* if '.' found, no add ext */ return(0); } file_ext[4] = '\0'; /* extension only 4 char */ cpystr(file_name, file_name, file_ext, NULL); /* add file ext */ return(2); } /****************************************************************************/ int movr() /* move cursor one column to the right */ { static char s[] = ""; /* $[1C */ putstr(STDERR, s, NULL); } /****************************************************************************/ int process() /* process the responses to keystrokes */ { int code; int move_cur(), vtmova(), vterln(), key(), leave(); int k_remove(), k_mark(), k_up(), k_down(), k_right(), k_left(); int k_ocf(), dis_error(), k_ccf(), k_ochar(), k_cchar(); extern int err_flag; FOREVER { code = key(); if(err_flag) { vtmova(6, 28); /* input/error line */ vterln(0); /* erase to end of line */ move_cur(0, 0); err_flag = NO; } if(code == DELETE) leave(NO); else if(code == PF1) k_ochar(); else if(code == PF2) k_cchar(YES); else if(code == PF4) k_cchar(NO); else if(code == F17) k_ocf(); else if(code == F18) k_ccf(YES); else if(code == F20) k_ccf(NO); else if(code == UP) k_up(); else if(code == DOWN) k_down(); else if(code == RIGHT) k_right(); else if(code == LEFT) k_left(); else if(code == SELECT) k_mark(); else if(code == REMOVE) k_remove(); else dis_error("illegal key"); } }