2 unifontpic.c - see the "Big Picture": the entire Unifont in one BMP bitmap.
4 Author: Paul Hardy, 2013
6 Copyright (C) 2013 Paul Hardy
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.
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.
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/>.
30 #define HEADER_STRING "GNU Unifont 6.3" /* to be printed as chart title */
33 int main(int argc, char **argv) {
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 */
40 int i; /* loop variable */
42 int bitarray[0x10000][16]; /* 16 pixel rows for each of 65,536 glyphs */
48 memset((void *)bitarray, 0, 0x10000 * 16 * sizeof(int));
50 gethex(bitarray); /* read .hex input file and fill bitarray with glyph data */
53 for (i = 1; i < argc; i++) {
54 if (strncmp(argv[i],"-l",2) == 0) { /* long display */
57 else if (strncmp(argv[i],"-d",2) == 0) {
58 dpi = atoi(&argv[i][2]); /* dots/inch specified on command line */
60 else if (strncmp(argv[i],"-t",2) == 0) {
67 genwidebmp(bitarray, dpi, tinynum); /* write bitarray glyph data to BMP file */
70 genlongbmp(bitarray, dpi, tinynum);
77 void output4(int thisword) {
79 putchar( thisword & 0xFF);
80 putchar((thisword >> 8) & 0xFF);
81 putchar((thisword >> 16) & 0xFF);
82 putchar((thisword >> 24) & 0xFF);
88 void output2(int thisword) {
90 putchar( thisword & 0xFF);
91 putchar((thisword >> 8) & 0xFF);
98 gethex() reads a Unifont .hex-format input file from stdin.
100 void gethex(int bitarray[0x10000][16]) {
102 char instring[MAXSTRING]; /* input buffer for a code point */
103 char *bitstring; /* pointer into instring for glyph bitmap */
106 int codept; /* the Unicode code point of the current glyph */
109 Read each input line and place its glyph into the bit array.
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];
117 If this glyph is only 8 pixels wide, expand so right half of glyph is 0s.
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 ];
127 bitstring[64] = '\0'; /* truncate string, overwriting newline */
129 for (i = 0; i < 16; i++) {
130 sscanf(bitstring, "%4X", &bitarray[codept][i]);
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.
143 void genlongbmp(int bitarray[0x10000][16], int dpi, int tinynum) {
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 */
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 */
155 unsigned toprow[16][16]; /* code point legend on top of chart */
158 hexdigit contains 4x5 pixel arrays of tiny digits for legend.
159 See unihexgen.c for more detailed description in comments.
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 */
179 int digitrow; /* row we're in (0..4) for the above hexdigit digits */
182 DataOffset = BMP Header bytes + InfoHeader bytes + ColorTable bytes.
184 int DataOffset = 14 + 40 + 8; /* fixed size for monochrome BMP */
187 int Width, Height; /* bitmap image width and height in pixels */
188 int ppm; /* integer pixels per meter */
194 void output4(int), output2(int);
197 Image width and height, in pixels.
199 N.B.: Width must be an even multiple of 32 pixels, or 4 bytes.
201 Width = 18 * 16; /* (2 legend + 16 glyphs) * 16 pixels/glyph */
202 Height = 4099 * 16; /* (1 header + 4096 glyphs) * 16 rows/glyph */
204 ImageSize = Height * (Width / 8); /* in bytes, calculated from pixels */
206 FileSize = DataOffset + ImageSize;
208 /* convert dots/inch to pixels/meter */
209 if (dpi == 0) dpi = 96;
210 ppm = (int)((double)dpi * 100.0 / 2.54 + 0.5);
213 Generate the BMP Header
221 BMP Header + InfoHeader + Color Table + Raster Data
223 output4(FileSize); /* FileSize */
224 output4(0x0000); /* reserved */
226 /* Calculate DataOffset */
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) */
247 Create header row bits.
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 */
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 */
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];
266 Create the left column legend.
268 memset((void *)leftcol, 0, 4096 * 16 * sizeof(unsigned));
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 */
276 thisrow = codept >> 4; /* rows of 16 glyphs */
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);
285 /* fill in third digit */
286 for (digitrow = 0; digitrow < 5; digitrow++) {
287 leftcol[thisrow][9 + digitrow] = hexdigit[d3][digitrow] << 10;
289 leftcol[thisrow][9 + 4] |= 0xF << 4; /* underscore as 4th digit */
291 for (i = 0; i < 15; i ++) {
292 leftcol[thisrow][i] |= 0x00000002; /* right border */
295 leftcol[thisrow][15] = 0x0000FFFE; /* bottom border */
297 if (d3 == 0xF) { /* 256-point boundary */
298 leftcol[thisrow][15] |= 0x00FF0000; /* longer tic mark */
301 if ((thisrow % 0x40) == 0x3F) { /* 1024-point boundary */
302 leftcol[thisrow][15] |= 0xFFFF0000; /* longest tic mark */
307 Create the top row legend.
309 memset((void *)toprow, 0, 16 * 16 * sizeof(unsigned));
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 */
317 /* fill in last digit */
318 for (digitrow = 0; digitrow < 5; digitrow++) {
319 toprow[6 + digitrow][codept] = hexdigit[d4][digitrow] << 6;
323 for (j = 0; j < 16; j++) {
324 /* force bottom pixel row to be white, for separation from glyphs */
325 toprow[15][j] = 0x0000;
328 /* 1 pixel row with left-hand legend line */
329 for (j = 0; j < 16; j++) {
330 toprow[14][j] |= 0xFFFF;
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;
341 Now write the raster image.
343 XOR each byte with 0xFF because black = 0, white = 1 in BMP.
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);
356 for (k = 0; k < 16; k++) {
357 bytesout = ~bitarray[i+k][j] & 0xFFFF;
358 putchar((bytesout >> 8) & 0xFF);
359 putchar( bytesout & 0xFF);
365 Write the top legend.
367 /* i == 15: bottom pixel row of header is output here */
368 /* left-hand legend: solid black line except for right-most pixel */
373 for (j = 0; j < 16; j++) {
374 putchar((~toprow[15][j] >> 8) & 0xFF);
375 putchar( ~toprow[15][j] & 0xFF);
382 for (j = 0; j < 16; j++) {
383 putchar((~toprow[14][j] >> 8) & 0xFF);
384 putchar( ~toprow[14][j] & 0xFF);
387 for (i = 13; i >= 0; i--) {
392 for (j = 0; j < 16; j++) {
393 putchar((~toprow[i][j] >> 8) & 0xFF);
394 putchar( ~toprow[i][j] & 0xFF);
402 /* 7 completely white rows */
403 for (i = 7; i >= 0; i--) {
404 for (j = 0; j < 18; j++) {
410 for (i = 15; i >= 0; i--) {
411 /* left-hand legend */
417 for (j = 0; j < 16; j++) {
418 bytesout = ~header[i][j] & 0xFFFF;
419 putchar((bytesout >> 8) & 0xFF);
420 putchar( bytesout & 0xFF);
424 /* 8 completely white rows at very top */
425 for (i = 7; i >= 0; i--) {
426 for (j = 0; j < 18; j++) {
438 genwidebmp() generates the BMP output file from a bitmap parameter.
439 This is a wide bitmap, 256 glyphs wide by 256 glyphs tall.
441 void genwidebmp(int bitarray[0x10000][16], int dpi, int tinynum) {
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 */
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 */
453 unsigned toprow[32][256]; /* code point legend on top of chart */
456 hexdigit contains 4x5 pixel arrays of tiny digits for legend.
457 See unihexgen.c for more detailed description in comments.
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 */
477 int digitrow; /* row we're in (0..4) for the above hexdigit digits */
478 int hexalpha1, hexalpha2; /* to convert hex digits to ASCII */
481 DataOffset = BMP Header bytes + InfoHeader bytes + ColorTable bytes.
483 int DataOffset = 14 + 40 + 8; /* fixed size for monochrome BMP */
486 int Width, Height; /* bitmap image width and height in pixels */
487 int ppm; /* integer pixels per meter */
493 void output4(int), output2(int);
496 Image width and height, in pixels.
498 N.B.: Width must be an even multiple of 32 pixels, or 4 bytes.
500 Width = 258 * 16; /* ( 2 legend + 256 glyphs) * 16 pixels/glyph */
501 Height = 260 * 16; /* (2 header + 2 legend + 256 glyphs) * 16 rows/glyph */
503 ImageSize = Height * (Width / 8); /* in bytes, calculated from pixels */
505 FileSize = DataOffset + ImageSize;
507 /* convert dots/inch to pixels/meter */
508 if (dpi == 0) dpi = 96;
509 ppm = (int)((double)dpi * 100.0 / 2.54 + 0.5);
512 Generate the BMP Header
519 BMP Header + InfoHeader + Color Table + Raster Data
521 output4(FileSize); /* FileSize */
522 output4(0x0000); /* reserved */
523 /* Calculate DataOffset */
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) */
544 Create header row bits.
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 */
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 */
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];
563 Create the left column legend.
565 memset((void *)leftcol, 0, 256 * 16 * sizeof(unsigned));
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 */
573 thisrow = codept >> 8; /* rows of 256 glyphs */
575 /* fill in first and second digits */
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);
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;
589 for (i = 0 ; i < 16; i++) {
590 leftcol[thisrow][i] =
591 (bitarray[hexalpha1][i] << 2) |
592 (bitarray[hexalpha2][i] >> 6);
596 for (i = 0; i < 15; i ++) {
597 leftcol[thisrow][i] |= 0x00000002; /* right border */
600 leftcol[thisrow][15] = 0x0000FFFE; /* bottom border */
602 if (d2 == 0xF) { /* 4096-point boundary */
603 leftcol[thisrow][15] |= 0x00FF0000; /* longer tic mark */
606 if ((thisrow % 0x40) == 0x3F) { /* 16,384-point boundary */
607 leftcol[thisrow][15] |= 0xFFFF0000; /* longest tic mark */
612 Create the top row legend.
614 memset((void *)toprow, 0, 32 * 256 * sizeof(unsigned));
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 */
623 for (digitrow = 0; digitrow < 5; digitrow++) {
624 toprow[16 + 6 + digitrow][codept] =
625 (hexdigit[d3][digitrow] << 10) |
626 (hexdigit[d4][digitrow] << 4);
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);
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;
646 /* 1 pixel row with left-hand legend line */
647 for (j = 0; j < 256; j++) {
648 toprow[16 + 14][j] |= 0xFFFF;
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;
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;
666 Now write the raster image.
668 XOR each byte with 0xFF because black = 0, white = 1 in BMP.
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);
681 for (k = 0x00; k < 0x100; k++) {
682 bytesout = ~bitarray[i+k][j] & 0xFFFF;
683 putchar((bytesout >> 8) & 0xFF);
684 putchar( bytesout & 0xFF);
690 Write the top legend.
692 /* i == 15: bottom pixel row of header is output here */
693 /* left-hand legend: solid black line except for right-most pixel */
698 for (j = 0; j < 256; j++) {
699 putchar((~toprow[16 + 15][j] >> 8) & 0xFF);
700 putchar( ~toprow[16 + 15][j] & 0xFF);
707 for (j = 0; j < 256; j++) {
708 putchar((~toprow[16 + 14][j] >> 8) & 0xFF);
709 putchar( ~toprow[16 + 14][j] & 0xFF);
712 for (i = 16 + 13; i >= 0; i--) {
713 if (i >= 8) { /* make vertical stroke on right */
719 else { /* all white */
725 for (j = 0; j < 256; j++) {
726 putchar((~toprow[i][j] >> 8) & 0xFF);
727 putchar( ~toprow[i][j] & 0xFF);
735 /* 8 completely white rows */
736 for (i = 7; i >= 0; i--) {
737 for (j = 0; j < 258; j++) {
743 for (i = 15; i >= 0; i--) {
744 /* left-hand legend */
750 for (j = 0; j < 256; j++) {
751 bytesout = ~header[i][j] & 0xFFFF;
752 putchar((bytesout >> 8) & 0xFF);
753 putchar( bytesout & 0xFF);
757 /* 8 completely white rows at very top */
758 for (i = 7; i >= 0; i--) {
759 for (j = 0; j < 258; j++) {