1 /* $NetBSD: bdfload.c,v 1.24 2026/06/06 17:58:53 macallan Exp $ */ 2 3 /* 4 * Copyright (c) 2018 Michael Lorenz 5 * All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 17 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 18 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 19 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 20 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 21 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 22 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 23 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 25 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 */ 27 28 /* 29 * a crude BDF loader for wsdisplay 30 */ 31 32 #include <stdlib.h> 33 #include <stdio.h> 34 #include <fcntl.h> 35 #include <unistd.h> 36 #include <string.h> 37 #include <errno.h> 38 #include <ctype.h> 39 #include <sys/ioctl.h> 40 #include <err.h> 41 42 #include <dev/wscons/wsconsio.h> 43 44 /* 45 * wsdisplay_font but with strings embedded and integer fields in 46 * little endian 47 */ 48 struct wsfthdr { 49 char magic[4]; /* "WSFT" */ 50 char name[64]; 51 uint32_t firstchar; 52 uint32_t numchars; 53 uint32_t encoding; 54 uint32_t fontwidth; 55 uint32_t fontheight; 56 uint32_t stride; 57 uint32_t bitorder; 58 uint32_t byteorder; 59 }; 60 61 62 const struct encmap { 63 const char *name; 64 int encoding; 65 } encmap[] = { 66 { "cp437", WSDISPLAY_FONTENC_IBM }, 67 { "ibm", WSDISPLAY_FONTENC_IBM }, 68 { "iso", WSDISPLAY_FONTENC_ISO }, 69 { "iso8859", WSDISPLAY_FONTENC_ISO }, 70 { "iso10646", WSDISPLAY_FONTENC_ISO }, 71 { "iso-8859-1", WSDISPLAY_FONTENC_ISO }, 72 { "iso-8859-2", WSDISPLAY_FONTENC_ISO2 }, 73 { "iso-8859-7", WSDISPLAY_FONTENC_ISO7 }, 74 { "iso2", WSDISPLAY_FONTENC_ISO2 }, 75 { "iso7", WSDISPLAY_FONTENC_ISO7 }, 76 { "iso8859-1", WSDISPLAY_FONTENC_ISO }, 77 { "iso8859-2", WSDISPLAY_FONTENC_ISO2 }, 78 { "iso8859-7", WSDISPLAY_FONTENC_ISO7 }, 79 { "koi8-r", WSDISPLAY_FONTENC_KOI8_R }, 80 { "koi8r", WSDISPLAY_FONTENC_KOI8_R }, 81 { "latin-1", WSDISPLAY_FONTENC_ISO }, 82 { "latin-2", WSDISPLAY_FONTENC_ISO2 }, 83 { "latin1", WSDISPLAY_FONTENC_ISO }, 84 { "latin2", WSDISPLAY_FONTENC_ISO2 }, 85 { "pcvt", WSDISPLAY_FONTENC_PCVT }, 86 { NULL, -1 } 87 }; 88 89 const char * const encname[] = { 90 #define _ENC(_e) [_e] = #_e 91 _ENC(WSDISPLAY_FONTENC_ISO), 92 _ENC(WSDISPLAY_FONTENC_IBM), 93 _ENC(WSDISPLAY_FONTENC_PCVT), 94 _ENC(WSDISPLAY_FONTENC_ISO7), 95 _ENC(WSDISPLAY_FONTENC_ISO2), 96 _ENC(WSDISPLAY_FONTENC_KOI8_R), 97 }; 98 99 100 const char *ofile = NULL; 101 int encoding = -1; 102 int verbose = 0; 103 int dump = 0; 104 int header = 0; 105 int force = 0; 106 int scale = 0; 107 int dub = 0; 108 int smoothe = 0; 109 char commentbuf[2048] = ""; 110 int commentptr = 0; 111 char fontname[64] = ""; 112 char *names[256]; 113 114 void 115 dump_line(char *gptr, int stride) 116 { 117 int i, j, msk, c; 118 119 for (i = 0; i < stride; i++) { 120 c = gptr[i]; 121 msk = 0x80; 122 for (j = 0; j < 8; j++) { 123 putchar((c & msk) != 0 ? '#' : ' '); 124 msk = msk >> 1; 125 } 126 } 127 printf("\n"); 128 } 129 130 void 131 write_wsf(const char *oname, struct wsdisplay_font *f) 132 { 133 struct wsfthdr h; 134 uint8_t *buffer = f->data; 135 int buflen = f->numchars * f->stride * f->fontheight; 136 137 memset(&h, 0, sizeof(h)); 138 strncpy(h.magic, "WSFT", sizeof(h.magic)); 139 strncpy(h.name, f->name, sizeof(h.name)); 140 h.firstchar = htole32(f->firstchar); 141 h.numchars = htole32(f->numchars); 142 h.encoding = htole32(f->encoding); 143 h.fontwidth = htole32(f->fontwidth); 144 h.fontheight = htole32(f->fontheight); 145 h.stride = htole32(f->stride); 146 h.bitorder = htole32(f->bitorder); 147 h.byteorder = htole32(f->byteorder); 148 149 int wsfd = open(ofile, O_WRONLY | O_CREAT | O_TRUNC, 0644); 150 if (wsfd < 0) 151 err(EXIT_FAILURE, "%s", ofile); 152 153 ssize_t nwritten; 154 nwritten = write(wsfd, &h, sizeof(h)); 155 if (nwritten < 0) 156 err(EXIT_FAILURE, "%s", ofile); 157 if (nwritten != sizeof(h)) 158 errx(EXIT_FAILURE, "%s: partial write", ofile); 159 160 nwritten = write(wsfd, buffer, buflen); 161 if (nwritten < 0) 162 err(EXIT_FAILURE, "%s", ofile); 163 if (nwritten != buflen) 164 errx(EXIT_FAILURE, "%s: partial write", ofile); 165 close(wsfd); 166 } 167 168 int 169 write_header(const char *filename, struct wsdisplay_font *f) 170 { 171 FILE *output; 172 uint8_t *buffer = f->data; 173 uint8_t c, msk; 174 int i, j, x, y, idx, pxls, left; 175 char name[64]; 176 177 /* now output as a header file */ 178 snprintf(name, sizeof(name), "%s_%dx%d", f->name, 179 f->fontwidth, f->fontheight); 180 for (i = 0; i < strlen(name); i++) { 181 if (isblank((unsigned char)name[i])) 182 name[i] = '_'; 183 } 184 if ((output = fopen(filename, "w")) == NULL) { 185 warn("Can't open output file `%s'", filename); 186 return -1; 187 } 188 if (commentptr > 0) { 189 fprintf(output, "/*\n"); 190 fputs(commentbuf, output); 191 fprintf(output, "*/\n\n"); 192 } 193 194 fprintf(output, "static u_char %s_data[];\n", name); 195 fprintf(output, "\n"); 196 fprintf(output, "static struct wsdisplay_font %s = {\n", name); 197 fprintf(output, "\t\"%s\",\t\t\t/* typeface name */\n", f->name); 198 fprintf(output, "\t%d,\t\t\t\t/* firstchar */\n", f->firstchar); 199 fprintf(output, "\t%d,\t\t\t\t/* numchars */\n", f->numchars); 200 fprintf(output, "\t%d,\t\t\t\t/* encoding */\n", f->encoding); 201 fprintf(output, "\t%d,\t\t\t\t/* fontwidth */\n", f->fontwidth); 202 fprintf(output, "\t%d,\t\t\t\t/* fontheight */\n", f->fontheight); 203 fprintf(output, "\t%d,\t\t\t\t/* stride */\n", f->stride); 204 fprintf(output, "\tWSDISPLAY_FONTORDER_L2R,\t/* bit order */\n"); 205 fprintf(output, "\tWSDISPLAY_FONTORDER_L2R,\t/* byte order */\n"); 206 fprintf(output, "\t%s_data\t\t/* data */\n", name); 207 fprintf(output, "};\n\n"); 208 fprintf(output, "static u_char %s_data[] = {\n", name); 209 for (i = 0; i < f->numchars; i++) { 210 if (names[i] != NULL) { 211 fprintf(output, "\t/* %d %s */\n", i + f->firstchar, names[i]); 212 } else 213 fprintf(output, "\t/* %d */\n", i + f->firstchar); 214 idx = i * f->stride * f->fontheight; 215 for (y = 0; y < f->fontheight; y++) { 216 for (x = 0; x < f->stride; x++) { 217 fprintf(output, "0x%02x, ",buffer[idx + x]); 218 } 219 fprintf(output, "/* "); 220 pxls = f->fontwidth; 221 for (x = 0; x < f->stride; x++) { 222 c = buffer[idx + x]; 223 msk = 0x80; 224 left = pxls > 8 ? 8 : pxls; 225 for (j = 0; j < left; j++) { 226 fprintf(output, "%s", 227 (c & msk) != 0 ? "[]" : ". "); 228 msk = msk >> 1; 229 } 230 pxls -= 8; 231 } 232 fprintf(output, " */\n"); 233 234 idx += f->stride; 235 } 236 } 237 fprintf(output, "};\n"); 238 fclose(output); 239 return 0; 240 } 241 242 void 243 double_pixels(uint8_t *inbuf, uint16_t *outbuf, int bytes) 244 { 245 int i, j; 246 uint16_t outmask, out; 247 uint8_t in, inmask; 248 249 for (i = 0; i < bytes; i++) { 250 inmask = 0x80; 251 outmask = 0xc000; 252 out = 0; 253 in = inbuf[i]; 254 for (j = 0; j < 8; j++) { 255 if (in & inmask) { 256 out |= outmask; 257 } 258 inmask = inmask >> 1; 259 outmask = outmask >> 2; 260 } 261 outbuf[i * 2] = htobe16(out); 262 } 263 } 264 265 void fill_dup(uint16_t *buf, int lines) 266 { 267 int i; 268 for (i = 0; i < lines; i++) { 269 buf[2 * i + 1] = buf[2 * i]; 270 } 271 } 272 273 void smoothe_pixels(uint16_t *buf, int lines) 274 { 275 int i, j, topright, topleft, botright, botleft; 276 uint16_t pmask, in, prev, next, out; 277 for (i = 0; i < lines; i++) { 278 pmask = 0xc000; 279 in = be16toh(buf[i]); 280 out = in; 281 prev = next = 0; 282 if (i > 1) prev = be16toh(buf[i - 2]); 283 if (i < (lines - 2)) next = be16toh(buf[i + 2]); 284 for (j = 0; j < 8; j++) { 285 if ((in & pmask) == 0) { 286 /* empty pixel, check surroundings */ 287 topright = topleft = botright = botleft = 0; 288 if (((i & 1) == 0) && (j < 6)) 289 topright = (((prev & pmask) == pmask) && 290 ((prev & (pmask >> 2)) != 0) && 291 ((in & (pmask >> 2)) != 0)); 292 if (((i & 1) == 0) && (j > 0)) 293 topleft = (((prev & pmask) == pmask) && 294 ((prev & (pmask << 2)) != 0) && 295 ((in & (pmask << 2)) != 0)); 296 if ((i & 1) && (j < 6)) 297 botright = (((next & pmask) == pmask) && 298 ((next & (pmask >> 2)) != 0) && 299 ((in & (pmask >> 2)) != 0)); 300 if ((i & 1) && (j > 0)) 301 botleft = (((next & pmask) == pmask) && 302 ((next & (pmask << 2)) != 0) && 303 ((in & (pmask << 2)) != 0)); 304 if ((topright + topleft + botright + botleft) == 1) { 305 if (topleft || botleft) out |= pmask << 1; 306 if (topright || botright) out |= pmask >> 1; 307 } 308 } 309 pmask = pmask >> 2; 310 } 311 buf[i] = htobe16(out); 312 } 313 } 314 315 void 316 interpret(FILE *foo) 317 { 318 char line[128], *arg, name[64] = "foo", *buffer, *cbitmap; 319 char charname[65], *charnamebuf; 320 int buflen = -1, charnamebufptr = 0, j; 321 int in_char = 0, current = -1, stride = 0, charsize = 0; 322 int width, height, x, y, num; 323 int first = 255, last = 0; 324 int left, top, lines; 325 int bl = 255, bt = 255, br = -1, bb = -1; 326 struct wsdisplay_font f; 327 int status; 328 329 charnamebuf = malloc(64 * 256); 330 if (charnamebuf == 0) err(EXIT_FAILURE, "failed to allocate memory\n"); 331 memset(charnamebuf, 0, 64 * 256); 332 for (j = 0; j < 256; j++) names[j] = NULL; 333 334 while (fgets(line, sizeof(line), foo) != NULL) { 335 size_t i = 0, len; 336 /* separate keyword from parameters */ 337 len = strlen(line); 338 while (!isspace((unsigned char)line[i]) && i < len) i++; 339 line[i] = 0; 340 arg = &line[i + 1]; 341 i = 0; 342 len = strlen(arg); 343 /* get rid of garbage */ 344 while ((!iscntrl((unsigned char)arg[i])) && (arg[i] != 0)) { 345 i++; 346 } 347 arg[i] = 0; 348 if (strcmp(line, "FAMILY_NAME") == 0) { 349 char *q; 350 /* cut off quotation marks */ 351 strlcpy(name, arg + 1, 64); 352 /* remove trailing " */ 353 if ((q = strnstr(name, "\"", 64)) != NULL) 354 *q = 0; 355 if (verbose) printf("name: %s\n", name); 356 } else if (strcmp(line, "COMMENT") == 0) { 357 commentptr += snprintf(&commentbuf[commentptr], 358 sizeof(commentbuf) - commentptr, "%s\n", arg); 359 } else if (strcmp(line, "SPACING") == 0) { 360 char spc[16]; 361 int res; 362 res = sscanf(arg, "%s", spc); 363 if (res > 0) { 364 if (verbose) printf("spacing %s\n", spc); 365 if ((spc[1] == 'P') && (force == 0)) { 366 warnx("This is a proportional font, " 367 "results are probably not suitable " 368 "for console use."); 369 errx(EXIT_FAILURE, "Use -f to override " 370 "if you want to try it anyway."); 371 } 372 } 373 } else if (strcmp(line, "FONTBOUNDINGBOX") == 0) { 374 int res; 375 res = sscanf(arg, "%d %d %d %d", 376 &width, &height, &x, &y); 377 stride = (width + 7) >> 3; 378 if (verbose) printf("box %d x %d\n", width, height); 379 if (stride > 2) { 380 errx(EXIT_FAILURE, 381 "no fonts wider than 16 work for now\n"); 382 } 383 charsize = height * stride; 384 buflen = 257 * charsize; 385 buffer = calloc(1, buflen); 386 if (buffer == NULL) { 387 err(EXIT_FAILURE, 388 "failed to allocate %dKB for glyphs\n", 389 buflen); 390 } 391 cbitmap = buffer + 256 * charsize; 392 } else if (strcmp(line, "CHARS") == 0) { 393 if (sscanf(arg, "%d", &num) == 1) 394 if (verbose) 395 printf("number of characters: %d\n", num); 396 } else if (strcmp(line, "STARTCHAR") == 0) { 397 in_char = 1; 398 if (charsize <= 1) err(EXIT_FAILURE, 399 "syntax error - no valid FONTBOUNDINGBOX\n"); 400 memset(cbitmap, 0, charsize); 401 strlcpy(charname, arg, 64); 402 if (dump && (strlen(charname) > 0)) 403 printf("name: %s\n", charname); 404 405 } else if (strcmp(line, "ENDCHAR") == 0) { 406 in_char = 0; 407 /* only commit the glyph if it's in range */ 408 if ((current >= 0) && (current < 256)) { 409 memcpy(&buffer[charsize * current], 410 cbitmap, charsize); 411 if ((strlen(charname) > 0) && 412 (charnamebufptr < 255 * 64)) { 413 char *cur; 414 int len; 415 /* copy name into buffer, keep a 416 * pointer to it for later */ 417 cur = &charnamebuf[charnamebufptr]; 418 len = strlcpy(cur, charname, 64); 419 charnamebufptr += len + 1; 420 names[current] = cur; 421 } 422 } 423 current = -1; 424 } else if (strcmp(line, "ENCODING") == 0) { 425 if (sscanf(arg, "%d", ¤t) == 1) { 426 if (current >= 0 && current < 256) { 427 if (current < first) first = current; 428 if (current > last) last = current; 429 if (dump) printf("glyph %d\n", current); 430 } 431 } 432 } else if (strcmp(line, "BBX") == 0) { 433 int cx, cy, cwi, che; 434 if (sscanf(arg, "%d %d %d %d", &cwi, &che, &cx, &cy) 435 == 4) { 436 left = cx; 437 lines = che; 438 top = height + y - che - cy; 439 if (left < bl) bl = left; 440 if (top < bt) bt = top; 441 if ((left + cwi) > br) br = left + cwi; 442 if ((top + che) > bb) bb = top + che; 443 if (dump && verbose) 444 printf("top %d left %d\n", top, left); 445 } 446 } else if (strcmp(line, "BITMAP") == 0) { 447 int i, j, k, l; 448 char num[32]; 449 char *gptr = cbitmap; 450 char *bptr = gptr + top; 451 uint16_t *bptr16 = (uint16_t *)gptr; 452 bptr16 += top; 453 /* see if the character is in range */ 454 if ((current < 0) || (current > 255)) continue; 455 /* now we read & render the character */ 456 for (i = 0; i < lines; i++) { 457 fgets(num, 32, foo); 458 sscanf(num, "%x", &l); 459 if ((stride) == 2 && (strlen(num) < 4)) 460 l = l << 8; 461 l = l >> left; 462 if (stride == 1) { 463 *bptr = l; 464 bptr++; 465 } else { 466 *bptr16 = htobe16(l); 467 bptr16++; 468 } 469 } 470 if (dump) { 471 gptr = cbitmap; 472 for (i = 0; i < height; i++) { 473 dump_line(gptr, stride); 474 gptr += stride; 475 } 476 } 477 } 478 } 479 if (verbose) { 480 printf("range %d to %d\n", first, last); 481 printf("encoding: %s\n", encname[encoding]); 482 printf("actual box: %d %d %d %d\n", bl, bt, br, bb); 483 } 484 485 /* now stuff it into a something wsfont understands */ 486 f.firstchar = first; 487 f.numchars = last - first + 1; 488 f.encoding = encoding; 489 if (fontname[0] == 0) { 490 f.name = name; 491 } else f.name = fontname; 492 f.bitorder = WSDISPLAY_FONTORDER_L2R; 493 f.byteorder = WSDISPLAY_FONTORDER_L2R; 494 495 if (scale) { 496 uint16_t *outbuf; 497 uint8_t *inbuf; 498 int i; 499 500 if (stride != 1) err(EXIT_FAILURE, 501 "scaling works only on fonts up to 8 pixels wide\n"); 502 f.fontwidth = width * 2 /*(width + 3) & ~3*/; 503 f.fontheight = height * 2; 504 f.stride = stride * 2; 505 outbuf = calloc(1, f.numchars * charsize * 4); 506 if (outbuf == NULL) err(EXIT_FAILURE, 507 "failed to allocate memory for scale buffer\n"); 508 f.data = outbuf; 509 inbuf = &buffer[first * charsize]; 510 for (i = 0; i < f.numchars; i++) { 511 double_pixels(inbuf, outbuf, charsize); 512 fill_dup(outbuf, charsize); 513 if (smoothe) smoothe_pixels(outbuf, charsize * 2); 514 inbuf += charsize; 515 outbuf += charsize * 2; 516 } 517 } else if (dub) { 518 uint8_t *outbuf; 519 uint8_t *inbuf; 520 int i, j; 521 522 if (stride != 1) err(EXIT_FAILURE, 523 "scaling works only on fonts up to 8 pixels wide\n"); 524 f.fontwidth = width; 525 f.fontheight = height * 2; 526 f.stride = stride; 527 outbuf = calloc(1, f.numchars * charsize * 2); 528 if (outbuf == NULL) err(EXIT_FAILURE, 529 "failed to allocate memory for scale buffer\n"); 530 f.data = outbuf; 531 inbuf = &buffer[first * charsize]; 532 for (i = 0; i < f.numchars; i++) { 533 for (j = 0; j < height; j++) { 534 outbuf[2 * j] = inbuf[j]; 535 outbuf[2 * j + 1] = inbuf[j]; 536 } 537 inbuf += charsize; 538 outbuf += charsize * 2; 539 } 540 541 } else{ 542 f.fontwidth = width /*(width + 3) & ~3*/; 543 f.fontheight = height; 544 f.stride = stride; 545 f.data = &buffer[first * charsize]; 546 } 547 548 if (ofile == NULL) { 549 int fdev = open("/dev/wsfont", O_RDWR, 0); 550 if (fdev < 0) 551 err(EXIT_FAILURE, "/dev/wsfont"); 552 status = ioctl(fdev, WSDISPLAYIO_LDFONT, &f); 553 if (status != 0) 554 err(EXIT_FAILURE, "WSDISPLAYIO_LDFONT"); 555 close(fdev); 556 } 557 else { 558 if (header == 0) 559 write_wsf(ofile, &f); 560 else 561 write_header(ofile, &f); 562 } 563 } 564 565 __dead void 566 usage() 567 { 568 fprintf(stderr, "Usage: %s [-vdhf2st] [-e encoding] [-N name] " 569 "[-o ofile.wsf] font.bdf\n", getprogname()); 570 exit(EXIT_FAILURE); 571 } 572 573 int 574 main(int argc, char *argv[]) 575 { 576 FILE *foo; 577 const char *encname = NULL; 578 579 int c; 580 while ((c = getopt(argc, argv, "e:o:N:vdhf2st")) != -1) { 581 switch (c) { 582 583 /* font encoding */ 584 case 'e': 585 if (encname != NULL) 586 usage(); 587 encname = optarg; 588 break; 589 590 /* output file name */ 591 case 'o': 592 if (ofile != NULL) 593 usage(); 594 ofile = optarg; 595 break; 596 597 case 'v': 598 verbose = 1; 599 break; 600 601 case 'd': 602 dump = 1; 603 break; 604 605 case 'h': 606 header = 1; 607 break; 608 case 'f': 609 force = 1; 610 break; 611 case '2': 612 scale = 1; 613 break; 614 case 't': 615 dub = 1; 616 break; 617 case 's': 618 smoothe = 1; 619 break; 620 case 'N': 621 strncpy(fontname, optarg, 64); 622 break; 623 case '?': /* FALLTHROUGH */ 624 default: 625 usage(); 626 } 627 } 628 629 argc -= optind; 630 argv += optind; 631 632 if (encname == NULL) { 633 encoding = WSDISPLAY_FONTENC_ISO; 634 } 635 else { 636 for (const struct encmap *e = encmap; e->name; ++e) { 637 if (strcmp(e->name, encname) == 0) { 638 encoding = e->encoding; 639 break; 640 } 641 } 642 } 643 644 /* get encoding from the bdf file? */ 645 if (encoding == -1) 646 encoding = WSDISPLAY_FONTENC_ISO; 647 648 if (argc == 0) 649 usage(); 650 651 const char *bdfname = argv[0]; 652 foo = fopen(bdfname, "r"); 653 if (foo == NULL) 654 err(EXIT_FAILURE, "%s", bdfname); 655 656 interpret(foo); 657 return EXIT_SUCCESS; 658 } 659