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", &current) == 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