main uint/cterm / source / font.c
  1#include <ctype.h>
  2#include <stdint.h>
  3#include <stdio.h>
  4#include <stdlib.h>
  5#include <string.h>
  6
  7#include "font.h"
  8
  9static int load_bdf(Fontface* f, const char* path);
 10static int strcicmp(char const* a, char const* b);
 11static bool is_bdf(const char* path);
 12
 13/* load a bdf font into `f`,  -1 on fail, 0 on success */
 14static int load_bdf(Fontface* f, const char* path)
 15{
 16	FILE* fp;
 17	fp = fopen(path, "r");
 18	if (!fp)
 19		return -1;
 20
 21	char line[1024];
 22	int asc = -1;
 23	int dsc = -1;
 24	int encoding = -1;
 25	int advance = 0;
 26	int bw = 0; /* bmp width */
 27	int bh = 0; /* bmp height */
 28	int xoff = 0;
 29	int yoff = 0;
 30	int seen_any_glyph = 0;
 31
 32	while (fgets(line, sizeof(line), fp)) {
 33		if (sscanf(line, "FONT_ASCENT %d", &asc) == 1)
 34			continue;
 35		if (sscanf(line, "FONT_DESCENT %d", &dsc) == 1)
 36			continue;
 37
 38		/* ignore everything until glyphs start */
 39		if (strncmp(line, "STARTCHAR", 9) != 0)
 40			continue;
 41
 42		/* reset vaules for new glyph */
 43		encoding = -1;
 44		advance = 0;
 45		bw = 0;
 46		bh = 0;
 47		xoff = 0;
 48		yoff = 0;
 49
 50		while (fgets(line, sizeof(line), fp)) {
 51			if (sscanf(line, "ENCODING %d", &encoding) == 1)
 52				continue;
 53			if (sscanf(line, "DWIDTH %d", &advance) == 1)
 54				continue;
 55			if (sscanf(line, "BBX %d %d %d %d",
 56			    &bw, &bh, &xoff, &yoff) == 4)
 57				continue;
 58
 59			if (strncmp(line, "BITMAP", 6) == 0) {
 60				BdfGlyph* g;
 61				int rowbits;
 62
 63				/* invalid character code 
 64				   TODO: just skip it
 65				 */
 66				if (encoding < 0 || encoding >= BDF_GLYPHS_MAX)
 67					break;
 68
 69				/* empty/invalid size */
 70				if (bw <= 0 || bh <= 0)
 71					break;
 72
 73				g = &f->bdf[encoding];
 74				free(g->bmp); /* prevent leak on reset */
 75				memset(g, 0, sizeof(*g));
 76
 77				g->w = bw;
 78				g->h = bh;
 79				g->xoff = xoff;
 80				g->yoff = yoff;
 81				g->adv = advance;
 82
 83				g->bmp = calloc((size_t)bw * bh, 1);
 84				if (!g->bmp) {
 85					fclose(fp);
 86					font_free(f);
 87					return -1;
 88				}
 89
 90				/* each bmp row in BDF is padded as a multiple
 91				   of 8 bits. this rounds width up to the next
 92				   multiple of 8 */
 93				rowbits = (bw + 7) & ~7;
 94
 95				for (int y = 0; y < bh; y++) {
 96					uint64_t bits;
 97
 98					/* missing row */
 99					if (!fgets(line, sizeof(line), fp)) {
100						fclose(fp);
101						font_free(f);
102						return -1;
103					}
104
105					bits = strtoull(line, NULL, 16); /* hex to bits */
106					for (int x = 0; x < bw; x++) {
107						int bit = rowbits - 1 - x;
108
109						if ((bits >> bit) & 1)
110							g->bmp[y * bw + x] = 255; /* solid */
111					}
112				}
113
114				g->valid = 1;
115				if (advance > f->cellw)
116					f->cellw = advance;
117				seen_any_glyph = 1;
118
119				continue;
120			}
121
122			if (strncmp(line, "ENDCHAR", 7) == 0)
123				break;
124		}
125	}
126
127	fclose(fp);
128
129	if (!seen_any_glyph)
130		return -1;
131
132	/* estimate asc/dsc if they werent provided */
133	if (asc < 0 || dsc < 0) {
134		asc = 0;
135		dsc = 0;
136
137		for (int i = 0; i < BDF_GLYPHS_MAX; i++) {
138			BdfGlyph* g = &f->bdf[i];
139
140			/* unloaded glpyh */
141			if (!g->valid)
142				continue;
143
144			/* highest point above baseline */
145			if (g->h + g->yoff > asc)
146				asc = g->h + g->yoff;
147
148			/* lowest point below baseline */
149			if (-g->yoff > dsc)
150				dsc = -g->yoff;
151		}
152	}
153
154	f->asc = asc;
155	f->dsc = -dsc;
156	f->cellh = asc + dsc;
157
158	if (f->cellw <= 0 || f->cellh <= 0) {
159		font_free(f);
160		return -1;
161	}
162
163	return 0;
164}
165
166/* case insensitive string compare 
167   -1=(a<b), 0=(a==b), 1=(a>b) */
168static int strcicmp(char const* a, char const* b)
169{
170	int la;
171	int lb;
172	while (*a && *b) {
173		la = tolower((unsigned char)*a);
174		lb = tolower((unsigned char)*b);
175		if (la != lb)
176			return la - lb;
177		a++;
178		b++;
179	}
180
181	return tolower((unsigned char)*a) - tolower((unsigned char)*b);
182}
183
184/* determine if font is BDF (by judging extension) */
185static bool is_bdf(const char* path)
186{
187	size_t pl = strlen(path);
188
189	if (pl < 4)
190		return false;
191
192	return strcicmp(path + pl - 4, ".bdf") == 0;
193}
194
195int font_load(Fontface* f, const char* path)
196{
197	int ret;
198
199	if (!f || !path)
200		return -1;
201
202	memset(f, 0, sizeof(*f));
203
204	if (!is_bdf(path))
205		return -1;
206
207	f->bdf = calloc(BDF_GLYPHS_MAX, sizeof(*f->bdf));
208	if (!f->bdf)
209		return -1;
210
211	ret = load_bdf(f, path);
212	if (ret < 0)
213		font_free(f);
214	return ret;
215}
216
217void font_free(Fontface* f)
218{
219	if (!f)
220		return;
221
222	if (f->bdf) {
223		for (int i = 0; i < BDF_GLYPHS_MAX; i++)
224			free(f->bdf[i].bmp);
225		free(f->bdf);
226	}
227
228	memset(f, 0, sizeof(*f));
229}
230