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