a18e14c37835987d301d4b7a84e7bbef1c8ede4b
[unifont.git] / src / unifontpic.c
1 /*
2    unifontpic.c - see the "Big Picture": the entire Unifont in one BMP bitmap.
3
4    Author: Paul Hardy, 2013
5
6    Copyright (C) 2013 Paul Hardy
7
8    LICENSE:
9
10       This program is free software: you can redistribute it and/or modify
11       it under the terms of the GNU General Public License as published by
12       the Free Software Foundation, either version 2 of the License, or
13       (at your option) any later version.
14
15       This program is distributed in the hope that it will be useful,
16       but WITHOUT ANY WARRANTY; without even the implied warranty of
17       MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18       GNU General Public License for more details.
19
20       You should have received a copy of the GNU General Public License
21       along with this program.  If not, see <http://www.gnu.org/licenses/>.
22 */
23
24 #include <stdio.h>
25 #include <stdlib.h>
26 #include <string.h>
27
28 #define MAXSTRING 256
29
30 #define HEADER_STRING "GNU Unifont 6.3" /* to be printed as chart title */
31
32
33 int main(int argc, char **argv) {
34
35    /* long and dpi are set from command-line options */
36    int wide=1; /* =1 for a 256x256 grid, =0 for a 16x4096 grid */
37    int dpi=96; /* change for 256x256 grid to fit paper if desired */
38    int tinynum=0; /* whether to use tiny labels for 256x256 grid */
39
40    int i; /* loop variable */
41
42    int bitarray[0x10000][16]; /* 16 pixel rows for each of 65,536 glyphs */
43
44    void gethex();
45    void genlongbmp();
46    void genwidebmp();
47
48    memset((void *)bitarray, 0, 0x10000 * 16 * sizeof(int));
49
50    gethex(bitarray); /* read .hex input file and fill bitarray with glyph data */
51
52    if (argc > 1) {
53       for (i = 1; i < argc; i++) {
54          if (strncmp(argv[i],"-l",2) == 0) { /* long display */
55            wide = 0;
56          }
57          else if (strncmp(argv[i],"-d",2) == 0) {
58             dpi = atoi(&argv[i][2]); /* dots/inch specified on command line */
59          }
60          else if (strncmp(argv[i],"-t",2) == 0) {
61             tinynum = 1;
62          }
63       }
64    }
65
66    if (wide) {
67       genwidebmp(bitarray, dpi, tinynum); /* write bitarray glyph data to BMP file */
68    }
69    else {
70       genlongbmp(bitarray, dpi, tinynum);
71    }
72
73    exit(EXIT_SUCCESS);
74 }
75
76
77 void output4(int thisword) {
78
79    putchar( thisword        & 0xFF);
80    putchar((thisword >>  8) & 0xFF);
81    putchar((thisword >> 16) & 0xFF);
82    putchar((thisword >> 24) & 0xFF);
83
84    return;
85 }
86
87
88 void output2(int thisword) {
89
90    putchar( thisword       & 0xFF);
91    putchar((thisword >> 8) & 0xFF);
92
93    return;
94 }
95
96
97 /*
98    gethex() reads a Unifont .hex-format input file from stdin.
99 */
100 void gethex(int bitarray[0x10000][16]) {
101
102    char instring[MAXSTRING]; /* input buffer for a code point */
103    char *bitstring;          /* pointer into instring for glyph bitmap */
104
105    int i;
106    int codept; /* the Unicode code point of the current glyph */
107
108    /*
109       Read each input line and place its glyph into the bit array.
110    */
111    while (fgets(instring, MAXSTRING, stdin) != NULL) {
112       sscanf(instring, "%X", &codept);
113       for (i = 0; (instring[i] != ':') && (i < 9); i++); /* find the colon separator */
114       i++; /* position past it */
115       bitstring = &instring[i];
116       /*
117          If this glyph is only 8 pixels wide, expand so right half of glyph is 0s.
118       */
119       if (strlen(bitstring) <= 33) { /* count terminating newline */
120          for (i = 60; i >= 0; i -= 4) {
121             bitstring[i + 3] = '0';
122             bitstring[i + 2] = '0';
123             bitstring[i + 1] = bitstring[(i >> 1) + 1];
124             bitstring[i    ] = bitstring[ i >> 1     ];
125          }
126       }
127       bitstring[64] = '\0'; /* truncate string, overwriting newline */
128
129       for (i = 0; i < 16; i++) {
130          sscanf(bitstring, "%4X", &bitarray[codept][i]);
131          bitstring += 4;
132       }
133    }
134
135    return;
136 }
137
138
139 /*
140    genlongbmp() generates the BMP output file from a bitmap parameter.
141    This is a long bitmap, 16 glyphs wide by 4,096 glyphs tall.
142 */
143 void genlongbmp(int bitarray[0x10000][16], int dpi, int tinynum) {
144
145    char header_string[17];
146    int header[16][16]; /* header row, for chart title */
147    int hdrlen;         /* length of HEADER_STRING */
148    int startcol;       /* column to start printing header, for centering */
149
150    unsigned leftcol[0x1000][16]; /* code point legend on left side of chart */
151    int d1, d2, d3, d4;          /* digits for filling leftcol[][] legend   */
152    int codept;                  /* current starting code point for legend  */
153    int thisrow;                 /* glyph row currently being rendered */
154
155    unsigned toprow[16][16];     /* code point legend on top of chart */
156
157    /*
158       hexdigit contains 4x5 pixel arrays of tiny digits for legend.
159       See unihexgen.c for more detailed description in comments.
160    */
161    char hexdigit[16][5] = {
162       {0x6,0x9,0x9,0x9,0x6},  /* 0x0 */
163       {0x2,0x6,0x2,0x2,0x7},  /* 0x1 */
164       {0xF,0x1,0xF,0x8,0xF},  /* 0x2 */
165       {0xE,0x1,0x7,0x1,0xE},  /* 0x3 */
166       {0x9,0x9,0xF,0x1,0x1},  /* 0x4 */
167       {0xF,0x8,0xF,0x1,0xF},  /* 0x5 */
168       {0x6,0x8,0xE,0x9,0x6},  /* 0x6 */
169       {0xF,0x1,0x2,0x4,0x4},  /* 0x7 */
170       {0x6,0x9,0x6,0x9,0x6},  /* 0x8 */
171       {0x6,0x9,0x7,0x1,0x6},  /* 0x9 */
172       {0xF,0x9,0xF,0x9,0x9},  /* 0xA */
173       {0xE,0x9,0xE,0x9,0xE},  /* 0xB */
174       {0x7,0x8,0x8,0x8,0x7},  /* 0xC */
175       {0xE,0x9,0x9,0x9,0xE},  /* 0xD */
176       {0xF,0x8,0xE,0x8,0xF},  /* 0xE */
177       {0xF,0x8,0xE,0x8,0x8}   /* 0xF */
178    };
179    int digitrow;  /* row we're in (0..4) for the above hexdigit digits */
180
181    /*
182       DataOffset = BMP Header bytes + InfoHeader bytes + ColorTable bytes.
183    */
184    int DataOffset = 14 + 40 + 8; /* fixed size for monochrome BMP */
185    int ImageSize;
186    int FileSize;
187    int Width, Height; /* bitmap image width and height in pixels */
188    int ppm;     /* integer pixels per meter */
189
190    int i, j, k;
191
192    unsigned bytesout;
193
194    void output4(int), output2(int);
195
196    /*
197       Image width and height, in pixels.
198
199          N.B.: Width must be an even multiple of 32 pixels, or 4 bytes.
200    */
201    Width  =   18 * 16;  /* (2 legend +   16 glyphs) * 16 pixels/glyph */
202    Height = 4099 * 16;  /* (1 header + 4096 glyphs) * 16 rows/glyph   */
203
204    ImageSize = Height * (Width / 8); /* in bytes, calculated from pixels */
205
206    FileSize = DataOffset + ImageSize;
207
208    /* convert dots/inch to pixels/meter */
209    if (dpi == 0) dpi = 96;
210    ppm = (int)((double)dpi * 100.0 / 2.54 + 0.5);
211
212    /*
213       Generate the BMP Header
214    */
215    putchar('B');
216    putchar('M');
217
218    /*
219       Calculate file size:
220
221          BMP Header + InfoHeader + Color Table + Raster Data
222    */
223    output4(FileSize);  /* FileSize */
224    output4(0x0000); /* reserved */
225
226    /* Calculate DataOffset */
227    output4(DataOffset);
228
229    /*
230       InfoHeader
231    */
232    output4(40);         /* Size of InfoHeader                       */
233    output4(Width);      /* Width of bitmap in pixels                */
234    output4(Height);     /* Height of bitmap in pixels               */
235    output2(1);          /* Planes (1 plane)                         */
236    output2(1);          /* BitCount (1 = monochrome)                */
237    output4(0);          /* Compression (0 = none)                   */
238    output4(ImageSize);  /* ImageSize, in bytes                      */
239    output4(ppm);        /* XpixelsPerM (96 dpi = 3780 pixels/meter) */
240    output4(ppm);        /* YpixelsPerM (96 dpi = 3780 pixels/meter) */
241    output4(2);          /* ColorsUsed (= 2)                         */
242    output4(2);          /* ColorsImportant (= 2)                    */
243    output4(0x00000000); /* black (reserved, B, G, R)                */
244    output4(0x00FFFFFF); /* white (reserved, B, G, R)                */
245
246    /*
247       Create header row bits.
248    */
249    memset((void *)header, 0, 16 * 16 * sizeof(int)); /* fill with white */
250    memset((void *)header_string, ' ', 16 * sizeof(char)); /* 16 spaces */
251    header_string[16] = '\0';  /* null-terminated */
252
253    hdrlen = strlen(HEADER_STRING);
254    if (hdrlen > 16) hdrlen = 16;        /* only 16 columns to print header */
255    startcol = 8 - ((hdrlen + 1) >> 1);                 /* to center header */
256    strncpy(&header_string[startcol], HEADER_STRING, hdrlen); /* center up to 16 chars */
257
258    /* Copy each letter's bitmap from the bitarray[][] we constructed. */
259    for (j = 0; j < 16; j++) {
260       for (i = 0; i < 16; i++) {
261          header[i][j] = bitarray[(unsigned)header_string[j]][i];
262       }
263    }
264
265    /*
266       Create the left column legend.
267    */
268    memset((void *)leftcol, 0, 4096 * 16 * sizeof(unsigned));
269
270    for (codept = 0x0000; codept < 0x10000; codept += 0x10) {
271       d1 = (codept >> 12) & 0xF; /* most significant hex digit */
272       d2 = (codept >>  8) & 0xF;
273       d3 = (codept >>  4) & 0xF;
274 //    d4 =  codept        & 0xF; /* least significant hex digit */
275
276       thisrow = codept >> 4; /* rows of 16 glyphs */
277
278       /* fill in first and second digits */
279       for (digitrow = 0; digitrow < 5; digitrow++) {
280          leftcol[thisrow][2 + digitrow] =
281             (hexdigit[d1][digitrow] << 10) |
282             (hexdigit[d2][digitrow] <<  4);
283       }
284
285       /* fill in third digit */
286       for (digitrow = 0; digitrow < 5; digitrow++) {
287          leftcol[thisrow][9 + digitrow] = hexdigit[d3][digitrow] << 10;
288       }
289       leftcol[thisrow][9 + 4] |= 0xF << 4; /* underscore as 4th digit */
290
291       for (i = 0; i < 15; i ++) {
292          leftcol[thisrow][i] |= 0x00000002;      /* right border */
293       }
294
295       leftcol[thisrow][15] = 0x0000FFFE;        /* bottom border */
296
297       if (d3 == 0xF) {                     /* 256-point boundary */
298          leftcol[thisrow][15] |= 0x00FF0000;  /* longer tic mark */
299       }
300
301       if ((thisrow % 0x40) == 0x3F) {    /* 1024-point boundary */
302          leftcol[thisrow][15] |= 0xFFFF0000; /* longest tic mark */
303       }
304    }
305
306    /*
307       Create the top row legend.
308    */
309    memset((void *)toprow, 0, 16 * 16 * sizeof(unsigned));
310
311    for (codept = 0x0; codept <= 0xF; codept++) {
312       d1 = (codept >> 12) & 0xF; /* most significant hex digit */
313       d2 = (codept >>  8) & 0xF;
314       d3 = (codept >>  4) & 0xF;
315       d4 =  codept        & 0xF; /* least significant hex digit */
316
317       /* fill in last digit */
318       for (digitrow = 0; digitrow < 5; digitrow++) {
319          toprow[6 + digitrow][codept] = hexdigit[d4][digitrow] << 6;
320       }
321    }
322
323    for (j = 0; j < 16; j++) {
324       /* force bottom pixel row to be white, for separation from glyphs */
325       toprow[15][j] = 0x0000;
326    }
327
328    /* 1 pixel row with left-hand legend line */
329    for (j = 0; j < 16; j++) {
330       toprow[14][j] |= 0xFFFF;
331    }
332
333    /* 14 rows with line on left to fill out this character row */
334    for (i = 13; i >= 0; i--) {
335       for (j = 0; j < 16; j++) {
336          toprow[i][j] |= 0x0001;
337       }
338    }
339
340    /*
341       Now write the raster image.
342
343       XOR each byte with 0xFF because black = 0, white = 1 in BMP.
344    */
345
346    /* Write the glyphs, bottom-up, left-to-right, in rows of 16 (i.e., 0x10) */
347    for (i = 0xFFF0; i >= 0; i -= 0x10) {
348       thisrow = i >> 4; /* 16 glyphs per row */
349       for (j = 15; j >= 0; j--) {
350          /* left-hand legend */
351          putchar((~leftcol[thisrow][j] >> 24) & 0xFF);
352          putchar((~leftcol[thisrow][j] >> 16) & 0xFF);
353          putchar((~leftcol[thisrow][j] >>  8) & 0xFF);
354          putchar( ~leftcol[thisrow][j]        & 0xFF);
355          /* Unifont glyph */
356          for (k = 0; k < 16; k++) {
357             bytesout = ~bitarray[i+k][j] & 0xFFFF;
358             putchar((bytesout >> 8) & 0xFF);
359             putchar( bytesout       & 0xFF);
360          }
361       }
362    }
363
364    /*
365       Write the top legend.
366    */
367    /* i == 15: bottom pixel row of header is output here */
368    /* left-hand legend: solid black line except for right-most pixel */
369    putchar(0x00);
370    putchar(0x00);
371    putchar(0x00);
372    putchar(0x01);
373    for (j = 0; j < 16; j++) {
374       putchar((~toprow[15][j] >> 8) & 0xFF);
375       putchar( ~toprow[15][j]       & 0xFF);
376    }
377
378    putchar(0xFF);
379    putchar(0xFF);
380    putchar(0xFF);
381    putchar(0xFC);
382    for (j = 0; j < 16; j++) {
383       putchar((~toprow[14][j] >> 8) & 0xFF);
384       putchar( ~toprow[14][j]       & 0xFF);
385    }
386
387    for (i = 13; i >= 0; i--) {
388       putchar(0xFF);
389       putchar(0xFF);
390       putchar(0xFF);
391       putchar(0xFD);
392       for (j = 0; j < 16; j++) {
393          putchar((~toprow[i][j] >> 8) & 0xFF);
394          putchar( ~toprow[i][j]       & 0xFF);
395       }
396    }
397
398    /*
399       Write the header.
400    */
401
402    /* 7 completely white rows */
403    for (i = 7; i >= 0; i--) {
404       for (j = 0; j < 18; j++) {
405          putchar(0xFF);
406          putchar(0xFF);
407       }
408    }
409
410    for (i = 15; i >= 0; i--) {
411       /* left-hand legend */
412       putchar(0xFF);
413       putchar(0xFF);
414       putchar(0xFF);
415       putchar(0xFF);
416       /* header glyph */
417       for (j = 0; j < 16; j++) {
418          bytesout = ~header[i][j] & 0xFFFF;
419          putchar((bytesout >> 8) & 0xFF);
420          putchar( bytesout       & 0xFF);
421       }
422    }
423
424    /* 8 completely white rows at very top */
425    for (i = 7; i >= 0; i--) {
426       for (j = 0; j < 18; j++) {
427       putchar(0xFF);
428       putchar(0xFF);
429       }
430    }
431
432    return;
433 }
434
435
436
437 /*
438    genwidebmp() generates the BMP output file from a bitmap parameter.
439    This is a wide bitmap, 256 glyphs wide by 256 glyphs tall.
440 */
441 void genwidebmp(int bitarray[0x10000][16], int dpi, int tinynum) {
442
443    char header_string[257];
444    int header[16][256]; /* header row, for chart title */
445    int hdrlen;         /* length of HEADER_STRING */
446    int startcol;       /* column to start printing header, for centering */
447
448    unsigned leftcol[0x100][16]; /* code point legend on left side of chart */
449    int d1, d2, d3, d4;          /* digits for filling leftcol[][] legend   */
450    int codept;                  /* current starting code point for legend  */
451    int thisrow;                 /* glyph row currently being rendered */
452
453    unsigned toprow[32][256];     /* code point legend on top of chart */
454
455    /*
456       hexdigit contains 4x5 pixel arrays of tiny digits for legend.
457       See unihexgen.c for more detailed description in comments.
458    */
459    char hexdigit[16][5] = {
460       {0x6,0x9,0x9,0x9,0x6},  /* 0x0 */
461       {0x2,0x6,0x2,0x2,0x7},  /* 0x1 */
462       {0xF,0x1,0xF,0x8,0xF},  /* 0x2 */
463       {0xE,0x1,0x7,0x1,0xE},  /* 0x3 */
464       {0x9,0x9,0xF,0x1,0x1},  /* 0x4 */
465       {0xF,0x8,0xF,0x1,0xF},  /* 0x5 */
466       {0x8,0x8,0xF,0x9,0xF},  /* 0x6 */
467       {0xF,0x1,0x2,0x4,0x4},  /* 0x7 */
468       {0x6,0x9,0x6,0x9,0x6},  /* 0x8 */
469       {0xF,0x9,0xF,0x1,0x1},  /* 0x9 */
470       {0xF,0x9,0xF,0x9,0x9},  /* 0xA */
471       {0xE,0x9,0xE,0x9,0xE},  /* 0xB */
472       {0x7,0x8,0x8,0x8,0x7},  /* 0xC */
473       {0xE,0x9,0x9,0x9,0xE},  /* 0xD */
474       {0xF,0x8,0xE,0x8,0xF},  /* 0xE */
475       {0xF,0x8,0xE,0x8,0x8}   /* 0xF */
476    };
477    int digitrow;  /* row we're in (0..4) for the above hexdigit digits */
478    int hexalpha1, hexalpha2; /* to convert hex digits to ASCII */
479
480    /*
481       DataOffset = BMP Header bytes + InfoHeader bytes + ColorTable bytes.
482    */
483    int DataOffset = 14 + 40 + 8; /* fixed size for monochrome BMP */
484    int ImageSize;
485    int FileSize;
486    int Width, Height; /* bitmap image width and height in pixels */
487    int ppm;     /* integer pixels per meter */
488
489    int i, j, k;
490
491    unsigned bytesout;
492
493    void output4(int), output2(int);
494
495    /*
496       Image width and height, in pixels.
497
498          N.B.: Width must be an even multiple of 32 pixels, or 4 bytes.
499    */
500    Width  = 258 * 16;  /* (           2 legend + 256 glyphs) * 16 pixels/glyph */
501    Height = 260 * 16;  /* (2 header + 2 legend + 256 glyphs) * 16 rows/glyph   */
502
503    ImageSize = Height * (Width / 8); /* in bytes, calculated from pixels */
504
505    FileSize = DataOffset + ImageSize;
506
507    /* convert dots/inch to pixels/meter */
508    if (dpi == 0) dpi = 96;
509    ppm = (int)((double)dpi * 100.0 / 2.54 + 0.5);
510
511    /*
512       Generate the BMP Header
513    */
514    putchar('B');
515    putchar('M');
516    /*
517       Calculate file size:
518
519          BMP Header + InfoHeader + Color Table + Raster Data
520    */
521    output4(FileSize);  /* FileSize */
522    output4(0x0000); /* reserved */
523    /* Calculate DataOffset */
524    output4(DataOffset);
525
526    /*
527       InfoHeader
528    */
529    output4(40);         /* Size of InfoHeader                       */
530    output4(Width);      /* Width of bitmap in pixels                */
531    output4(Height);     /* Height of bitmap in pixels               */
532    output2(1);          /* Planes (1 plane)                         */
533    output2(1);          /* BitCount (1 = monochrome)                */
534    output4(0);          /* Compression (0 = none)                   */
535    output4(ImageSize);  /* ImageSize, in bytes                      */
536    output4(ppm);        /* XpixelsPerM (96 dpi = 3780 pixels/meter) */
537    output4(ppm);        /* YpixelsPerM (96 dpi = 3780 pixels/meter) */
538    output4(2);          /* ColorsUsed (= 2)                         */
539    output4(2);          /* ColorsImportant (= 2)                    */
540    output4(0x00000000); /* black (reserved, B, G, R)                */
541    output4(0x00FFFFFF); /* white (reserved, B, G, R)                */
542
543    /*
544       Create header row bits.
545    */
546    memset((void *)header, 0, 256 * 16 * sizeof(int)); /* fill with white */
547    memset((void *)header_string, ' ', 256 * sizeof(char)); /* 256 spaces */
548    header_string[256] = '\0';  /* null-terminated */
549
550    hdrlen = strlen(HEADER_STRING);
551    if (hdrlen > 256) hdrlen = 256;        /* only 256 columns to print header */
552    startcol = 128 - ((hdrlen + 1) >> 1);                 /* to center header */
553    strncpy(&header_string[startcol], HEADER_STRING, hdrlen); /* center up to 16 chars */
554
555    /* Copy each letter's bitmap from the bitarray[][] we constructed. */
556    for (j = 0; j < 256; j++) {
557       for (i = 0; i < 16; i++) {
558          header[i][j] = bitarray[(unsigned)header_string[j]][i];
559       }
560    }
561
562    /*
563       Create the left column legend.
564    */
565    memset((void *)leftcol, 0, 256 * 16 * sizeof(unsigned));
566
567    for (codept = 0x0000; codept < 0x10000; codept += 0x100) {
568       d1 = (codept >> 12) & 0xF; /* most significant hex digit */
569       d2 = (codept >>  8) & 0xF;
570   //  d3 = (codept >>  4) & 0xF;
571   //  d4 =  codept        & 0xF; /* least significant hex digit */
572
573       thisrow = codept >> 8; /* rows of 256 glyphs */
574
575       /* fill in first and second digits */
576
577       if (tinynum) { /* use 4x5 pixel glyphs */
578          for (digitrow = 0; digitrow < 5; digitrow++) {
579             leftcol[thisrow][6 + digitrow] =
580                (hexdigit[d1][digitrow] << 10) |
581                (hexdigit[d2][digitrow] <<  4);
582          }
583       }
584       else { /* bigger numbers -- use glyphs from Unifont itself */
585          /* convert hexadecimal digits to ASCII equivalent */
586          hexalpha1 = d1 < 0xA ? '0' + d1 : 'A' + d1 - 0xA;
587          hexalpha2 = d2 < 0xA ? '0' + d2 : 'A' + d2 - 0xA;
588
589          for (i = 0 ; i < 16; i++) {
590             leftcol[thisrow][i] =
591                (bitarray[hexalpha1][i] << 2) |
592                (bitarray[hexalpha2][i] >> 6);
593          }
594       }
595
596       for (i = 0; i < 15; i ++) {
597          leftcol[thisrow][i] |= 0x00000002;      /* right border */
598       }
599
600       leftcol[thisrow][15] = 0x0000FFFE;        /* bottom border */
601
602       if (d2 == 0xF) {                     /* 4096-point boundary */
603          leftcol[thisrow][15] |= 0x00FF0000;  /* longer tic mark */
604       }
605
606       if ((thisrow % 0x40) == 0x3F) {    /* 16,384-point boundary */
607          leftcol[thisrow][15] |= 0xFFFF0000; /* longest tic mark */
608       }
609    }
610
611    /*
612       Create the top row legend.
613    */
614    memset((void *)toprow, 0, 32 * 256 * sizeof(unsigned));
615
616    for (codept = 0x00; codept <= 0xFF; codept++) {
617 //    d1 = (codept >> 12) & 0xF; /* most significant hex digit */
618 //    d2 = (codept >>  8) & 0xF;
619       d3 = (codept >>  4) & 0xF;
620       d4 =  codept        & 0xF; /* least significant hex digit */
621
622       if (tinynum) {
623          for (digitrow = 0; digitrow < 5; digitrow++) {
624             toprow[16 + 6 + digitrow][codept] =
625                (hexdigit[d3][digitrow] << 10) |
626                (hexdigit[d4][digitrow] <<  4);
627          }
628       }
629       else {
630          /* convert hexadecimal digits to ASCII equivalent */
631          hexalpha1 = d3 < 0xA ? '0' + d3 : 'A' + d3 - 0xA;
632          hexalpha2 = d4 < 0xA ? '0' + d4 : 'A' + d4 - 0xA;
633          for (i = 0 ; i < 16; i++) {
634             toprow[14 + i][codept] =
635                (bitarray[hexalpha1][i]     ) |
636                (bitarray[hexalpha2][i] >> 7);
637          }
638       }
639    }
640
641    for (j = 0; j < 256; j++) {
642       /* force bottom pixel row to be white, for separation from glyphs */
643       toprow[16 + 15][j] = 0x0000;
644    }
645
646    /* 1 pixel row with left-hand legend line */
647    for (j = 0; j < 256; j++) {
648       toprow[16 + 14][j] |= 0xFFFF;
649    }
650
651    /* 14 rows with line on left to fill out this character row */
652    for (i = 13; i >= 0; i--) {
653       for (j = 0; j < 256; j++) {
654          toprow[16 + i][j] |= 0x0001;
655       }
656    }
657
658    /* Form the longer tic marks in top legend */
659    for (i = 8; i < 16; i++) {
660       for (j = 0x0F; j < 0x100; j += 0x10) {
661          toprow[i][j] |= 0x0001;
662       }
663    }
664
665    /*
666       Now write the raster image.
667
668       XOR each byte with 0xFF because black = 0, white = 1 in BMP.
669    */
670
671    /* Write the glyphs, bottom-up, left-to-right, in rows of 16 (i.e., 0x10) */
672    for (i = 0xFF00; i >= 0; i -= 0x100) {
673       thisrow = i >> 8; /* 256 glyphs per row */
674       for (j = 15; j >= 0; j--) {
675          /* left-hand legend */
676          putchar((~leftcol[thisrow][j] >> 24) & 0xFF);
677          putchar((~leftcol[thisrow][j] >> 16) & 0xFF);
678          putchar((~leftcol[thisrow][j] >>  8) & 0xFF);
679          putchar( ~leftcol[thisrow][j]        & 0xFF);
680          /* Unifont glyph */
681          for (k = 0x00; k < 0x100; k++) {
682             bytesout = ~bitarray[i+k][j] & 0xFFFF;
683             putchar((bytesout >> 8) & 0xFF);
684             putchar( bytesout       & 0xFF);
685          }
686       }
687    }
688
689    /*
690       Write the top legend.
691    */
692    /* i == 15: bottom pixel row of header is output here */
693    /* left-hand legend: solid black line except for right-most pixel */
694    putchar(0x00);
695    putchar(0x00);
696    putchar(0x00);
697    putchar(0x01);
698    for (j = 0; j < 256; j++) {
699       putchar((~toprow[16 + 15][j] >> 8) & 0xFF);
700       putchar( ~toprow[16 + 15][j]       & 0xFF);
701    }
702
703    putchar(0xFF);
704    putchar(0xFF);
705    putchar(0xFF);
706    putchar(0xFC);
707    for (j = 0; j < 256; j++) {
708       putchar((~toprow[16 + 14][j] >> 8) & 0xFF);
709       putchar( ~toprow[16 + 14][j]       & 0xFF);
710    }
711
712    for (i = 16 + 13; i >= 0; i--) {
713       if (i >= 8) { /* make vertical stroke on right */
714          putchar(0xFF);
715          putchar(0xFF);
716          putchar(0xFF);
717          putchar(0xFD);
718       }
719       else { /* all white */
720          putchar(0xFF);
721          putchar(0xFF);
722          putchar(0xFF);
723          putchar(0xFF);
724       }
725       for (j = 0; j < 256; j++) {
726          putchar((~toprow[i][j] >> 8) & 0xFF);
727          putchar( ~toprow[i][j]       & 0xFF);
728       }
729    }
730
731    /*
732       Write the header.
733    */
734
735    /* 8 completely white rows */
736    for (i = 7; i >= 0; i--) {
737       for (j = 0; j < 258; j++) {
738          putchar(0xFF);
739          putchar(0xFF);
740       }
741    }
742
743    for (i = 15; i >= 0; i--) {
744       /* left-hand legend */
745       putchar(0xFF);
746       putchar(0xFF);
747       putchar(0xFF);
748       putchar(0xFF);
749       /* header glyph */
750       for (j = 0; j < 256; j++) {
751          bytesout = ~header[i][j] & 0xFFFF;
752          putchar((bytesout >> 8) & 0xFF);
753          putchar( bytesout       & 0xFF);
754       }
755    }
756
757    /* 8 completely white rows at very top */
758    for (i = 7; i >= 0; i--) {
759       for (j = 0; j < 258; j++) {
760       putchar(0xFF);
761       putchar(0xFF);
762       }
763    }
764
765    return;
766 }
767
768