1c7b97aa03d9c811319fe49266bf9564955dec90
[unifont.git] / src / unihex2bmp.c
1 /*
2    unihex2bmp - program to turn a GNU Unifont hex glyph page of 256 code
3                 points into a Microsoft Bitmap (.bmp) or Wireless Bitmap file
4
5    Synopsis: unihex2bmp [-iin_file.hex] [-oout_file.bmp]
6                 [-f] [-phex_page_num] [-w]
7
8
9    Author: Paul Hardy, unifoundry <at> unifoundry.com, December 2007
10    
11    Copyright (C) 2007, 2008, 2013 Paul Hardy
12
13    LICENSE:
14
15       This program is free software: you can redistribute it and/or modify
16       it under the terms of the GNU General Public License as published by
17       the Free Software Foundation, either version 2 of the License, or
18       (at your option) any later version.
19
20       This program is distributed in the hope that it will be useful,
21       but WITHOUT ANY WARRANTY; without even the implied warranty of
22       MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
23       GNU General Public License for more details.
24
25       You should have received a copy of the GNU General Public License
26       along with this program.  If not, see <http://www.gnu.org/licenses/>.
27 */
28
29 #include <stdio.h>
30 #include <stdlib.h>
31 #include <string.h>
32
33 #define MAXBUF 256
34
35
36 /*
37    These are the GNU Unifont hex strings for '0'-'9' and 'A'-'F',
38    for encoding as bit strings in row and column headers.
39
40    Looking at the final bitmap as a grid of 32*32 bit tiles, the
41    first row contains a hexadecimal character string of the first
42    3 hex digits in a 4 digit Unicode character name; the top column
43    contains a hex character string of the 4th (low-order) hex digit
44    of the Unicode character.
45 */
46 char *hex[18]= {
47       "0030:00000000182442424242424224180000",  /* Hex digit 0 */
48       "0031:000000000818280808080808083E0000",  /* Hex digit 1 */
49       "0032:000000003C4242020C102040407E0000",  /* Hex digit 2 */
50       "0033:000000003C4242021C020242423C0000",  /* Hex digit 3 */
51       "0034:00000000040C142444447E0404040000",  /* Hex digit 4 */
52       "0035:000000007E4040407C020202423C0000",  /* Hex digit 5 */
53       "0036:000000001C2040407C424242423C0000",  /* Hex digit 6 */
54       "0037:000000007E0202040404080808080000",  /* Hex digit 7 */
55       "0038:000000003C4242423C424242423C0000",  /* Hex digit 8 */
56       "0039:000000003C4242423E02020204380000",  /* Hex digit 9 */
57       "0041:0000000018242442427E424242420000",  /* Hex digit A */
58       "0042:000000007C4242427C424242427C0000",  /* Hex digit B */
59       "0043:000000003C42424040404042423C0000",  /* Hex digit C */
60       "0044:00000000784442424242424244780000",  /* Hex digit D */
61       "0045:000000007E4040407C404040407E0000",  /* Hex digit E */
62       "0046:000000007E4040407C40404040400000",  /* Hex digit F */
63       "0055:000000004242424242424242423C0000",  /* Unicode 'U' */
64       "002B:0000000000000808087F080808000000"   /* Unicode '+' */
65    };
66 unsigned char hexbits[18][32]; /* The above digits converted into bitmap */
67
68 unsigned unipage=0;        /* Unicode page number, 0x00..0xff */
69 int flip=1;                /* transpose entire matrix as in Unicode book */
70
71
72
73 int main(int argc, char *argv[]) {
74
75    int i, j;                  /* loop variables                    */
76    unsigned k0;               /* temp Unicode char variable        */
77    unsigned swap;             /* temp variable for swapping values */
78    char inbuf[256];           /* input buffer                      */
79 // unsigned headersize=0x28;  /* size of header (fixed in Windows) */
80    unsigned filesize;         /* size of file in bytes             */
81    unsigned bitmapsize;       /* size of bitmap image in bytes     */
82    unsigned thischar;         /* the current character             */
83    unsigned char thischarbyte; /* unsigned char lowest byte of Unicode char */
84    int thischarrow;           /* row 0..15 where this character belongs  */
85    int thiscol;               /* column 0..15 where this character belongs */
86    int toppixelrow;           /* pixel row, 0..16*32-1               */
87    unsigned lastpage=0;       /* the last Unicode page read in font file */
88    int wbmp=0;                /* set to 1 if writing .wbmp format file */
89
90    unsigned char bitmap[17*32][18*4]; /* final bitmap */
91    unsigned char charbits[32][4];  /* bitmap for one character, 4 bytes/row */
92
93    char *infile="", *outfile="";  /* names of input and output files */
94    FILE *infp, *outfp;      /* file pointers of input and output files */
95
96    int init();                  /* initializes bitmap row/col labeling, &c. */
97    int hex2bit();               /* convert hex string --> bitmap */
98
99    bitmapsize = 17*32*18*4;  /* 17 rows by 18 cols, each 4 bytes */
100
101    if (argc > 1) {
102       for (i = 1; i < argc; i++) {
103          if (argv[i][0] == '-') {  /* this is an option argument */
104             switch (argv[i][1]) {
105                case 'f':  /* flip (transpose) glyphs in bitmap as in standard */
106                   flip = !flip;
107                   break;
108                case 'i':  /* name of input file */
109                   infile = &argv[i][2];
110                   break;
111                case 'o':  /* name of output file */
112                   outfile = &argv[i][2];
113                   break;
114                case 'p':  /* specify a Unicode page other than default of 0 */
115                   sscanf(&argv[i][2], "%x", &unipage); /* Get Unicode page */
116                   break;
117                case 'w':  /* write a .wbmp file instead of a .bmp file */
118                   wbmp = 1;
119                   break;
120                default:   /* if unrecognized option, print list and exit */
121                   fprintf(stderr, "\nSyntax:\n\n");
122                   fprintf(stderr, "   %s -p<Unicode_Page> ", argv[0]);
123                   fprintf(stderr, "-i<Input_File> -o<Output_File> -w\n\n");
124                   fprintf(stderr, "   -w specifies .wbmp output instead of ");
125                   fprintf(stderr, "default Windows .bmp output.\n\n");
126                   fprintf(stderr, "   -p is followed by 1 to 6 ");
127                   fprintf(stderr, "Unicode page hex digits ");
128                   fprintf(stderr, "(default is Page 0).\n\n");
129                   fprintf(stderr, "\nExample:\n\n");
130                   fprintf(stderr, "   %s -p83 -iunifont.hex -ou83.bmp\n\n\n",
131                          argv[0]);
132                   exit(1);
133             }
134          }
135       }
136    }
137    /*
138       Make sure we can open any I/O files that were specified before
139       doing anything else.
140    */
141    if (strlen(infile) > 0) {
142       if ((infp = fopen(infile, "r")) == NULL) {
143          fprintf(stderr, "Error: can't open %s for input.\n", infile);
144          exit(1);
145       }
146    }
147    else {
148       infp = stdin;
149    }
150    if (strlen(outfile) > 0) {
151       if ((outfp = fopen(outfile, "w")) == NULL) {
152          fprintf(stderr, "Error: can't open %s for output.\n", outfile);
153          exit(1);
154       }
155    }
156    else {
157       outfp = stdout;
158    }
159
160    (void)init(bitmap); /* initialize bitmap with row/column headers, etc. */
161
162    /*
163       Read in the characters in the page
164    */
165    while (lastpage <= unipage && fgets(inbuf, MAXBUF-1, infp) != NULL) {
166       sscanf(inbuf, "%x", &thischar);
167       lastpage = thischar >> 8; /* keep Unicode page to see if we can stop */
168       if (lastpage == unipage) {
169          thischarbyte = (unsigned char)(thischar & 0xff);
170          for (k0=0; inbuf[k0] != ':'; k0++);
171          k0++;
172          hex2bit(&inbuf[k0], charbits);  /* convert hex string to 32*4 bitmap */
173
174          /*
175             Now write character bitmap upside-down in page array, to match
176             .bmp file order.  In the .wbmp` and .bmp files, white is a '1'
177             bit and black is a '0' bit, so complement charbits[][].
178          */
179
180          thiscol = (thischarbyte & 0xf) + 2;  /* column number will be 1..16  */
181          thischarrow = thischarbyte >> 4;     /* charcter row number, 0..15   */
182          if (flip) {  /* swap row and column placement */
183             swap = thiscol;
184             thiscol = thischarrow;
185             thischarrow = swap;
186             thiscol += 2;       /* column index starts at 1 */
187             thischarrow -= 2;   /* row index starts at 0    */
188          }
189          toppixelrow = 32 * (thischarrow + 1) - 1; /* from bottom to top    */
190    
191          /*
192             Copy the center of charbits[][] because hex characters only
193             occupy rows 8 to 23 and column byte 2 (and for 16 bit wide
194             characters, byte 3).  The charbits[][] array was given 32 rows
195             and 4 column bytes for completeness in the beginning.
196          */
197          for (i=8; i<24; i++) {
198             bitmap[toppixelrow + i][(thiscol << 2) | 1] =
199                ~charbits[i][1] & 0xff;
200             bitmap[toppixelrow + i][(thiscol << 2) | 2] =
201                ~charbits[i][2] & 0xff;
202          }
203       }
204    }
205    /*
206       Now write the appropriate bitmap file format, either
207       Wireless Bitmap or Microsoft Windows bitmap.
208    */
209    if (wbmp) {  /* Write a Wireless Bitmap .wbmp format file */
210       /*
211          Write WBMP header
212       */
213       fprintf(outfp, "%c", 0x00); /* Type of image; always 0 (monochrome) */
214       fprintf(outfp, "%c", 0x00); /* Reserved; always 0                   */
215       fprintf(outfp, "%c%c", 0x84, 0x40); /* Width  = 576 pixels          */
216       fprintf(outfp, "%c%c", 0x84, 0x20); /* Height = 544 pixels          */
217       /*
218          Write bitmap image
219       */
220       for (toppixelrow=0; toppixelrow <= 17*32-1; toppixelrow++) {
221          for (j=0; j<18; j++) {
222             fprintf(outfp, "%c", bitmap[toppixelrow][(j<<2)    ]);
223             fprintf(outfp, "%c", bitmap[toppixelrow][(j<<2) | 1]);
224             fprintf(outfp, "%c", bitmap[toppixelrow][(j<<2) | 2]);
225             fprintf(outfp, "%c", bitmap[toppixelrow][(j<<2) | 3]);
226          }
227       }
228    }
229    else {  /* otherwise, write a Microsoft Windows .bmp format file */
230       /*
231          Write the .bmp file -- start with the header, then write the bitmap
232       */
233
234       /* 'B', 'M' appears at start of every .bmp file */
235       fprintf(outfp, "%c%c", 0x42, 0x4d);
236
237       /* Write file size in bytes */
238       filesize   = 0x3E + bitmapsize;
239       fprintf(outfp, "%c", (unsigned char)((filesize        ) & 0xff));
240       fprintf(outfp, "%c", (unsigned char)((filesize >> 0x08) & 0xff));
241       fprintf(outfp, "%c", (unsigned char)((filesize >> 0x10) & 0xff));
242       fprintf(outfp, "%c", (unsigned char)((filesize >> 0x18) & 0xff));
243
244       /* Reserved - 0's */
245       fprintf(outfp, "%c%c%c%c", 0x00, 0x00, 0x00, 0x00);
246
247       /* Offset from start of file to bitmap data */
248       fprintf(outfp, "%c%c%c%c", 0x3E, 0x00, 0x00, 0x00);
249
250       /* Length of bitmap info header */
251       fprintf(outfp, "%c%c%c%c", 0x28, 0x00, 0x00, 0x00);
252
253       /* Width of bitmap in pixels */
254       fprintf(outfp, "%c%c%c%c", 0x40, 0x02, 0x00, 0x00);
255
256       /* Height of bitmap in pixels */
257       fprintf(outfp, "%c%c%c%c", 0x20, 0x02, 0x00, 0x00);
258
259       /* Planes in bitmap (fixed at 1) */
260       fprintf(outfp, "%c%c", 0x01, 0x00);
261
262       /* bits per pixel (1 = monochrome) */
263       fprintf(outfp, "%c%c", 0x01, 0x00);
264
265       /* Compression (0 = none) */
266       fprintf(outfp, "%c%c%c%c", 0x00, 0x00, 0x00, 0x00);
267
268       /* Size of bitmap data in bytes */
269       fprintf(outfp, "%c", (unsigned char)((bitmapsize        ) & 0xff));
270       fprintf(outfp, "%c", (unsigned char)((bitmapsize >> 0x08) & 0xff));
271       fprintf(outfp, "%c", (unsigned char)((bitmapsize >> 0x10) & 0xff));
272       fprintf(outfp, "%c", (unsigned char)((bitmapsize >> 0x18) & 0xff));
273
274       /* Horizontal resolution in pixels per meter */
275       fprintf(outfp, "%c%c%c%c", 0xC4, 0x0E, 0x00, 0x00);
276
277       /* Vertical resolution in pixels per meter */
278       fprintf(outfp, "%c%c%c%c", 0xC4, 0x0E, 0x00, 0x00);
279
280       /* Number of colors used */
281       fprintf(outfp, "%c%c%c%c", 0x02, 0x00, 0x00, 0x00);
282
283       /* Number of important colors */
284       fprintf(outfp, "%c%c%c%c", 0x02, 0x00, 0x00, 0x00);
285
286       /* The color black: B=0x00, G=0x00, R=0x00, Filler=0xFF */
287       fprintf(outfp, "%c%c%c%c", 0x00, 0x00, 0x00, 0x00);
288
289       /* The color white: B=0xFF, G=0xFF, R=0xFF, Filler=0xFF */
290       fprintf(outfp, "%c%c%c%c", 0xFF, 0xFF, 0xFF, 0x00);
291
292       /*
293          Now write the raw data bits.  Data is written from the lower
294          left-hand corner of the image to the upper right-hand corner
295          of the image.
296       */
297       for (toppixelrow=17*32-1; toppixelrow >= 0; toppixelrow--) {
298          for (j=0; j<18; j++) {
299             fprintf(outfp, "%c", bitmap[toppixelrow][(j<<2)    ]);
300             fprintf(outfp, "%c", bitmap[toppixelrow][(j<<2) | 1]);
301             fprintf(outfp, "%c", bitmap[toppixelrow][(j<<2) | 2]);
302
303             fprintf(outfp, "%c", bitmap[toppixelrow][(j<<2) | 3]);
304          }
305       }
306    }
307    exit(0);
308 }
309
310 /*
311    Convert the portion of a hex string after the ':' into a character bitmap.
312
313    If string is >= 128 characters, it will fill all 4 bytes per row.
314    If string is >= 64 characters and < 128, it will fill 2 bytes per row.
315    Otherwise, it will fill 1 byte per row.
316 */
317 int hex2bit(char *instring, unsigned char character[32][4]) {
318
319    int i;  /* current row in bitmap character */
320    int j;  /* current character in input string */
321    int k;  /* current byte in bitmap character */
322    int width;  /* number of output bytes to fill: 1, 2, or 4 */
323
324    for (i=0; i<32; i++)  /* erase previous character */
325       character[i][0] = character[i][1] = character[i][2] = character[i][3] = 0; 
326    j=0;  /* current location is at beginning of instring */
327
328    // width = (strlen(instring) - 1) >> 4; /* 16 hex digits per 8 bytes */
329
330    if (strlen(instring) <= 34)  /* 32 + possible '\r', '\n' */
331       width = 0;
332    else if (strlen(instring) <= 66)  /* 64 + possible '\r', '\n' */
333       width = 1;
334    else
335       width = 4;
336
337    k = (width > 1) ? 0 : 1;  /* if width < 3, start at index 1 else at 0 */
338
339    for (i=8; i<24; i++) {  /* 16 rows per input character, rows 8..23 */
340       sscanf(&instring[j], "%2hhx", &character[i][k]);
341       j += 2;
342       if (width > 0) { /* add next pair of hex digits to this row */
343          sscanf(&instring[j], "%2hhx", &character[i][k+1]);
344          j += 2;
345       }
346       if (width > 1) { /* add 2 next pairs of hex digits to this row */
347          sscanf(&instring[j], "%2hhx", &character[i][k+2]);
348          j += 2;
349          sscanf(&instring[j], "%2hhx", &character[i][k+3]);
350          j += 2;
351       }
352    }
353
354    return(0);
355 }
356
357 int init(unsigned char bitmap[17*32][18*4]) {
358    int i, j;
359    unsigned char charbits[32][4];  /* bitmap for one character, 4 bytes/row */
360    unsigned toppixelrow;
361    unsigned thiscol;
362    unsigned char pnybble0, pnybble1, pnybble2, pnybble3;
363
364    for (i=0; i<18; i++) { /* bitmaps for '0'..'9', 'A'-'F', 'u', '+' */
365
366       hex2bit(&hex[i][5], charbits);  /* convert hex string to 32*4 bitmap */
367
368       for (j=0; j<32; j++) hexbits[i][j] = ~charbits[j][1];
369    }
370
371    /*
372       Initialize bitmap to all white.
373    */
374    for (toppixelrow=0; toppixelrow < 17*32; toppixelrow++) {
375       for (thiscol=0; thiscol<18; thiscol++) {
376          bitmap[toppixelrow][(thiscol << 2)    ] = 0xff;
377          bitmap[toppixelrow][(thiscol << 2) | 1] = 0xff;
378          bitmap[toppixelrow][(thiscol << 2) | 2] = 0xff;
379          bitmap[toppixelrow][(thiscol << 2) | 3] = 0xff;
380       }
381    }
382    /*
383       Write the "u+nnnn" table header in the upper left-hand corner,
384       where nnnn is the upper 16 bits of a 32-bit Unicode assignment.
385    */
386    pnybble3 = (unipage >> 20);
387    pnybble2 = (unipage >> 16) & 0xf;
388    pnybble1 = (unipage >> 12) & 0xf;
389    pnybble0 = (unipage >>  8) & 0xf;
390    for (i=0; i<32; i++) {
391       bitmap[i][1] = hexbits[16][i];  /* copy 'u' */
392       bitmap[i][2] = hexbits[17][i];  /* copy '+' */
393       bitmap[i][3] = hexbits[pnybble3][i];
394       bitmap[i][4] = hexbits[pnybble2][i];
395       bitmap[i][5] = hexbits[pnybble1][i];
396       bitmap[i][6] = hexbits[pnybble0][i];
397    }
398    /*
399       Write low-order 2 bytes of Unicode number assignments, as hex labels
400    */
401    pnybble3 = (unipage >> 4) & 0xf;  /* Highest-order hex digit */
402    pnybble2 = (unipage     ) & 0xf;  /* Next highest-order hex digit */
403    /*
404       Write the column headers in bitmap[][] (row headers if flipped)
405    */
406    toppixelrow = 32 * 17 - 1; /* maximum pixel row number */
407    /*
408       Label the column headers. The hexbits[][] bytes are split across two
409       bitmap[][] entries to center a the hex digits in a column of 4 bytes.
410       OR highest byte with 0xf0 and lowest byte with 0x0f to make outer
411       nybbles white (0=black, 1-white).
412    */
413    for (i=0; i<16; i++) {
414       for (j=0; j<32; j++) {
415          if (flip) {  /* transpose matrix */
416             bitmap[j][((i+2) << 2) | 0]  = (hexbits[pnybble3][j] >> 4) | 0xf0;
417             bitmap[j][((i+2) << 2) | 1]  = (hexbits[pnybble3][j] << 4) |
418                                            (hexbits[pnybble2][j] >> 4);
419             bitmap[j][((i+2) << 2) | 2]  = (hexbits[pnybble2][j] << 4) |
420                                            (hexbits[i][j] >> 4);
421             bitmap[j][((i+2) << 2) | 3]  = (hexbits[i][j] << 4) | 0x0f;
422          }
423          else {
424             bitmap[j][((i+2) << 2) | 1] = (hexbits[i][j] >> 4) | 0xf0;
425             bitmap[j][((i+2) << 2) | 2] = (hexbits[i][j] << 4) | 0x0f;
426          }
427       }
428    }
429    /*
430       Now use the single hex digit column graphics to label the row headers.
431    */
432    for (i=0; i<16; i++) {
433       toppixelrow = 32 * (i + 1) - 1; /* from bottom to top    */
434
435       for (j=0; j<32; j++) {
436          if (!flip) {  /* if not transposing matrix */
437             bitmap[toppixelrow + j][4] = hexbits[pnybble3][j];
438             bitmap[toppixelrow + j][5] = hexbits[pnybble2][j];
439          }
440          bitmap[toppixelrow + j][6] = hexbits[i][j];
441       }
442    }
443    /*
444       Now draw grid lines in bitmap, around characters we just copied.
445    */
446    /* draw vertical lines 2 pixels wide */
447    for (i=1*32; i<17*32; i++) {
448       if ((i & 0x1f) == 7)
449          i++;
450       else if ((i & 0x1f) == 14)
451          i += 2;
452       else if ((i & 0x1f) == 22)
453          i++;
454       for (j=1; j<18; j++) {
455          bitmap[i][(j << 2) | 3] &= 0xfe;
456       }
457    }
458    /* draw horizontal lines 2 pixels tall */
459    for (i=1*32-1; i<18*32-1; i+=32) {
460       for (j=2; j<18; j++) {
461          bitmap[i][(j << 2)    ] = 0x00;
462          bitmap[i][(j << 2) | 1] = 0x81;
463          bitmap[i][(j << 2) | 2] = 0x81;
464          bitmap[i][(j << 2) | 3] = 0x00;
465       }
466    }
467    /* fill in top left corner pixel of grid */
468    bitmap[31][7] = 0xfe;
469
470    return(0);
471 }