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 7.0" /* to be printed as chart title */
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.
42 main (int argc, char **argv)
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 */
50 int i; /* loop variable */
52 int bitarray[0x10000][16]; /* 16 pixel rows for each of 65,536 glyphs */
58 memset ((void *)bitarray, 0, 0x10000 * 16 * sizeof (int));
60 gethex (bitarray); /* read .hex input file and fill bitarray with glyph data */
63 for (i = 1; i < argc; i++) {
64 if (strncmp (argv[i],"-l",2) == 0) { /* long display */
67 else if (strncmp (argv[i],"-d",2) == 0) {
68 dpi = atoi (&argv[i][2]); /* dots/inch specified on command line */
70 else if (strncmp (argv[i],"-t",2) == 0) {
77 genwidebmp (bitarray, dpi, tinynum); /* write bitarray glyph data to BMP file */
80 genlongbmp (bitarray, dpi, tinynum);
88 output4 (int thisword)
91 putchar ( thisword & 0xFF);
92 putchar ((thisword >> 8) & 0xFF);
93 putchar ((thisword >> 16) & 0xFF);
94 putchar ((thisword >> 24) & 0xFF);
101 output2 (int thisword)
104 putchar ( thisword & 0xFF);
105 putchar ((thisword >> 8) & 0xFF);
112 gethex reads a Unifont .hex-format input file from stdin.
115 gethex (int bitarray[0x10000][16])
118 char instring[MAXSTRING]; /* input buffer for a code point */
119 char *bitstring; /* pointer into instring for glyph bitmap */
122 int codept; /* the Unicode code point of the current glyph */
125 Read each input line and place its glyph into the bit array.
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];
133 If this glyph is only 8 pixels wide, expand so right half of glyph is 0s.
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 ];
143 bitstring[64] = '\0'; /* truncate string, overwriting newline */
145 for (i = 0; i < 16; i++) {
146 sscanf (bitstring, "%4X", &bitarray[codept][i]);
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.
160 genlongbmp (int bitarray[0x10000][16], int dpi, int tinynum)
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 */
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 */
175 hexdigit contains 4x5 pixel arrays of tiny digits for legend.
176 See unihexgen.c for more detailed description in comments.
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 */
196 int digitrow; /* row we're in (0..4) for the above hexdigit digits */
199 DataOffset = BMP Header bytes + InfoHeader bytes + ColorTable bytes.
201 int DataOffset = 14 + 40 + 8; /* fixed size for monochrome BMP */
204 int Width, Height; /* bitmap image width and height in pixels */
205 int ppm; /* integer pixels per meter */
211 void output4(int), output2(int);
214 Image width and height, in pixels.
216 N.B.: Width must be an even multiple of 32 pixels, or 4 bytes.
218 Width = 18 * 16; /* (2 legend + 16 glyphs) * 16 pixels/glyph */
219 Height = 4099 * 16; /* (1 header + 4096 glyphs) * 16 rows/glyph */
221 ImageSize = Height * (Width / 8); /* in bytes, calculated from pixels */
223 FileSize = DataOffset + ImageSize;
225 /* convert dots/inch to pixels/meter */
226 if (dpi == 0) dpi = 96;
227 ppm = (int)((double)dpi * 100.0 / 2.54 + 0.5);
230 Generate the BMP Header
238 BMP Header + InfoHeader + Color Table + Raster Data
240 output4 (FileSize); /* FileSize */
241 output4 (0x0000); /* reserved */
243 /* Calculate DataOffset */
244 output4 (DataOffset);
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) */
264 Create header row bits.
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 */
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 */
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];
283 Create the left column legend.
285 memset ((void *)leftcol, 0, 4096 * 16 * sizeof (unsigned));
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;
292 thisrow = codept >> 4; /* rows of 16 glyphs */
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);
301 /* fill in third digit */
302 for (digitrow = 0; digitrow < 5; digitrow++) {
303 leftcol[thisrow][9 + digitrow] = hexdigit[d3][digitrow] << 10;
305 leftcol[thisrow][9 + 4] |= 0xF << 4; /* underscore as 4th digit */
307 for (i = 0; i < 15; i ++) {
308 leftcol[thisrow][i] |= 0x00000002; /* right border */
311 leftcol[thisrow][15] = 0x0000FFFE; /* bottom border */
313 if (d3 == 0xF) { /* 256-point boundary */
314 leftcol[thisrow][15] |= 0x00FF0000; /* longer tic mark */
317 if ((thisrow % 0x40) == 0x3F) { /* 1024-point boundary */
318 leftcol[thisrow][15] |= 0xFFFF0000; /* longest tic mark */
323 Create the top row legend.
325 memset ((void *)toprow, 0, 16 * 16 * sizeof (unsigned));
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 */
333 /* fill in last digit */
334 for (digitrow = 0; digitrow < 5; digitrow++) {
335 toprow[6 + digitrow][codept] = hexdigit[d4][digitrow] << 6;
339 for (j = 0; j < 16; j++) {
340 /* force bottom pixel row to be white, for separation from glyphs */
341 toprow[15][j] = 0x0000;
344 /* 1 pixel row with left-hand legend line */
345 for (j = 0; j < 16; j++) {
346 toprow[14][j] |= 0xFFFF;
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;
357 Now write the raster image.
359 XOR each byte with 0xFF because black = 0, white = 1 in BMP.
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);
372 for (k = 0; k < 16; k++) {
373 bytesout = ~bitarray[i+k][j] & 0xFFFF;
374 putchar ((bytesout >> 8) & 0xFF);
375 putchar ( bytesout & 0xFF);
381 Write the top legend.
383 /* i == 15: bottom pixel row of header is output here */
384 /* left-hand legend: solid black line except for right-most pixel */
389 for (j = 0; j < 16; j++) {
390 putchar ((~toprow[15][j] >> 8) & 0xFF);
391 putchar ( ~toprow[15][j] & 0xFF);
398 for (j = 0; j < 16; j++) {
399 putchar ((~toprow[14][j] >> 8) & 0xFF);
400 putchar ( ~toprow[14][j] & 0xFF);
403 for (i = 13; i >= 0; i--) {
408 for (j = 0; j < 16; j++) {
409 putchar ((~toprow[i][j] >> 8) & 0xFF);
410 putchar ( ~toprow[i][j] & 0xFF);
418 /* 7 completely white rows */
419 for (i = 7; i >= 0; i--) {
420 for (j = 0; j < 18; j++) {
426 for (i = 15; i >= 0; i--) {
427 /* left-hand legend */
433 for (j = 0; j < 16; j++) {
434 bytesout = ~header[i][j] & 0xFFFF;
435 putchar ((bytesout >> 8) & 0xFF);
436 putchar ( bytesout & 0xFF);
440 /* 8 completely white rows at very top */
441 for (i = 7; i >= 0; i--) {
442 for (j = 0; j < 18; j++) {
454 genwidebmp generates the BMP output file from a bitmap parameter.
455 This is a wide bitmap, 256 glyphs wide by 256 glyphs tall.
458 genwidebmp (int bitarray[0x10000][16], int dpi, int tinynum)
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 */
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 */
473 hexdigit contains 4x5 pixel arrays of tiny digits for legend.
474 See unihexgen.c for more detailed description in comments.
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 */
494 int digitrow; /* row we're in (0..4) for the above hexdigit digits */
495 int hexalpha1, hexalpha2; /* to convert hex digits to ASCII */
498 DataOffset = BMP Header bytes + InfoHeader bytes + ColorTable bytes.
500 int DataOffset = 14 + 40 + 8; /* fixed size for monochrome BMP */
503 int Width, Height; /* bitmap image width and height in pixels */
504 int ppm; /* integer pixels per meter */
510 void output4(int), output2(int);
513 Image width and height, in pixels.
515 N.B.: Width must be an even multiple of 32 pixels, or 4 bytes.
517 Width = 258 * 16; /* ( 2 legend + 256 glyphs) * 16 pixels/glyph */
518 Height = 260 * 16; /* (2 header + 2 legend + 256 glyphs) * 16 rows/glyph */
520 ImageSize = Height * (Width / 8); /* in bytes, calculated from pixels */
522 FileSize = DataOffset + ImageSize;
524 /* convert dots/inch to pixels/meter */
525 if (dpi == 0) dpi = 96;
526 ppm = (int)((double)dpi * 100.0 / 2.54 + 0.5);
529 Generate the BMP Header
536 BMP Header + InfoHeader + Color Table + Raster Data
538 output4 (FileSize); /* FileSize */
539 output4 (0x0000); /* reserved */
540 /* Calculate DataOffset */
541 output4 (DataOffset);
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) */
561 Create header row bits.
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 */
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 */
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];
580 Create the left column legend.
582 memset ((void *)leftcol, 0, 256 * 16 * sizeof (unsigned));
584 for (codept = 0x0000; codept < 0x10000; codept += 0x100) {
585 d1 = (codept >> 12) & 0xF; /* most significant hex digit */
586 d2 = (codept >> 8) & 0xF;
588 thisrow = codept >> 8; /* rows of 256 glyphs */
590 /* fill in first and second digits */
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);
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;
604 for (i = 0 ; i < 16; i++) {
605 leftcol[thisrow][i] =
606 (bitarray[hexalpha1][i] << 2) |
607 (bitarray[hexalpha2][i] >> 6);
611 for (i = 0; i < 15; i ++) {
612 leftcol[thisrow][i] |= 0x00000002; /* right border */
615 leftcol[thisrow][15] = 0x0000FFFE; /* bottom border */
617 if (d2 == 0xF) { /* 4096-point boundary */
618 leftcol[thisrow][15] |= 0x00FF0000; /* longer tic mark */
621 if ((thisrow % 0x40) == 0x3F) { /* 16,384-point boundary */
622 leftcol[thisrow][15] |= 0xFFFF0000; /* longest tic mark */
627 Create the top row legend.
629 memset ((void *)toprow, 0, 32 * 256 * sizeof (unsigned));
631 for (codept = 0x00; codept <= 0xFF; codept++) {
632 d3 = (codept >> 4) & 0xF;
633 d4 = codept & 0xF; /* least significant hex digit */
636 for (digitrow = 0; digitrow < 5; digitrow++) {
637 toprow[16 + 6 + digitrow][codept] =
638 (hexdigit[d3][digitrow] << 10) |
639 (hexdigit[d4][digitrow] << 4);
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);
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;
659 /* 1 pixel row with left-hand legend line */
660 for (j = 0; j < 256; j++) {
661 toprow[16 + 14][j] |= 0xFFFF;
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;
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;
679 Now write the raster image.
681 XOR each byte with 0xFF because black = 0, white = 1 in BMP.
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);
694 for (k = 0x00; k < 0x100; k++) {
695 bytesout = ~bitarray[i+k][j] & 0xFFFF;
696 putchar ((bytesout >> 8) & 0xFF);
697 putchar ( bytesout & 0xFF);
703 Write the top legend.
705 /* i == 15: bottom pixel row of header is output here */
706 /* left-hand legend: solid black line except for right-most pixel */
711 for (j = 0; j < 256; j++) {
712 putchar ((~toprow[16 + 15][j] >> 8) & 0xFF);
713 putchar ( ~toprow[16 + 15][j] & 0xFF);
720 for (j = 0; j < 256; j++) {
721 putchar ((~toprow[16 + 14][j] >> 8) & 0xFF);
722 putchar ( ~toprow[16 + 14][j] & 0xFF);
725 for (i = 16 + 13; i >= 0; i--) {
726 if (i >= 8) { /* make vertical stroke on right */
732 else { /* all white */
738 for (j = 0; j < 256; j++) {
739 putchar ((~toprow[i][j] >> 8) & 0xFF);
740 putchar ( ~toprow[i][j] & 0xFF);
748 /* 8 completely white rows */
749 for (i = 7; i >= 0; i--) {
750 for (j = 0; j < 258; j++) {
756 for (i = 15; i >= 0; i--) {
757 /* left-hand legend */
763 for (j = 0; j < 256; j++) {
764 bytesout = ~header[i][j] & 0xFFFF;
765 putchar ((bytesout >> 8) & 0xFF);
766 putchar ( bytesout & 0xFF);
770 /* 8 completely white rows at very top */
771 for (i = 7; i >= 0; i--) {
772 for (j = 0; j < 258; j++) {