bdfload.c revision 682eda17
1/* $NetBSD: bdfload.c,v 1.19 2022/10/25 13:36:35 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; 106char commentbuf[2048] = ""; 107int commentptr = 0; 108char fontname[64] = ""; 109char *names[256]; 110 111void 112dump_line(char *gptr, int stride) 113{ 114 int i, j, msk, c; 115 116 for (i = 0; i < stride; i++) { 117 c = gptr[i]; 118 msk = 0x80; 119 for (j = 0; j < 8; j++) { 120 putchar((c & msk) != 0 ? '#' : ' '); 121 msk = msk >> 1; 122 } 123 } 124 printf("\n"); 125} 126 127void 128write_wsf(const char *oname, struct wsdisplay_font *f, char *buffer, int buflen) 129{ 130 struct wsfthdr h; 131 132 memset(&h, 0, sizeof(h)); 133 strncpy(h.magic, "WSFT", sizeof(h.magic)); 134 strncpy(h.name, f->name, sizeof(h.name)); 135 h.firstchar = htole32(f->firstchar); 136 h.numchars = htole32(f->numchars); 137 h.encoding = htole32(f->encoding); 138 h.fontwidth = htole32(f->fontwidth); 139 h.fontheight = htole32(f->fontheight); 140 h.stride = htole32(f->stride); 141 h.bitorder = htole32(f->bitorder); 142 h.byteorder = htole32(f->byteorder); 143 144 int wsfd = open(ofile, O_WRONLY | O_CREAT | O_TRUNC, 0644); 145 if (wsfd < 0) 146 err(EXIT_FAILURE, "%s", ofile); 147 148 ssize_t nwritten; 149 nwritten = write(wsfd, &h, sizeof(h)); 150 if (nwritten < 0) 151 err(EXIT_FAILURE, "%s", ofile); 152 if (nwritten != sizeof(h)) 153 errx(EXIT_FAILURE, "%s: partial write", ofile); 154 155 nwritten = write(wsfd, buffer, buflen); 156 if (nwritten < 0) 157 err(EXIT_FAILURE, "%s", ofile); 158 if (nwritten != buflen) 159 errx(EXIT_FAILURE, "%s: partial write", ofile); 160 close(wsfd); 161} 162 163int 164write_header(const char *filename, struct wsdisplay_font *f, 165 char *buffer, int buflen) 166{ 167 FILE *output; 168 int i, j, x, y, idx, pxls, left; 169 char name[64], c, msk; 170 171 /* now output as a header file */ 172 snprintf(name, sizeof(name), "%s_%dx%d", f->name, 173 f->fontwidth, f->fontheight); 174 for (i = 0; i < strlen(name); i++) { 175 if (isblank((unsigned char)name[i])) 176 name[i] = '_'; 177 } 178 if ((output = fopen(filename, "w")) == NULL) { 179 warn("Can't open output file `%s'", filename); 180 return -1; 181 } 182 if (commentptr > 0) { 183 fprintf(output, "/*\n"); 184 fputs(commentbuf, output); 185 fprintf(output, "*/\n\n"); 186 } 187 188 fprintf(output, "static u_char %s_data[];\n", name); 189 fprintf(output, "\n"); 190 fprintf(output, "static struct wsdisplay_font %s = {\n", name); 191 fprintf(output, "\t\"%s\",\t\t\t/* typeface name */\n", f->name); 192 fprintf(output, "\t%d,\t\t\t\t/* firstchar */\n", f->firstchar); 193 fprintf(output, "\t%d,\t\t\t\t/* numchars */\n", f->numchars); 194 fprintf(output, "\t%d,\t\t\t\t/* encoding */\n", f->encoding); 195 fprintf(output, "\t%d,\t\t\t\t/* fontwidth */\n", f->fontwidth); 196 fprintf(output, "\t%d,\t\t\t\t/* fontheight */\n", f->fontheight); 197 fprintf(output, "\t%d,\t\t\t\t/* stride */\n", f->stride); 198 fprintf(output, "\tWSDISPLAY_FONTORDER_L2R,\t/* bit order */\n"); 199 fprintf(output, "\tWSDISPLAY_FONTORDER_L2R,\t/* byte order */\n"); 200 fprintf(output, "\t%s_data\t\t/* data */\n", name); 201 fprintf(output, "};\n\n"); 202 fprintf(output, "static u_char %s_data[] = {\n", name); 203 for (i = f->firstchar; i < f->firstchar + f->numchars; i++) { 204 if (names[i] != NULL) { 205 fprintf(output, "\t/* %d %s */\n", i, names[i]); 206 } else 207 fprintf(output, "\t/* %d */\n", i); 208 idx = i * f->stride * f->fontheight; 209 for (y = 0; y < f->fontheight; y++) { 210 for (x = 0; x < f->stride; x++) { 211 fprintf(output, "0x%02x, ",buffer[idx + x]); 212 } 213 fprintf(output, "/* "); 214 pxls = f->fontwidth; 215 for (x = 0; x < f->stride; x++) { 216 c = buffer[idx + x]; 217 msk = 0x80; 218 left = pxls > 8 ? 8 : pxls; 219 for (j = 0; j < left; j++) { 220 fprintf(output, "%s", 221 (c & msk) != 0 ? "[]" : ". "); 222 msk = msk >> 1; 223 } 224 pxls -= 8; 225 } 226 fprintf(output, " */\n"); 227 228 idx += f->stride; 229 } 230 } 231 fprintf(output, "};\n"); 232 fclose(output); 233 return 0; 234} 235 236void 237interpret(FILE *foo) 238{ 239 char line[128], *arg, name[64] = "foo", *buffer, *cbitmap; 240 char charname[65], *charnamebuf; 241 int buflen = -1, charnamebufptr = 0, j; 242 int in_char = 0, current = -1, stride = 0, charsize = 0; 243 int width, height, x, y, num; 244 int first = 255, last = 0; 245 int left, top, lines; 246 int bl = 255, bt = 255, br = -1, bb = -1; 247 struct wsdisplay_font f; 248 int status; 249 250 charnamebuf = malloc(64 * 256); 251 if (charnamebuf == 0) err(EXIT_FAILURE, "failed to allocate memory\n"); 252 memset(charnamebuf, 0, 64 * 256); 253 for (j = 0; j < 256; j++) names[j] = NULL; 254 255 while (fgets(line, sizeof(line), foo) != NULL) { 256 size_t i = 0, len; 257 /* separate keyword from parameters */ 258 len = strlen(line); 259 while (!isspace((unsigned char)line[i]) && i < len) i++; 260 line[i] = 0; 261 arg = &line[i + 1]; 262 i = 0; 263 len = strlen(arg); 264 /* get rid of garbage */ 265 while ((!iscntrl((unsigned char)arg[i])) && (arg[i] != 0)) { 266 i++; 267 } 268 arg[i] = 0; 269 if (strcmp(line, "FAMILY_NAME") == 0) { 270 char *q; 271 /* cut off quotation marks */ 272 strlcpy(name, arg + 1, 64); 273 /* remove trailing " */ 274 if ((q = strnstr(name, "\"", 64)) != NULL) 275 *q = 0; 276 if (verbose) printf("name: %s\n", name); 277 } else if (strcmp(line, "COMMENT") == 0) { 278 commentptr += snprintf(&commentbuf[commentptr], 279 sizeof(commentbuf) - commentptr, "%s\n", arg); 280 } else if (strcmp(line, "SPACING") == 0) { 281 char spc[16]; 282 int res; 283 res = sscanf(arg, "%s", spc); 284 if (res > 0) { 285 if (verbose) printf("spacing %s\n", spc); 286 if ((spc[1] == 'P') && (force == 0)) { 287 warnx("This is a proportional font, " 288 "results are probably not suitable " 289 "for console use."); 290 errx(EXIT_FAILURE, "Use -f to override " 291 "if you want to try it anyway."); 292 } 293 } 294 } else if (strcmp(line, "FONTBOUNDINGBOX") == 0) { 295 int res; 296 res = sscanf(arg, "%d %d %d %d", 297 &width, &height, &x, &y); 298 stride = (width + 7) >> 3; 299 if (verbose) printf("box %d x %d\n", width, height); 300 if (stride > 2) { 301 errx(EXIT_FAILURE, 302 "no fonts wider than 16 work for now\n"); 303 } 304 charsize = height * stride; 305 buflen = 257 * charsize; 306 buffer = calloc(1, buflen); 307 if (buffer == NULL) { 308 err(EXIT_FAILURE, 309 "failed to allocate %dKB for glyphs\n", 310 buflen); 311 } 312 cbitmap = buffer + 256 * charsize; 313 } else if (strcmp(line, "CHARS") == 0) { 314 if (sscanf(arg, "%d", &num) == 1) 315 if (verbose) 316 printf("number of characters: %d\n", num); 317 } else if (strcmp(line, "STARTCHAR") == 0) { 318 in_char = 1; 319 if (charsize <= 1) err(EXIT_FAILURE, 320 "syntax error - no valid FONTBOUNDINGBOX\n"); 321 memset(cbitmap, 0, charsize); 322 strlcpy(charname, arg, 64); 323 } else if (strcmp(line, "ENDCHAR") == 0) { 324 in_char = 0; 325 /* only commit the glyph if it's in range */ 326 if ((current >= 0) && (current < 256)) { 327 memcpy(&buffer[charsize * current], 328 cbitmap, charsize); 329 if ((strlen(charname) > 0) && 330 (charnamebufptr < 255 * 64)) { 331 char *cur; 332 int len; 333 /* copy name into buffer, keep a 334 * pointer to it for later */ 335 cur = &charnamebuf[charnamebufptr]; 336 len = strlcpy(cur, charname, 64); 337 charnamebufptr += len + 1; 338 names[current] = cur; 339 } 340 } 341 current = -1; 342 } else if (strcmp(line, "ENCODING") == 0) { 343 if (sscanf(arg, "%d", ¤t) == 1) { 344 if (current >= 0 && current < 256) { 345 if (current < first) first = current; 346 if (current > last) last = current; 347 if (dump) printf("glyph %d\n", current); 348 } 349 } 350 } else if (strcmp(line, "BBX") == 0) { 351 int cx, cy, cwi, che; 352 if (sscanf(arg, "%d %d %d %d", &cwi, &che, &cx, &cy) 353 == 4) { 354 left = cx; 355 lines = che; 356 top = height + y - che - cy; 357 if (left < bl) bl = left; 358 if (top < bt) bt = top; 359 if ((left + cwi) > br) br = left + cwi; 360 if ((top + che) > bb) bb = top + che; 361 if (dump && verbose) 362 printf("top %d left %d\n", top, left); 363 } 364 } else if (strcmp(line, "BITMAP") == 0) { 365 int i, j, k, l; 366 char num[32]; 367 char *gptr = cbitmap; 368 char *bptr = gptr + top; 369 uint16_t *bptr16 = (uint16_t *)gptr; 370 bptr16 += top; 371 /* see if the character is in range */ 372 if ((current < 0) || (current > 255)) continue; 373 /* now we read & render the character */ 374 for (i = 0; i < lines; i++) { 375 fgets(num, 32, foo); 376 sscanf(num, "%x", &l); 377 if ((stride) == 2 && (strlen(num) < 4)) 378 l = l << 8; 379 l = l >> left; 380 if (stride == 1) { 381 *bptr = l; 382 bptr++; 383 } else { 384 *bptr16 = htobe16(l); 385 bptr16++; 386 } 387 } 388 if (dump) { 389 gptr = cbitmap; 390 for (i = 0; i < height; i++) { 391 dump_line(gptr, stride); 392 gptr += stride; 393 } 394 } 395 } 396 } 397 if (verbose) { 398 printf("range %d to %d\n", first, last); 399 printf("encoding: %s\n", encname[encoding]); 400 printf("actual box: %d %d %d %d\n", bl, bt, br, bb); 401 } 402 403 /* now stuff it into a something wsfont understands */ 404 f.fontwidth = width /*(width + 3) & ~3*/; 405 f.fontheight = height; 406 f.firstchar = first; 407 f.numchars = last - first + 1; 408 f.stride = stride; 409 f.encoding = encoding; 410 if (fontname[0] == 0) { 411 f.name = name; 412 } else f.name = fontname; 413 f.bitorder = WSDISPLAY_FONTORDER_L2R; 414 f.byteorder = WSDISPLAY_FONTORDER_L2R; 415 f.data = &buffer[first * charsize]; 416 417 if (ofile == NULL) { 418 int fdev = open("/dev/wsfont", O_RDWR, 0); 419 if (fdev < 0) 420 err(EXIT_FAILURE, "/dev/wsfont"); 421 status = ioctl(fdev, WSDISPLAYIO_LDFONT, &f); 422 if (status != 0) 423 err(EXIT_FAILURE, "WSDISPLAYIO_LDFONT"); 424 close(fdev); 425 } 426 else { 427 if (header == 0) 428 write_wsf(ofile, &f, buffer, buflen); 429 else 430 write_header(ofile, &f, buffer, buflen); 431 } 432} 433 434__dead void 435usage() 436{ 437 fprintf(stderr, "Usage: %s [-vdhf] [-e encoding] [-N name] " 438 "[-o ofile.wsf] font.bdf\n", getprogname()); 439 exit(EXIT_FAILURE); 440} 441 442int 443main(int argc, char *argv[]) 444{ 445 FILE *foo; 446 const char *encname = NULL; 447 448 int c; 449 while ((c = getopt(argc, argv, "e:o:N:vdhf")) != -1) { 450 switch (c) { 451 452 /* font encoding */ 453 case 'e': 454 if (encname != NULL) 455 usage(); 456 encname = optarg; 457 break; 458 459 /* output file name */ 460 case 'o': 461 if (ofile != NULL) 462 usage(); 463 ofile = optarg; 464 break; 465 466 case 'v': 467 verbose = 1; 468 break; 469 470 case 'd': 471 dump = 1; 472 break; 473 474 case 'h': 475 header = 1; 476 break; 477 case 'f': 478 force = 1; 479 break; 480 case 'N': 481 strncpy(fontname, optarg, 64); 482 break; 483 case '?': /* FALLTHROUGH */ 484 default: 485 usage(); 486 } 487 } 488 489 argc -= optind; 490 argv += optind; 491 492 if (encname == NULL) { 493 encoding = WSDISPLAY_FONTENC_ISO; 494 } 495 else { 496 for (const struct encmap *e = encmap; e->name; ++e) { 497 if (strcmp(e->name, encname) == 0) { 498 encoding = e->encoding; 499 break; 500 } 501 } 502 } 503 504 /* get encoding from the bdf file? */ 505 if (encoding == -1) 506 encoding = WSDISPLAY_FONTENC_ISO; 507 508 if (argc == 0) 509 usage(); 510 511 const char *bdfname = argv[0]; 512 foo = fopen(bdfname, "r"); 513 if (foo == NULL) 514 err(EXIT_FAILURE, "%s", bdfname); 515 516 interpret(foo); 517 return EXIT_SUCCESS; 518} 519