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