/* $NetBSD: bdfload.c,v 1.23 2024/03/12 09:42:55 macallan Exp $ */ /* * Copyright (c) 2018 Michael Lorenz * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ /* * a crude BDF loader for wsdisplay */ #include #include #include #include #include #include #include #include #include #include /* * wsdisplay_font but with strings embedded and integer fields in * little endian */ struct wsfthdr { char magic[4]; /* "WSFT" */ char name[64]; uint32_t firstchar; uint32_t numchars; uint32_t encoding; uint32_t fontwidth; uint32_t fontheight; uint32_t stride; uint32_t bitorder; uint32_t byteorder; }; const struct encmap { const char *name; int encoding; } encmap[] = { { "cp437", WSDISPLAY_FONTENC_IBM }, { "ibm", WSDISPLAY_FONTENC_IBM }, { "iso", WSDISPLAY_FONTENC_ISO }, { "iso8859", WSDISPLAY_FONTENC_ISO }, { "iso10646", WSDISPLAY_FONTENC_ISO }, { "iso-8859-1", WSDISPLAY_FONTENC_ISO }, { "iso-8859-2", WSDISPLAY_FONTENC_ISO2 }, { "iso-8859-7", WSDISPLAY_FONTENC_ISO7 }, { "iso2", WSDISPLAY_FONTENC_ISO2 }, { "iso7", WSDISPLAY_FONTENC_ISO7 }, { "iso8859-1", WSDISPLAY_FONTENC_ISO }, { "iso8859-2", WSDISPLAY_FONTENC_ISO2 }, { "iso8859-7", WSDISPLAY_FONTENC_ISO7 }, { "koi8-r", WSDISPLAY_FONTENC_KOI8_R }, { "koi8r", WSDISPLAY_FONTENC_KOI8_R }, { "latin-1", WSDISPLAY_FONTENC_ISO }, { "latin-2", WSDISPLAY_FONTENC_ISO2 }, { "latin1", WSDISPLAY_FONTENC_ISO }, { "latin2", WSDISPLAY_FONTENC_ISO2 }, { "pcvt", WSDISPLAY_FONTENC_PCVT }, { NULL, -1 } }; const char * const encname[] = { #define _ENC(_e) [_e] = #_e _ENC(WSDISPLAY_FONTENC_ISO), _ENC(WSDISPLAY_FONTENC_IBM), _ENC(WSDISPLAY_FONTENC_PCVT), _ENC(WSDISPLAY_FONTENC_ISO7), _ENC(WSDISPLAY_FONTENC_ISO2), _ENC(WSDISPLAY_FONTENC_KOI8_R), }; const char *ofile = NULL; int encoding = -1; int verbose = 0; int dump = 0; int header = 0; int force = 0; int scale = 0; int smoothe = 0; char commentbuf[2048] = ""; int commentptr = 0; char fontname[64] = ""; char *names[256]; void dump_line(char *gptr, int stride) { int i, j, msk, c; for (i = 0; i < stride; i++) { c = gptr[i]; msk = 0x80; for (j = 0; j < 8; j++) { putchar((c & msk) != 0 ? '#' : ' '); msk = msk >> 1; } } printf("\n"); } void write_wsf(const char *oname, struct wsdisplay_font *f) { struct wsfthdr h; uint8_t *buffer = f->data; int buflen = f->numchars * f->stride * f->fontheight; memset(&h, 0, sizeof(h)); strncpy(h.magic, "WSFT", sizeof(h.magic)); strncpy(h.name, f->name, sizeof(h.name)); h.firstchar = htole32(f->firstchar); h.numchars = htole32(f->numchars); h.encoding = htole32(f->encoding); h.fontwidth = htole32(f->fontwidth); h.fontheight = htole32(f->fontheight); h.stride = htole32(f->stride); h.bitorder = htole32(f->bitorder); h.byteorder = htole32(f->byteorder); int wsfd = open(ofile, O_WRONLY | O_CREAT | O_TRUNC, 0644); if (wsfd < 0) err(EXIT_FAILURE, "%s", ofile); ssize_t nwritten; nwritten = write(wsfd, &h, sizeof(h)); if (nwritten < 0) err(EXIT_FAILURE, "%s", ofile); if (nwritten != sizeof(h)) errx(EXIT_FAILURE, "%s: partial write", ofile); nwritten = write(wsfd, buffer, buflen); if (nwritten < 0) err(EXIT_FAILURE, "%s", ofile); if (nwritten != buflen) errx(EXIT_FAILURE, "%s: partial write", ofile); close(wsfd); } int write_header(const char *filename, struct wsdisplay_font *f) { FILE *output; uint8_t *buffer = f->data; uint8_t c, msk; int i, j, x, y, idx, pxls, left; char name[64]; /* now output as a header file */ snprintf(name, sizeof(name), "%s_%dx%d", f->name, f->fontwidth, f->fontheight); for (i = 0; i < strlen(name); i++) { if (isblank((unsigned char)name[i])) name[i] = '_'; } if ((output = fopen(filename, "w")) == NULL) { warn("Can't open output file `%s'", filename); return -1; } if (commentptr > 0) { fprintf(output, "/*\n"); fputs(commentbuf, output); fprintf(output, "*/\n\n"); } fprintf(output, "static u_char %s_data[];\n", name); fprintf(output, "\n"); fprintf(output, "static struct wsdisplay_font %s = {\n", name); fprintf(output, "\t\"%s\",\t\t\t/* typeface name */\n", f->name); fprintf(output, "\t%d,\t\t\t\t/* firstchar */\n", f->firstchar); fprintf(output, "\t%d,\t\t\t\t/* numchars */\n", f->numchars); fprintf(output, "\t%d,\t\t\t\t/* encoding */\n", f->encoding); fprintf(output, "\t%d,\t\t\t\t/* fontwidth */\n", f->fontwidth); fprintf(output, "\t%d,\t\t\t\t/* fontheight */\n", f->fontheight); fprintf(output, "\t%d,\t\t\t\t/* stride */\n", f->stride); fprintf(output, "\tWSDISPLAY_FONTORDER_L2R,\t/* bit order */\n"); fprintf(output, "\tWSDISPLAY_FONTORDER_L2R,\t/* byte order */\n"); fprintf(output, "\t%s_data\t\t/* data */\n", name); fprintf(output, "};\n\n"); fprintf(output, "static u_char %s_data[] = {\n", name); for (i = 0; i < f->numchars; i++) { if (names[i] != NULL) { fprintf(output, "\t/* %d %s */\n", i + f->firstchar, names[i]); } else fprintf(output, "\t/* %d */\n", i + f->firstchar); idx = i * f->stride * f->fontheight; for (y = 0; y < f->fontheight; y++) { for (x = 0; x < f->stride; x++) { fprintf(output, "0x%02x, ",buffer[idx + x]); } fprintf(output, "/* "); pxls = f->fontwidth; for (x = 0; x < f->stride; x++) { c = buffer[idx + x]; msk = 0x80; left = pxls > 8 ? 8 : pxls; for (j = 0; j < left; j++) { fprintf(output, "%s", (c & msk) != 0 ? "[]" : ". "); msk = msk >> 1; } pxls -= 8; } fprintf(output, " */\n"); idx += f->stride; } } fprintf(output, "};\n"); fclose(output); return 0; } void double_pixels(uint8_t *inbuf, uint16_t *outbuf, int bytes) { int i, j; uint16_t outmask, out; uint8_t in, inmask; for (i = 0; i < bytes; i++) { inmask = 0x80; outmask = 0xc000; out = 0; in = inbuf[i]; for (j = 0; j < 8; j++) { if (in & inmask) { out |= outmask; } inmask = inmask >> 1; outmask = outmask >> 2; } outbuf[i * 2] = htobe16(out); } } void fill_dup(uint16_t *buf, int lines) { int i; for (i = 0; i < lines; i++) { buf[2 * i + 1] = buf[2 * i]; } } void smoothe_pixels(uint16_t *buf, int lines) { int i, j, topright, topleft, botright, botleft; uint16_t pmask, in, prev, next, out; for (i = 0; i < lines; i++) { pmask = 0xc000; in = be16toh(buf[i]); out = in; prev = next = 0; if (i > 1) prev = be16toh(buf[i - 2]); if (i < (lines - 2)) next = be16toh(buf[i + 2]); for (j = 0; j < 8; j++) { if ((in & pmask) == 0) { /* empty pixel, check surroundings */ topright = topleft = botright = botleft = 0; if (((i & 1) == 0) && (j < 6)) topright = (((prev & pmask) == pmask) && ((prev & (pmask >> 2)) != 0) && ((in & (pmask >> 2)) != 0)); if (((i & 1) == 0) && (j > 0)) topleft = (((prev & pmask) == pmask) && ((prev & (pmask << 2)) != 0) && ((in & (pmask << 2)) != 0)); if ((i & 1) && (j < 6)) botright = (((next & pmask) == pmask) && ((next & (pmask >> 2)) != 0) && ((in & (pmask >> 2)) != 0)); if ((i & 1) && (j > 0)) botleft = (((next & pmask) == pmask) && ((next & (pmask << 2)) != 0) && ((in & (pmask << 2)) != 0)); if ((topright + topleft + botright + botleft) == 1) { if (topleft || botleft) out |= pmask << 1; if (topright || botright) out |= pmask >> 1; } } pmask = pmask >> 2; } buf[i] = htobe16(out); } } void interpret(FILE *foo) { char line[128], *arg, name[64] = "foo", *buffer, *cbitmap; char charname[65], *charnamebuf; int buflen = -1, charnamebufptr = 0, j; int in_char = 0, current = -1, stride = 0, charsize = 0; int width, height, x, y, num; int first = 255, last = 0; int left, top, lines; int bl = 255, bt = 255, br = -1, bb = -1; struct wsdisplay_font f; int status; charnamebuf = malloc(64 * 256); if (charnamebuf == 0) err(EXIT_FAILURE, "failed to allocate memory\n"); memset(charnamebuf, 0, 64 * 256); for (j = 0; j < 256; j++) names[j] = NULL; while (fgets(line, sizeof(line), foo) != NULL) { size_t i = 0, len; /* separate keyword from parameters */ len = strlen(line); while (!isspace((unsigned char)line[i]) && i < len) i++; line[i] = 0; arg = &line[i + 1]; i = 0; len = strlen(arg); /* get rid of garbage */ while ((!iscntrl((unsigned char)arg[i])) && (arg[i] != 0)) { i++; } arg[i] = 0; if (strcmp(line, "FAMILY_NAME") == 0) { char *q; /* cut off quotation marks */ strlcpy(name, arg + 1, 64); /* remove trailing " */ if ((q = strnstr(name, "\"", 64)) != NULL) *q = 0; if (verbose) printf("name: %s\n", name); } else if (strcmp(line, "COMMENT") == 0) { commentptr += snprintf(&commentbuf[commentptr], sizeof(commentbuf) - commentptr, "%s\n", arg); } else if (strcmp(line, "SPACING") == 0) { char spc[16]; int res; res = sscanf(arg, "%s", spc); if (res > 0) { if (verbose) printf("spacing %s\n", spc); if ((spc[1] == 'P') && (force == 0)) { warnx("This is a proportional font, " "results are probably not suitable " "for console use."); errx(EXIT_FAILURE, "Use -f to override " "if you want to try it anyway."); } } } else if (strcmp(line, "FONTBOUNDINGBOX") == 0) { int res; res = sscanf(arg, "%d %d %d %d", &width, &height, &x, &y); stride = (width + 7) >> 3; if (verbose) printf("box %d x %d\n", width, height); if (stride > 2) { errx(EXIT_FAILURE, "no fonts wider than 16 work for now\n"); } charsize = height * stride; buflen = 257 * charsize; buffer = calloc(1, buflen); if (buffer == NULL) { err(EXIT_FAILURE, "failed to allocate %dKB for glyphs\n", buflen); } cbitmap = buffer + 256 * charsize; } else if (strcmp(line, "CHARS") == 0) { if (sscanf(arg, "%d", &num) == 1) if (verbose) printf("number of characters: %d\n", num); } else if (strcmp(line, "STARTCHAR") == 0) { in_char = 1; if (charsize <= 1) err(EXIT_FAILURE, "syntax error - no valid FONTBOUNDINGBOX\n"); memset(cbitmap, 0, charsize); strlcpy(charname, arg, 64); if (dump && (strlen(charname) > 0)) printf("name: %s\n", charname); } else if (strcmp(line, "ENDCHAR") == 0) { in_char = 0; /* only commit the glyph if it's in range */ if ((current >= 0) && (current < 256)) { memcpy(&buffer[charsize * current], cbitmap, charsize); if ((strlen(charname) > 0) && (charnamebufptr < 255 * 64)) { char *cur; int len; /* copy name into buffer, keep a * pointer to it for later */ cur = &charnamebuf[charnamebufptr]; len = strlcpy(cur, charname, 64); charnamebufptr += len + 1; names[current] = cur; } } current = -1; } else if (strcmp(line, "ENCODING") == 0) { if (sscanf(arg, "%d", ¤t) == 1) { if (current >= 0 && current < 256) { if (current < first) first = current; if (current > last) last = current; if (dump) printf("glyph %d\n", current); } } } else if (strcmp(line, "BBX") == 0) { int cx, cy, cwi, che; if (sscanf(arg, "%d %d %d %d", &cwi, &che, &cx, &cy) == 4) { left = cx; lines = che; top = height + y - che - cy; if (left < bl) bl = left; if (top < bt) bt = top; if ((left + cwi) > br) br = left + cwi; if ((top + che) > bb) bb = top + che; if (dump && verbose) printf("top %d left %d\n", top, left); } } else if (strcmp(line, "BITMAP") == 0) { int i, j, k, l; char num[32]; char *gptr = cbitmap; char *bptr = gptr + top; uint16_t *bptr16 = (uint16_t *)gptr; bptr16 += top; /* see if the character is in range */ if ((current < 0) || (current > 255)) continue; /* now we read & render the character */ for (i = 0; i < lines; i++) { fgets(num, 32, foo); sscanf(num, "%x", &l); if ((stride) == 2 && (strlen(num) < 4)) l = l << 8; l = l >> left; if (stride == 1) { *bptr = l; bptr++; } else { *bptr16 = htobe16(l); bptr16++; } } if (dump) { gptr = cbitmap; for (i = 0; i < height; i++) { dump_line(gptr, stride); gptr += stride; } } } } if (verbose) { printf("range %d to %d\n", first, last); printf("encoding: %s\n", encname[encoding]); printf("actual box: %d %d %d %d\n", bl, bt, br, bb); } /* now stuff it into a something wsfont understands */ f.firstchar = first; f.numchars = last - first + 1; f.encoding = encoding; if (fontname[0] == 0) { f.name = name; } else f.name = fontname; f.bitorder = WSDISPLAY_FONTORDER_L2R; f.byteorder = WSDISPLAY_FONTORDER_L2R; if (scale) { uint16_t *outbuf; uint8_t *inbuf; int i; if (stride != 1) err(EXIT_FAILURE, "scaling works only on fonts up to 8 pixels wide\n"); f.fontwidth = width * 2 /*(width + 3) & ~3*/; f.fontheight = height * 2; f.stride = stride * 2; outbuf = calloc(1, f.numchars * charsize * 4); if (outbuf == NULL) err(EXIT_FAILURE, "failed to allocete memory for scale buffer\n"); f.data = outbuf; inbuf = &buffer[first * charsize]; for (i = 0; i < f.numchars; i++) { double_pixels(inbuf, outbuf, charsize); fill_dup(outbuf, charsize); if (smoothe) smoothe_pixels(outbuf, charsize * 2); inbuf += charsize; outbuf += charsize * 2; } } else { f.fontwidth = width /*(width + 3) & ~3*/; f.fontheight = height; f.stride = stride; f.data = &buffer[first * charsize]; } if (ofile == NULL) { int fdev = open("/dev/wsfont", O_RDWR, 0); if (fdev < 0) err(EXIT_FAILURE, "/dev/wsfont"); status = ioctl(fdev, WSDISPLAYIO_LDFONT, &f); if (status != 0) err(EXIT_FAILURE, "WSDISPLAYIO_LDFONT"); close(fdev); } else { if (header == 0) write_wsf(ofile, &f); else write_header(ofile, &f); } } __dead void usage() { fprintf(stderr, "Usage: %s [-vdhf2s] [-e encoding] [-N name] " "[-o ofile.wsf] font.bdf\n", getprogname()); exit(EXIT_FAILURE); } int main(int argc, char *argv[]) { FILE *foo; const char *encname = NULL; int c; while ((c = getopt(argc, argv, "e:o:N:vdhf2s")) != -1) { switch (c) { /* font encoding */ case 'e': if (encname != NULL) usage(); encname = optarg; break; /* output file name */ case 'o': if (ofile != NULL) usage(); ofile = optarg; break; case 'v': verbose = 1; break; case 'd': dump = 1; break; case 'h': header = 1; break; case 'f': force = 1; break; case '2': scale = 1; break; case 's': smoothe = 1; break; case 'N': strncpy(fontname, optarg, 64); break; case '?': /* FALLTHROUGH */ default: usage(); } } argc -= optind; argv += optind; if (encname == NULL) { encoding = WSDISPLAY_FONTENC_ISO; } else { for (const struct encmap *e = encmap; e->name; ++e) { if (strcmp(e->name, encname) == 0) { encoding = e->encoding; break; } } } /* get encoding from the bdf file? */ if (encoding == -1) encoding = WSDISPLAY_FONTENC_ISO; if (argc == 0) usage(); const char *bdfname = argv[0]; foo = fopen(bdfname, "r"); if (foo == NULL) err(EXIT_FAILURE, "%s", bdfname); interpret(foo); return EXIT_SUCCESS; }