bdfload.c revision 53a12b5f
1/* $NetBSD: bdfload.c,v 1.21 2024/01/08 18:09:33 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 */ 48struct 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 62const 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 89const 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 100const char *ofile = NULL; 101int encoding = -1; 102int verbose = 0; 103int dump = 0; 104int header = 0; 105int force = 0; 106int scale = 0; 107int smoothe = 0; 108char commentbuf[2048] = ""; 109int commentptr = 0; 110char fontname[64] = ""; 111char *names[256]; 112 113void 114dump_line(char *gptr, int stride) 115{ 116 int i, j, msk, c; 117 118 for (i = 0; i < stride; i++) { 119 c = gptr[i]; 120 msk = 0x80; 121 for (j = 0; j < 8; j++) { 122 putchar((c & msk) != 0 ? '#' : ' '); 123 msk = msk >> 1; 124 } 125 } 126 printf("\n"); 127} 128 129void 130write_wsf(const char *oname, struct wsdisplay_font *f) 131{ 132 struct wsfthdr h; 133 uint8_t *buffer = f->data; 134 int buflen = f->numchars * f->stride * f->fontheight; 135 136 memset(&h, 0, sizeof(h)); 137 strncpy(h.magic, "WSFT", sizeof(h.magic)); 138 strncpy(h.name, f->name, sizeof(h.name)); 139 h.firstchar = htole32(f->firstchar); 140 h.numchars = htole32(f->numchars); 141 h.encoding = htole32(f->encoding); 142 h.fontwidth = htole32(f->fontwidth); 143 h.fontheight = htole32(f->fontheight); 144 h.stride = htole32(f->stride); 145 h.bitorder = htole32(f->bitorder); 146 h.byteorder = htole32(f->byteorder); 147 148 int wsfd = open(ofile, O_WRONLY | O_CREAT | O_TRUNC, 0644); 149 if (wsfd < 0) 150 err(EXIT_FAILURE, "%s", ofile); 151 152 ssize_t nwritten; 153 nwritten = write(wsfd, &h, sizeof(h)); 154 if (nwritten < 0) 155 err(EXIT_FAILURE, "%s", ofile); 156 if (nwritten != sizeof(h)) 157 errx(EXIT_FAILURE, "%s: partial write", ofile); 158 159 nwritten = write(wsfd, buffer, buflen); 160 if (nwritten < 0) 161 err(EXIT_FAILURE, "%s", ofile); 162 if (nwritten != buflen) 163 errx(EXIT_FAILURE, "%s: partial write", ofile); 164 close(wsfd); 165} 166 167int 168write_header(const char *filename, struct wsdisplay_font *f) 169{ 170 FILE *output; 171 char *buffer = f->data; 172 int i, j, x, y, idx, pxls, left; 173 char name[64], c, msk; 174 175 /* now output as a header file */ 176 snprintf(name, sizeof(name), "%s_%dx%d", f->name, 177 f->fontwidth, f->fontheight); 178 for (i = 0; i < strlen(name); i++) { 179 if (isblank((unsigned char)name[i])) 180 name[i] = '_'; 181 } 182 if ((output = fopen(filename, "w")) == NULL) { 183 warn("Can't open output file `%s'", filename); 184 return -1; 185 } 186 if (commentptr > 0) { 187 fprintf(output, "/*\n"); 188 fputs(commentbuf, output); 189 fprintf(output, "*/\n\n"); 190 } 191 192 fprintf(output, "static u_char %s_data[];\n", name); 193 fprintf(output, "\n"); 194 fprintf(output, "static struct wsdisplay_font %s = {\n", name); 195 fprintf(output, "\t\"%s\",\t\t\t/* typeface name */\n", f->name); 196 fprintf(output, "\t%d,\t\t\t\t/* firstchar */\n", f->firstchar); 197 fprintf(output, "\t%d,\t\t\t\t/* numchars */\n", f->numchars); 198 fprintf(output, "\t%d,\t\t\t\t/* encoding */\n", f->encoding); 199 fprintf(output, "\t%d,\t\t\t\t/* fontwidth */\n", f->fontwidth); 200 fprintf(output, "\t%d,\t\t\t\t/* fontheight */\n", f->fontheight); 201 fprintf(output, "\t%d,\t\t\t\t/* stride */\n", f->stride); 202 fprintf(output, "\tWSDISPLAY_FONTORDER_L2R,\t/* bit order */\n"); 203 fprintf(output, "\tWSDISPLAY_FONTORDER_L2R,\t/* byte order */\n"); 204 fprintf(output, "\t%s_data\t\t/* data */\n", name); 205 fprintf(output, "};\n\n"); 206 fprintf(output, "static u_char %s_data[] = {\n", name); 207 for (i = 0; i < f->numchars; i++) { 208 if (names[i] != NULL) { 209 fprintf(output, "\t/* %d %s */\n", i + f->firstchar, names[i]); 210 } else 211 fprintf(output, "\t/* %d */\n", i + f->firstchar); 212 idx = i * f->stride * f->fontheight; 213 for (y = 0; y < f->fontheight; y++) { 214 for (x = 0; x < f->stride; x++) { 215 fprintf(output, "0x%02x, ",buffer[idx + x]); 216 } 217 fprintf(output, "/* "); 218 pxls = f->fontwidth; 219 for (x = 0; x < f->stride; x++) { 220 c = buffer[idx + x]; 221 msk = 0x80; 222 left = pxls > 8 ? 8 : pxls; 223 for (j = 0; j < left; j++) { 224 fprintf(output, "%s", 225 (c & msk) != 0 ? "[]" : ". "); 226 msk = msk >> 1; 227 } 228 pxls -= 8; 229 } 230 fprintf(output, " */\n"); 231 232 idx += f->stride; 233 } 234 } 235 fprintf(output, "};\n"); 236 fclose(output); 237 return 0; 238} 239 240void 241double_pixels(uint8_t *inbuf, uint16_t *outbuf, int bytes) 242{ 243 int i, j; 244 uint16_t outmask, out; 245 uint8_t in, inmask; 246 247 for (i = 0; i < bytes; i++) { 248 inmask = 0x80; 249 outmask = 0xc000; 250 out = 0; 251 in = inbuf[i]; 252 for (j = 0; j < 8; j++) { 253 if (in & inmask) { 254 out |= outmask; 255 } 256 inmask = inmask >> 1; 257 outmask = outmask >> 2; 258 } 259 outbuf[i * 2] = htobe16(out); 260 } 261} 262 263void fill_dup(uint16_t *buf, int lines) 264{ 265 int i; 266 for (i = 0; i < lines; i++) { 267 buf[2 * i + 1] = buf[2 * i]; 268 } 269} 270 271void smoothe_pixels(uint16_t *buf, int lines) 272{ 273 int i, j, topright, topleft, botright, botleft; 274 uint16_t pmask, in, prev, next, out; 275 for (i = 0; i < lines; i++) { 276 pmask = 0xc000; 277 in = be16toh(buf[i]); 278 out = in; 279 prev = next = 0; 280 if (i > 1) prev = be16toh(buf[i - 2]); 281 if (i < (lines - 2)) next = be16toh(buf[i + 2]); 282 for (j = 0; j < 8; j++) { 283 if ((in & pmask) == 0) { 284 /* empty pixel, check surroundings */ 285 topright = topleft = botright = botleft = 0; 286 if (((i & 1) == 0) && (j < 6)) 287 topright = (((prev & pmask) == pmask) && 288 ((prev & (pmask >> 2)) != 0) && 289 ((in & (pmask >> 2)) != 0)); 290 if (((i & 1) == 0) && (j > 0)) 291 topleft = (((prev & pmask) == pmask) && 292 ((prev & (pmask << 2)) != 0) && 293 ((in & (pmask << 2)) != 0)); 294 if ((i & 1) && (j < 6)) 295 botright = (((next & pmask) == pmask) && 296 ((next & (pmask >> 2)) != 0) && 297 ((in & (pmask >> 2)) != 0)); 298 if ((i & 1) && (j > 0)) 299 botleft = (((next & pmask) == pmask) && 300 ((next & (pmask << 2)) != 0) && 301 ((in & (pmask << 2)) != 0)); 302 if ((topright + topleft + botright + botleft) == 1) { 303 if (topleft || botleft) out |= pmask << 1; 304 if (topright || botright) out |= pmask >> 1; 305 } 306 } 307 pmask = pmask >> 2; 308 } 309 buf[i] = htobe16(out); 310 } 311} 312 313void 314interpret(FILE *foo) 315{ 316 char line[128], *arg, name[64] = "foo", *buffer, *cbitmap; 317 char charname[65], *charnamebuf; 318 int buflen = -1, charnamebufptr = 0, j; 319 int in_char = 0, current = -1, stride = 0, charsize = 0; 320 int width, height, x, y, num; 321 int first = 255, last = 0; 322 int left, top, lines; 323 int bl = 255, bt = 255, br = -1, bb = -1; 324 struct wsdisplay_font f; 325 int status; 326 327 charnamebuf = malloc(64 * 256); 328 if (charnamebuf == 0) err(EXIT_FAILURE, "failed to allocate memory\n"); 329 memset(charnamebuf, 0, 64 * 256); 330 for (j = 0; j < 256; j++) names[j] = NULL; 331 332 while (fgets(line, sizeof(line), foo) != NULL) { 333 size_t i = 0, len; 334 /* separate keyword from parameters */ 335 len = strlen(line); 336 while (!isspace((unsigned char)line[i]) && i < len) i++; 337 line[i] = 0; 338 arg = &line[i + 1]; 339 i = 0; 340 len = strlen(arg); 341 /* get rid of garbage */ 342 while ((!iscntrl((unsigned char)arg[i])) && (arg[i] != 0)) { 343 i++; 344 } 345 arg[i] = 0; 346 if (strcmp(line, "FAMILY_NAME") == 0) { 347 char *q; 348 /* cut off quotation marks */ 349 strlcpy(name, arg + 1, 64); 350 /* remove trailing " */ 351 if ((q = strnstr(name, "\"", 64)) != NULL) 352 *q = 0; 353 if (verbose) printf("name: %s\n", name); 354 } else if (strcmp(line, "COMMENT") == 0) { 355 commentptr += snprintf(&commentbuf[commentptr], 356 sizeof(commentbuf) - commentptr, "%s\n", arg); 357 } else if (strcmp(line, "SPACING") == 0) { 358 char spc[16]; 359 int res; 360 res = sscanf(arg, "%s", spc); 361 if (res > 0) { 362 if (verbose) printf("spacing %s\n", spc); 363 if ((spc[1] == 'P') && (force == 0)) { 364 warnx("This is a proportional font, " 365 "results are probably not suitable " 366 "for console use."); 367 errx(EXIT_FAILURE, "Use -f to override " 368 "if you want to try it anyway."); 369 } 370 } 371 } else if (strcmp(line, "FONTBOUNDINGBOX") == 0) { 372 int res; 373 res = sscanf(arg, "%d %d %d %d", 374 &width, &height, &x, &y); 375 stride = (width + 7) >> 3; 376 if (verbose) printf("box %d x %d\n", width, height); 377 if (stride > 2) { 378 errx(EXIT_FAILURE, 379 "no fonts wider than 16 work for now\n"); 380 } 381 charsize = height * stride; 382 buflen = 257 * charsize; 383 buffer = calloc(1, buflen); 384 if (buffer == NULL) { 385 err(EXIT_FAILURE, 386 "failed to allocate %dKB for glyphs\n", 387 buflen); 388 } 389 cbitmap = buffer + 256 * charsize; 390 } else if (strcmp(line, "CHARS") == 0) { 391 if (sscanf(arg, "%d", &num) == 1) 392 if (verbose) 393 printf("number of characters: %d\n", num); 394 } else if (strcmp(line, "STARTCHAR") == 0) { 395 in_char = 1; 396 if (charsize <= 1) err(EXIT_FAILURE, 397 "syntax error - no valid FONTBOUNDINGBOX\n"); 398 memset(cbitmap, 0, charsize); 399 strlcpy(charname, arg, 64); 400 if (dump && (strlen(charname) > 0)) 401 printf("name: %s\n", charname); 402 403 } else if (strcmp(line, "ENDCHAR") == 0) { 404 in_char = 0; 405 /* only commit the glyph if it's in range */ 406 if ((current >= 0) && (current < 256)) { 407 memcpy(&buffer[charsize * current], 408 cbitmap, charsize); 409 if ((strlen(charname) > 0) && 410 (charnamebufptr < 255 * 64)) { 411 char *cur; 412 int len; 413 /* copy name into buffer, keep a 414 * pointer to it for later */ 415 cur = &charnamebuf[charnamebufptr]; 416 len = strlcpy(cur, charname, 64); 417 charnamebufptr += len + 1; 418 names[current] = cur; 419 } 420 } 421 current = -1; 422 } else if (strcmp(line, "ENCODING") == 0) { 423 if (sscanf(arg, "%d", ¤t) == 1) { 424 if (current >= 0 && current < 256) { 425 if (current < first) first = current; 426 if (current > last) last = current; 427 if (dump) printf("glyph %d\n", current); 428 } 429 } 430 } else if (strcmp(line, "BBX") == 0) { 431 int cx, cy, cwi, che; 432 if (sscanf(arg, "%d %d %d %d", &cwi, &che, &cx, &cy) 433 == 4) { 434 left = cx; 435 lines = che; 436 top = height + y - che - cy; 437 if (left < bl) bl = left; 438 if (top < bt) bt = top; 439 if ((left + cwi) > br) br = left + cwi; 440 if ((top + che) > bb) bb = top + che; 441 if (dump && verbose) 442 printf("top %d left %d\n", top, left); 443 } 444 } else if (strcmp(line, "BITMAP") == 0) { 445 int i, j, k, l; 446 char num[32]; 447 char *gptr = cbitmap; 448 char *bptr = gptr + top; 449 uint16_t *bptr16 = (uint16_t *)gptr; 450 bptr16 += top; 451 /* see if the character is in range */ 452 if ((current < 0) || (current > 255)) continue; 453 /* now we read & render the character */ 454 for (i = 0; i < lines; i++) { 455 fgets(num, 32, foo); 456 sscanf(num, "%x", &l); 457 if ((stride) == 2 && (strlen(num) < 4)) 458 l = l << 8; 459 l = l >> left; 460 if (stride == 1) { 461 *bptr = l; 462 bptr++; 463 } else { 464 *bptr16 = htobe16(l); 465 bptr16++; 466 } 467 } 468 if (dump) { 469 gptr = cbitmap; 470 for (i = 0; i < height; i++) { 471 dump_line(gptr, stride); 472 gptr += stride; 473 } 474 } 475 } 476 } 477 if (verbose) { 478 printf("range %d to %d\n", first, last); 479 printf("encoding: %s\n", encname[encoding]); 480 printf("actual box: %d %d %d %d\n", bl, bt, br, bb); 481 } 482 483 /* now stuff it into a something wsfont understands */ 484 f.firstchar = first; 485 f.numchars = last - first + 1; 486 f.encoding = encoding; 487 if (fontname[0] == 0) { 488 f.name = name; 489 } else f.name = fontname; 490 f.bitorder = WSDISPLAY_FONTORDER_L2R; 491 f.byteorder = WSDISPLAY_FONTORDER_L2R; 492 493 if (scale) { 494 uint16_t *outbuf; 495 uint8_t *inbuf; 496 int i; 497 498 if (stride != 1) err(EXIT_FAILURE, 499 "scaling works only on fonts up to 8 pixels wide\n"); 500 f.fontwidth = width * 2 /*(width + 3) & ~3*/; 501 f.fontheight = height * 2; 502 f.stride = stride * 2; 503 outbuf = calloc(1, f.numchars * charsize * 4); 504 if (outbuf == NULL) err(EXIT_FAILURE, 505 "failed to allocete memory for scale buffer\n"); 506 f.data = outbuf; 507 inbuf = &buffer[first * charsize]; 508 for (i = 0; i < f.numchars; i++) { 509 double_pixels(inbuf, outbuf, charsize); 510 fill_dup(outbuf, charsize); 511 if (smoothe) smoothe_pixels(outbuf, charsize * 2); 512 inbuf += charsize; 513 outbuf += charsize * 2; 514 } 515 516 } else { 517 f.fontwidth = width /*(width + 3) & ~3*/; 518 f.fontheight = height; 519 f.stride = stride; 520 f.data = &buffer[first * charsize]; 521 } 522if (0) { 523 int i; 524 uint16_t pixbuf[16]; 525 double_pixels(&buffer[charsize * 'Q'], pixbuf, charsize); 526 fill_dup(pixbuf, charsize); 527 for (i = 0; i < charsize * 2; i++) { 528 printf("%2d: ", i); 529 dump_line((char *)&pixbuf[i], 2); 530 } 531 smoothe_pixels(pixbuf, charsize * 2); 532 for (i = 0; i < charsize * 2; i++) { 533 printf("%2d: ", i); 534 dump_line((char *)&pixbuf[i], 2); 535 } 536} 537 538 if (ofile == NULL) { 539 int fdev = open("/dev/wsfont", O_RDWR, 0); 540 if (fdev < 0) 541 err(EXIT_FAILURE, "/dev/wsfont"); 542 status = ioctl(fdev, WSDISPLAYIO_LDFONT, &f); 543 if (status != 0) 544 err(EXIT_FAILURE, "WSDISPLAYIO_LDFONT"); 545 close(fdev); 546 } 547 else { 548 if (header == 0) 549 write_wsf(ofile, &f); 550 else 551 write_header(ofile, &f); 552 } 553} 554 555__dead void 556usage() 557{ 558 fprintf(stderr, "Usage: %s [-vdhf2s] [-e encoding] [-N name] " 559 "[-o ofile.wsf] font.bdf\n", getprogname()); 560 exit(EXIT_FAILURE); 561} 562 563int 564main(int argc, char *argv[]) 565{ 566 FILE *foo; 567 const char *encname = NULL; 568 569 int c; 570 while ((c = getopt(argc, argv, "e:o:N:vdhf2s")) != -1) { 571 switch (c) { 572 573 /* font encoding */ 574 case 'e': 575 if (encname != NULL) 576 usage(); 577 encname = optarg; 578 break; 579 580 /* output file name */ 581 case 'o': 582 if (ofile != NULL) 583 usage(); 584 ofile = optarg; 585 break; 586 587 case 'v': 588 verbose = 1; 589 break; 590 591 case 'd': 592 dump = 1; 593 break; 594 595 case 'h': 596 header = 1; 597 break; 598 case 'f': 599 force = 1; 600 break; 601 case '2': 602 scale = 1; 603 break; 604 case 's': 605 smoothe = 1; 606 break; 607 case 'N': 608 strncpy(fontname, optarg, 64); 609 break; 610 case '?': /* FALLTHROUGH */ 611 default: 612 usage(); 613 } 614 } 615 616 argc -= optind; 617 argv += optind; 618 619 if (encname == NULL) { 620 encoding = WSDISPLAY_FONTENC_ISO; 621 } 622 else { 623 for (const struct encmap *e = encmap; e->name; ++e) { 624 if (strcmp(e->name, encname) == 0) { 625 encoding = e->encoding; 626 break; 627 } 628 } 629 } 630 631 /* get encoding from the bdf file? */ 632 if (encoding == -1) 633 encoding = WSDISPLAY_FONTENC_ISO; 634 635 if (argc == 0) 636 usage(); 637 638 const char *bdfname = argv[0]; 639 foo = fopen(bdfname, "r"); 640 if (foo == NULL) 641 err(EXIT_FAILURE, "%s", bdfname); 642 643 interpret(foo); 644 return EXIT_SUCCESS; 645} 646