bdfload.c revision ec211365
1/*	$NetBSD: bdfload.c,v 1.7 2022/08/16 21:52:00 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	{ "iso-8859-1",	WSDISPLAY_FONTENC_ISO },
70	{ "iso-8859-2",	WSDISPLAY_FONTENC_ISO2 },
71	{ "iso-8859-7",	WSDISPLAY_FONTENC_ISO7 },
72	{ "iso2",	WSDISPLAY_FONTENC_ISO2 },
73	{ "iso7",	WSDISPLAY_FONTENC_ISO7 },
74	{ "iso8859-1",	WSDISPLAY_FONTENC_ISO },
75	{ "iso8859-2",	WSDISPLAY_FONTENC_ISO2 },
76	{ "iso8859-7",	WSDISPLAY_FONTENC_ISO7 },
77	{ "koi8-r",	WSDISPLAY_FONTENC_KOI8_R },
78	{ "koi8r",	WSDISPLAY_FONTENC_KOI8_R },
79	{ "latin-1",	WSDISPLAY_FONTENC_ISO },
80	{ "latin-2",	WSDISPLAY_FONTENC_ISO2 },
81	{ "latin1",	WSDISPLAY_FONTENC_ISO },
82	{ "latin2",	WSDISPLAY_FONTENC_ISO2 },
83	{ "pcvt",	WSDISPLAY_FONTENC_PCVT },
84	{ NULL, -1 }
85};
86
87const char * const encname[] = {
88#define _ENC(_e) [_e] = #_e
89	_ENC(WSDISPLAY_FONTENC_ISO),
90	_ENC(WSDISPLAY_FONTENC_IBM),
91	_ENC(WSDISPLAY_FONTENC_PCVT),
92	_ENC(WSDISPLAY_FONTENC_ISO7),
93	_ENC(WSDISPLAY_FONTENC_ISO2),
94	_ENC(WSDISPLAY_FONTENC_KOI8_R),
95};
96
97
98const char *ofile = NULL;
99int encoding = -1;
100int verbose = 0;
101int dump = 0;
102
103void
104dump_line(char *gptr, int stride)
105{
106	int i, j, msk, c;
107
108	for (i = 0; i < stride; i++) {
109		c = gptr[i];
110		msk = 0x80;
111		for (j = 0; j < 8; j++) {
112			putchar((c & msk) != 0 ? '#' : ' ');
113			msk = msk >> 1;
114		}
115	}
116	printf("\n");
117}
118
119void
120interpret(FILE *foo)
121{
122	char line[128], *arg, name[64] = "foop", *buffer;
123	int buflen = -1;
124	int len, in_char = 0, current = -1, stride = 0, charsize = 0;
125	int width, height, x, y, num;
126	int first = 255, last = 0;
127	int left, top, lines;
128	int bl = 255, bt = 255, br = -1, bb = -1;
129	struct wsdisplay_font f;
130	int status;
131
132	while (fgets(line, sizeof(line), foo) != NULL) {
133		int i = 0;
134		/* separate keyword from parameters */
135		len = strlen(line);
136		while (!isspace(line[i]) && (i < len)) i++;
137		line[i] = 0;
138		arg = &line[i + 1];
139		i = 0;
140		len = strlen(arg);
141		/* get rid of garbage */
142		while ((!iscntrl(arg[i])) && (arg[i] != 0)) {
143			i++;
144		}
145		arg[i] = 0;
146		if (strcmp(line, "FAMILY_NAME") == 0) {
147			/* cut off quotation marks */
148			strncpy(name, arg + 1, 64);
149			name[strlen(name) - 1] = 0;
150			if (verbose) printf("name: %s\n", name);
151		} else if (strcmp(line, "FONTBOUNDINGBOX") == 0) {
152			int res;
153			res = sscanf(arg, "%d %d %d %d",
154					  &width, &height, &x, &y);
155			stride = (width + 7) >> 3;
156			if (verbose) printf("box %d x %d\n", width, height);
157			if (stride > 2) {
158				err(EXIT_FAILURE,
159				    "no fonts wider than 16 work for now\n");
160			}
161			charsize = height * stride;
162			buflen = 256 * charsize;
163			buffer = calloc(1, buflen);
164			if (buffer == NULL) {
165				err(EXIT_FAILURE,
166				    "failed to allocate %dKB for glyphs\n",
167				    buflen);
168			}
169		} else if (strcmp(line, "CHARS") == 0) {
170			if (sscanf(arg, "%d", &num) == 1)
171				if (verbose)
172				    printf("number of characters: %d\n", num);
173		} else if (strcmp(line, "STARTCHAR") == 0) {
174			in_char = 1;
175		} else if (strcmp(line, "ENDCHAR") == 0) {
176			in_char = 0;
177			current = -1;
178		} else if (strcmp(line, "ENCODING") == 0) {
179			if (sscanf(arg, "%d", &current) == 1) {
180				if (current >= 0 && current < 256) {
181					if (current < first) first = current;
182					if (current > last) last = current;
183					if (dump) printf("glyph %d\n", current);
184				}
185			}
186		} else if (strcmp(line, "BBX") == 0) {
187			int cx, cy, cwi, che;
188			if (sscanf(arg, "%d %d %d %d", &cwi, &che, &cx, &cy)
189			     == 4) {
190				left = cx;
191				lines = che;
192				top = height + y - che - cy;
193				if (left < bl) bl = left;
194				if (top < bt) bt = top;
195				if ((left + cwi) > br) br = left + cwi;
196				if ((top + che) > bb) bb = top + che;
197				if(dump && verbose) printf("top %d left %d\n", top, left);
198			}
199		} else if (strcmp(line, "BITMAP") == 0) {
200			int i, j, k, l;
201			char num[32];
202			char *gptr = &buffer[charsize * current];
203			char *bptr = gptr + top;
204			uint16_t *bptr16 = (uint16_t *)gptr;
205			bptr16 += top;
206			/* see if the character is in range */
207			if ((current < 0) || (current > 255)) continue;
208			/* now we read & render the character */
209			for (i = 0; i < lines; i++) {
210				fgets(num, 32, foo);
211				sscanf(num, "%x", &l);
212				if ((stride) == 2 && (strlen(num) < 4))
213					l = l << 8;
214				l = l >> left;
215				if (stride == 1) {
216					*bptr = l;
217					bptr++;
218				} else {
219					*bptr16 = htobe16(l);
220					bptr16++;
221				}
222			}
223			if (dump) {
224				gptr = &buffer[charsize * current];
225				for (i = 0; i < height; i++) {
226					dump_line(gptr, stride);
227					gptr += stride;
228				}
229			}
230		}
231	}
232	if (verbose) {
233		printf("range %d to %d\n", first, last);
234		printf("encoding: %s\n", encname[encoding]);
235		printf("actual box: %d %d %d %d\n", bl, bt, br, bb);
236	}
237
238	/* now stuff it into a something wsfont understands */
239	f.fontwidth = width /*(width + 3) & ~3*/;
240	f.fontheight = height;
241	f.firstchar = first;
242	f.numchars = last - first + 1;
243	f.stride = stride;
244	f.encoding = encoding;
245	f.name = name;
246	f.bitorder = WSDISPLAY_FONTORDER_L2R;
247	f.byteorder = WSDISPLAY_FONTORDER_L2R;
248	f.data = &buffer[first * charsize];
249
250	if (ofile == NULL) {
251		int fdev = open("/dev/wsfont", O_RDWR, 0);
252		if (fdev < 0)
253			err(EXIT_FAILURE, "/dev/wsfont");
254		status = ioctl(fdev, WSDISPLAYIO_LDFONT, &f);
255		if (status != 0)
256			err(EXIT_FAILURE, "WSDISPLAYIO_LDFONT");
257		close(fdev);
258	}
259	else {
260		struct wsfthdr h;
261
262		memset(&h, 0, sizeof(h));
263		strncpy(h.magic, "WSFT", sizeof(h.magic));
264		strncpy(h.name, f.name, sizeof(h.name));
265		h.firstchar = htole32(f.firstchar);
266		h.numchars = htole32(f.numchars);
267		h.encoding = htole32(f.encoding);
268		h.fontwidth = htole32(f.fontwidth);
269		h.fontheight = htole32(f.fontheight);
270		h.stride = htole32(f.stride);
271		h.bitorder = htole32(f.bitorder);
272		h.byteorder = htole32(f.byteorder);
273
274		int wsfd = open(ofile, O_WRONLY | O_CREAT | O_TRUNC, 0644);
275		if (wsfd < 0)
276			err(EXIT_FAILURE, "%s", ofile);
277
278		ssize_t nwritten;
279		nwritten = write(wsfd, &h, sizeof(h));
280		if (nwritten < 0)
281			err(EXIT_FAILURE, "%s", ofile);
282		if (nwritten != sizeof(h))
283			errx(EXIT_FAILURE, "%s: partial write", ofile);
284
285		nwritten = write(wsfd, buffer, buflen);
286		if (nwritten < 0)
287			err(EXIT_FAILURE, "%s", ofile);
288		if (nwritten != buflen)
289			errx(EXIT_FAILURE, "%s: partial write", ofile);
290		close(wsfd);
291	}
292}
293
294
295__dead void
296usage()
297{
298	fprintf(stderr, "usage: bdfload [-vd] [-e encoding] [-o ofile.wsf] font.bdf\n");
299	exit(EXIT_FAILURE);
300}
301
302int
303main(int argc, char *argv[])
304{
305	FILE *foo;
306	const char *encname = NULL;
307
308	int c;
309	while ((c = getopt(argc, argv, "e:o:vd")) != -1) {
310		switch (c) {
311
312		/* font encoding */
313		case 'e':
314			if (encname != NULL)
315				usage();
316			encname = optarg;
317			break;
318
319		/* output file name */
320		case 'o':
321			if (ofile != NULL)
322				usage();
323			ofile = optarg;
324			break;
325
326		case 'v':
327			verbose = 1;
328			break;
329
330		case 'd':
331			dump = 1;
332			break;
333
334		case '?':	/* FALLTHROUGH */
335		default:
336			usage();
337		}
338	}
339
340	argc -= optind;
341	argv += optind;
342
343	if (encname == NULL) {
344		encoding = WSDISPLAY_FONTENC_ISO;
345	}
346	else {
347		for (const struct encmap *e = encmap; e->name; ++e) {
348			if (strcmp(e->name, encname) == 0) {
349				encoding = e->encoding;
350				break;
351			}
352		}
353	}
354
355	/* get encoding from the bdf file? */
356	if (encoding == -1)
357		encoding = WSDISPLAY_FONTENC_ISO;
358
359	if (argc == 0)
360		usage();
361
362	const char *bdfname = argv[0];
363	foo = fopen(bdfname, "r");
364	if (foo == NULL)
365		err(EXIT_FAILURE, "%s", bdfname);
366
367	interpret(foo);
368	return EXIT_SUCCESS;
369}
370