bdfload.c revision 426ebb7f
1/*	$NetBSD: bdfload.c,v 1.9 2022/08/23 18:11:43 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;
102int header = 0;
103
104void
105dump_line(char *gptr, int stride)
106{
107	int i, j, msk, c;
108
109	for (i = 0; i < stride; i++) {
110		c = gptr[i];
111		msk = 0x80;
112		for (j = 0; j < 8; j++) {
113			putchar((c & msk) != 0 ? '#' : ' ');
114			msk = msk >> 1;
115		}
116	}
117	printf("\n");
118}
119
120void
121write_wsf(const char *oname, struct wsdisplay_font *f, char *buffer, int buflen)
122{
123	struct wsfthdr h;
124
125	memset(&h, 0, sizeof(h));
126	strncpy(h.magic, "WSFT", sizeof(h.magic));
127	strncpy(h.name, f->name, sizeof(h.name));
128	h.firstchar = htole32(f->firstchar);
129	h.numchars = htole32(f->numchars);
130	h.encoding = htole32(f->encoding);
131	h.fontwidth = htole32(f->fontwidth);
132	h.fontheight = htole32(f->fontheight);
133	h.stride = htole32(f->stride);
134	h.bitorder = htole32(f->bitorder);
135	h.byteorder = htole32(f->byteorder);
136
137	int wsfd = open(ofile, O_WRONLY | O_CREAT | O_TRUNC, 0644);
138	if (wsfd < 0)
139		err(EXIT_FAILURE, "%s", ofile);
140
141	ssize_t nwritten;
142	nwritten = write(wsfd, &h, sizeof(h));
143	if (nwritten < 0)
144		err(EXIT_FAILURE, "%s", ofile);
145	if (nwritten != sizeof(h))
146		errx(EXIT_FAILURE, "%s: partial write", ofile);
147
148	nwritten = write(wsfd, buffer, buflen);
149	if (nwritten < 0)
150		err(EXIT_FAILURE, "%s", ofile);
151	if (nwritten != buflen)
152		errx(EXIT_FAILURE, "%s: partial write", ofile);
153	close(wsfd);
154}
155
156int
157write_header(const char *filename, struct wsdisplay_font *f, char *name,
158             char *buffer, int buflen)
159{
160	FILE *output;
161	int i, j, x, y, idx;
162	char fontname[64], c, msk;
163
164	/* now output as a header file */
165	snprintf(fontname, sizeof(fontname), "%s_%dx%d", name,
166	    f->fontwidth, f->fontheight);
167	for (i = 0; i < strlen(fontname); i++) {
168		if (isblank((int)fontname[i]))
169			fontname[i]='_';
170	}
171	if ((output = fopen(filename, "w")) == NULL) {
172		fprintf(stderr, "Can't open output file %s\n", filename);
173		return -1;
174	}
175	fprintf(output, "static u_char %s_data[];\n", fontname);
176	fprintf(output, "\n");
177	fprintf(output, "static struct wsdisplay_font %s = {\n", fontname);
178	fprintf(output, "\t\"%s\",\t\t\t/* typeface name */\n", name);
179	fprintf(output, "\t%d,\t\t\t\t/* firstchar */\n", f->firstchar);
180	fprintf(output, "\t%d,\t\t\t\t/* numchars */\n", f->numchars);
181	fprintf(output, "\t%d,\t\t\t\t/* encoding */\n", f->encoding);
182	fprintf(output, "\t%d,\t\t\t\t/* fontwidth */\n", f->fontwidth);
183	fprintf(output, "\t%d,\t\t\t\t/* fontheight */\n", f->fontheight);
184	fprintf(output, "\t%d,\t\t\t\t/* stride */\n", f->stride);
185	fprintf(output, "\tWSDISPLAY_FONTORDER_L2R,\t/* bit order */\n");
186	fprintf(output, "\tWSDISPLAY_FONTORDER_L2R,\t/* byte order */\n");
187	fprintf(output, "\t%s_data\t\t/* data */\n", fontname);
188	fprintf(output, "};\n\n");
189	fprintf(output, "static u_char %s_data[] = {\n", fontname);
190	for (i = f->firstchar; i < f->firstchar + f->numchars; i++) {
191		fprintf(output, "\t/* %d */\n", i);
192		idx = i * f->stride * f->fontheight;
193		for (y = 0; y < f->fontheight; y++) {
194			for (x = 0; x < f->stride; x++) {
195				fprintf(output, "0x%02x, ",buffer[idx + x]);
196			}
197			fprintf(output, "/* ");
198			for (x = 0; x < f->stride; x++) {
199				c = buffer[idx + x];
200				msk = 0x80;
201				for (j = 0; j < 8; j++) {
202					fprintf(output, "%s",
203					    (c & msk) != 0 ? "[]" : ". ");
204					msk = msk >> 1;
205				}
206			}
207			fprintf(output, " */\n");
208
209			idx += f->stride;
210		}
211	}
212	fprintf(output, "};\n");
213	fclose(output);
214	return 0;
215}
216
217void
218interpret(FILE *foo)
219{
220	char line[128], *arg, name[64] = "foop", *buffer;
221	int buflen = -1;
222	int len, in_char = 0, current = -1, stride = 0, charsize = 0;
223	int width, height, x, y, num;
224	int first = 255, last = 0;
225	int left, top, lines;
226	int bl = 255, bt = 255, br = -1, bb = -1;
227	struct wsdisplay_font f;
228	int status;
229
230	while (fgets(line, sizeof(line), foo) != NULL) {
231		int i = 0;
232		/* separate keyword from parameters */
233		len = strlen(line);
234		while (!isspace(line[i]) && (i < len)) i++;
235		line[i] = 0;
236		arg = &line[i + 1];
237		i = 0;
238		len = strlen(arg);
239		/* get rid of garbage */
240		while ((!iscntrl(arg[i])) && (arg[i] != 0)) {
241			i++;
242		}
243		arg[i] = 0;
244		if (strcmp(line, "FAMILY_NAME") == 0) {
245			/* cut off quotation marks */
246			strncpy(name, arg + 1, 64);
247			name[strlen(name) - 1] = 0;
248			if (verbose) printf("name: %s\n", name);
249		} else if (strcmp(line, "FONTBOUNDINGBOX") == 0) {
250			int res;
251			res = sscanf(arg, "%d %d %d %d",
252					  &width, &height, &x, &y);
253			stride = (width + 7) >> 3;
254			if (verbose) printf("box %d x %d\n", width, height);
255			if (stride > 2) {
256				err(EXIT_FAILURE,
257				    "no fonts wider than 16 work for now\n");
258			}
259			charsize = height * stride;
260			buflen = 256 * charsize;
261			buffer = calloc(1, buflen);
262			if (buffer == NULL) {
263				err(EXIT_FAILURE,
264				    "failed to allocate %dKB for glyphs\n",
265				    buflen);
266			}
267		} else if (strcmp(line, "CHARS") == 0) {
268			if (sscanf(arg, "%d", &num) == 1)
269				if (verbose)
270				    printf("number of characters: %d\n", num);
271		} else if (strcmp(line, "STARTCHAR") == 0) {
272			in_char = 1;
273		} else if (strcmp(line, "ENDCHAR") == 0) {
274			in_char = 0;
275			current = -1;
276		} else if (strcmp(line, "ENCODING") == 0) {
277			if (sscanf(arg, "%d", &current) == 1) {
278				if (current >= 0 && current < 256) {
279					if (current < first) first = current;
280					if (current > last) last = current;
281					if (dump) printf("glyph %d\n", current);
282				}
283			}
284		} else if (strcmp(line, "BBX") == 0) {
285			int cx, cy, cwi, che;
286			if (sscanf(arg, "%d %d %d %d", &cwi, &che, &cx, &cy)
287			     == 4) {
288				left = cx;
289				lines = che;
290				top = height + y - che - cy;
291				if (left < bl) bl = left;
292				if (top < bt) bt = top;
293				if ((left + cwi) > br) br = left + cwi;
294				if ((top + che) > bb) bb = top + che;
295				if(dump && verbose) printf("top %d left %d\n", top, left);
296			}
297		} else if (strcmp(line, "BITMAP") == 0) {
298			int i, j, k, l;
299			char num[32];
300			char *gptr = &buffer[charsize * current];
301			char *bptr = gptr + top;
302			uint16_t *bptr16 = (uint16_t *)gptr;
303			bptr16 += top;
304			/* see if the character is in range */
305			if ((current < 0) || (current > 255)) continue;
306			/* now we read & render the character */
307			for (i = 0; i < lines; i++) {
308				fgets(num, 32, foo);
309				sscanf(num, "%x", &l);
310				if ((stride) == 2 && (strlen(num) < 4))
311					l = l << 8;
312				l = l >> left;
313				if (stride == 1) {
314					*bptr = l;
315					bptr++;
316				} else {
317					*bptr16 = htobe16(l);
318					bptr16++;
319				}
320			}
321			if (dump) {
322				gptr = &buffer[charsize * current];
323				for (i = 0; i < height; i++) {
324					dump_line(gptr, stride);
325					gptr += stride;
326				}
327			}
328		}
329	}
330	if (verbose) {
331		printf("range %d to %d\n", first, last);
332		printf("encoding: %s\n", encname[encoding]);
333		printf("actual box: %d %d %d %d\n", bl, bt, br, bb);
334	}
335
336	/* now stuff it into a something wsfont understands */
337	f.fontwidth = width /*(width + 3) & ~3*/;
338	f.fontheight = height;
339	f.firstchar = first;
340	f.numchars = last - first + 1;
341	f.stride = stride;
342	f.encoding = encoding;
343	f.name = name;
344	f.bitorder = WSDISPLAY_FONTORDER_L2R;
345	f.byteorder = WSDISPLAY_FONTORDER_L2R;
346	f.data = &buffer[first * charsize];
347
348	if (ofile == NULL) {
349		int fdev = open("/dev/wsfont", O_RDWR, 0);
350		if (fdev < 0)
351			err(EXIT_FAILURE, "/dev/wsfont");
352		status = ioctl(fdev, WSDISPLAYIO_LDFONT, &f);
353		if (status != 0)
354			err(EXIT_FAILURE, "WSDISPLAYIO_LDFONT");
355		close(fdev);
356	}
357	else {
358		if (header == 0) {
359			write_wsf(ofile, &f, buffer, buflen);
360		} else
361			write_header(ofile, &f, name, buffer, buflen);
362	}
363}
364
365__dead void
366usage()
367{
368	fprintf(stderr, "usage: bdfload [-vdh] [-e encoding] [-o ofile.wsf] font.bdf\n");
369	exit(EXIT_FAILURE);
370}
371
372int
373main(int argc, char *argv[])
374{
375	FILE *foo;
376	const char *encname = NULL;
377
378	int c;
379	while ((c = getopt(argc, argv, "e:o:vdh")) != -1) {
380		switch (c) {
381
382		/* font encoding */
383		case 'e':
384			if (encname != NULL)
385				usage();
386			encname = optarg;
387			break;
388
389		/* output file name */
390		case 'o':
391			if (ofile != NULL)
392				usage();
393			ofile = optarg;
394			break;
395
396		case 'v':
397			verbose = 1;
398			break;
399
400		case 'd':
401			dump = 1;
402			break;
403
404		case 'h':
405			header = 1;
406			break;
407
408		case '?':	/* FALLTHROUGH */
409		default:
410			usage();
411		}
412	}
413
414	argc -= optind;
415	argv += optind;
416
417	if (encname == NULL) {
418		encoding = WSDISPLAY_FONTENC_ISO;
419	}
420	else {
421		for (const struct encmap *e = encmap; e->name; ++e) {
422			if (strcmp(e->name, encname) == 0) {
423				encoding = e->encoding;
424				break;
425			}
426		}
427	}
428
429	/* get encoding from the bdf file? */
430	if (encoding == -1)
431		encoding = WSDISPLAY_FONTENC_ISO;
432
433	if (argc == 0)
434		usage();
435
436	const char *bdfname = argv[0];
437	foo = fopen(bdfname, "r");
438	if (foo == NULL)
439		err(EXIT_FAILURE, "%s", bdfname);
440
441	interpret(foo);
442	return EXIT_SUCCESS;
443}
444