Deleted Added
full compact
vtfontcvt.c (267298) vtfontcvt.c (267301)
1/*-
2 * Copyright (c) 2009, 2014 The FreeBSD Foundation
3 * All rights reserved.
4 *
5 * This software was developed by Ed Schouten under sponsorship from the
6 * FreeBSD Foundation.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 * 1. Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in the
15 * documentation and/or other materials provided with the distribution.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
21 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27 * SUCH DAMAGE.
28 */
29
30#include <sys/cdefs.h>
1/*-
2 * Copyright (c) 2009, 2014 The FreeBSD Foundation
3 * All rights reserved.
4 *
5 * This software was developed by Ed Schouten under sponsorship from the
6 * FreeBSD Foundation.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 * 1. Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in the
15 * documentation and/or other materials provided with the distribution.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
21 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27 * SUCH DAMAGE.
28 */
29
30#include <sys/cdefs.h>
31__FBSDID("$FreeBSD: head/tools/tools/vt/fontcvt/fontcvt.c 267298 2014-06-09 20:49:13Z emaste $");
31__FBSDID("$FreeBSD: head/tools/tools/vt/fontcvt/fontcvt.c 267301 2014-06-09 20:52:35Z emaste $");
32
33#include <sys/types.h>
34#include <sys/fnv_hash.h>
35#include <sys/endian.h>
36#include <sys/param.h>
37#include <sys/queue.h>
38
39#include <assert.h>
32
33#include <sys/types.h>
34#include <sys/fnv_hash.h>
35#include <sys/endian.h>
36#include <sys/param.h>
37#include <sys/queue.h>
38
39#include <assert.h>
40#include <err.h>
40#include <stdint.h>
41#include <stdio.h>
42#include <stdlib.h>
43#include <string.h>
44#include <unistd.h>
45
46#define VFNT_MAPS 4
47#define VFNT_MAP_NORMAL 0
41#include <stdint.h>
42#include <stdio.h>
43#include <stdlib.h>
44#include <string.h>
45#include <unistd.h>
46
47#define VFNT_MAPS 4
48#define VFNT_MAP_NORMAL 0
49#define VFNT_MAP_NORMAL_RH 1
48#define VFNT_MAP_BOLD 2
50#define VFNT_MAP_BOLD 2
51#define VFNT_MAP_BOLD_RH 3
49
50static unsigned int width = 8, wbytes, height = 16;
51
52struct glyph {
53 TAILQ_ENTRY(glyph) g_list;
54 SLIST_ENTRY(glyph) g_hash;
55 uint8_t *g_data;
56 unsigned int g_index;
57};
58
59#define FONTCVT_NHASH 4096
60TAILQ_HEAD(glyph_list, glyph);
61static SLIST_HEAD(, glyph) glyph_hash[FONTCVT_NHASH];
62static struct glyph_list glyphs[VFNT_MAPS] = {
63 TAILQ_HEAD_INITIALIZER(glyphs[0]),
64 TAILQ_HEAD_INITIALIZER(glyphs[1]),
65 TAILQ_HEAD_INITIALIZER(glyphs[2]),
66 TAILQ_HEAD_INITIALIZER(glyphs[3]),
67};
68static unsigned int glyph_total, glyph_count[4], glyph_unique, glyph_dupe;
69
70struct mapping {
71 TAILQ_ENTRY(mapping) m_list;
72 unsigned int m_char;
73 unsigned int m_length;
74 struct glyph *m_glyph;
75};
76
77TAILQ_HEAD(mapping_list, mapping);
78static struct mapping_list maps[VFNT_MAPS] = {
79 TAILQ_HEAD_INITIALIZER(maps[0]),
80 TAILQ_HEAD_INITIALIZER(maps[1]),
81 TAILQ_HEAD_INITIALIZER(maps[2]),
82 TAILQ_HEAD_INITIALIZER(maps[3]),
83};
84static unsigned int mapping_total, map_count[4], map_folded_count[4],
85 mapping_unique, mapping_dupe;
86
87static void
88usage(void)
89{
90
91 errx(1,
92"usage: fontcvt [-w width] [-h height] [-v] normal.bdf [bold.bdf] out.fnt\n");
93 exit(1);
94}
95
96static int
97add_mapping(struct glyph *gl, unsigned int c, unsigned int map_idx)
98{
99 struct mapping *mp;
100 struct mapping_list *ml;
101
102 mapping_total++;
103
52
53static unsigned int width = 8, wbytes, height = 16;
54
55struct glyph {
56 TAILQ_ENTRY(glyph) g_list;
57 SLIST_ENTRY(glyph) g_hash;
58 uint8_t *g_data;
59 unsigned int g_index;
60};
61
62#define FONTCVT_NHASH 4096
63TAILQ_HEAD(glyph_list, glyph);
64static SLIST_HEAD(, glyph) glyph_hash[FONTCVT_NHASH];
65static struct glyph_list glyphs[VFNT_MAPS] = {
66 TAILQ_HEAD_INITIALIZER(glyphs[0]),
67 TAILQ_HEAD_INITIALIZER(glyphs[1]),
68 TAILQ_HEAD_INITIALIZER(glyphs[2]),
69 TAILQ_HEAD_INITIALIZER(glyphs[3]),
70};
71static unsigned int glyph_total, glyph_count[4], glyph_unique, glyph_dupe;
72
73struct mapping {
74 TAILQ_ENTRY(mapping) m_list;
75 unsigned int m_char;
76 unsigned int m_length;
77 struct glyph *m_glyph;
78};
79
80TAILQ_HEAD(mapping_list, mapping);
81static struct mapping_list maps[VFNT_MAPS] = {
82 TAILQ_HEAD_INITIALIZER(maps[0]),
83 TAILQ_HEAD_INITIALIZER(maps[1]),
84 TAILQ_HEAD_INITIALIZER(maps[2]),
85 TAILQ_HEAD_INITIALIZER(maps[3]),
86};
87static unsigned int mapping_total, map_count[4], map_folded_count[4],
88 mapping_unique, mapping_dupe;
89
90static void
91usage(void)
92{
93
94 errx(1,
95"usage: fontcvt [-w width] [-h height] [-v] normal.bdf [bold.bdf] out.fnt\n");
96 exit(1);
97}
98
99static int
100add_mapping(struct glyph *gl, unsigned int c, unsigned int map_idx)
101{
102 struct mapping *mp;
103 struct mapping_list *ml;
104
105 mapping_total++;
106
104 if (map_idx >= VFNT_MAP_BOLD) {
105 int found = 0;
106 unsigned normal_map_idx = map_idx - VFNT_MAP_BOLD;
107
108 TAILQ_FOREACH(mp, &maps[normal_map_idx], m_list) {
109 if (mp->m_char < c)
110 continue;
111 else if (mp->m_char > c)
112 break;
113 found = 1;
114
115 /*
116 * No mapping is needed if it's equal to the
117 * normal mapping.
118 */
119 if (mp->m_glyph == gl) {
120 mapping_dupe++;
121 return (0);
122 }
123 }
124
125 if (!found) {
126 fprintf(stderr,
127 "Character %u not in normal font!\n", c);
128 return (1);
129 }
130 }
131
132 mp = malloc(sizeof *mp);
133 mp->m_char = c;
134 mp->m_glyph = gl;
135 mp->m_length = 0;
136
137 ml = &maps[map_idx];
138 if (TAILQ_LAST(ml, mapping_list) != NULL &&
139 TAILQ_LAST(ml, mapping_list)->m_char >= c) {
140 errx(1, "Bad ordering at character %u\n", c);
141 return (1);
142 }
143 TAILQ_INSERT_TAIL(ml, mp, m_list);
144
145 map_count[map_idx]++;
146 mapping_unique++;
147
148 return (0);
149}
150
107 mp = malloc(sizeof *mp);
108 mp->m_char = c;
109 mp->m_glyph = gl;
110 mp->m_length = 0;
111
112 ml = &maps[map_idx];
113 if (TAILQ_LAST(ml, mapping_list) != NULL &&
114 TAILQ_LAST(ml, mapping_list)->m_char >= c) {
115 errx(1, "Bad ordering at character %u\n", c);
116 return (1);
117 }
118 TAILQ_INSERT_TAIL(ml, mp, m_list);
119
120 map_count[map_idx]++;
121 mapping_unique++;
122
123 return (0);
124}
125
126static int
127dedup_mapping(unsigned int map_idx)
128{
129 struct mapping *mp_bold, *mp_normal, *mp_temp;
130 unsigned normal_map_idx = map_idx - VFNT_MAP_BOLD;
131
132 assert(map_idx == VFNT_MAP_BOLD || map_idx == VFNT_MAP_BOLD_RH);
133 mp_normal = TAILQ_FIRST(&maps[normal_map_idx]);
134 TAILQ_FOREACH_SAFE(mp_bold, &maps[map_idx], m_list, mp_temp) {
135 while (mp_normal->m_char < mp_bold->m_char)
136 mp_normal = TAILQ_NEXT(mp_normal, m_list);
137 if (mp_bold->m_char != mp_normal->m_char) {
138 errx(1, "Character %u not in normal font!\n",
139 mp_bold->m_char);
140 return (1);
141 }
142 if (mp_bold->m_glyph != mp_normal->m_glyph)
143 continue;
144
145 /* No mapping is needed if it's equal to the normal mapping. */
146 TAILQ_REMOVE(&maps[map_idx], mp_bold, m_list);
147 free(mp_bold);
148 mapping_dupe++;
149 }
150 return (0);
151}
152
151static struct glyph *
152add_glyph(const uint8_t *bytes, unsigned int map_idx, int fallback)
153{
154 struct glyph *gl;
155 int hash;
156
157 glyph_total++;
158 glyph_count[map_idx]++;
159
160 hash = fnv_32_buf(bytes, wbytes * height, FNV1_32_INIT) % FONTCVT_NHASH;
161 SLIST_FOREACH(gl, &glyph_hash[hash], g_hash) {
162 if (memcmp(gl->g_data, bytes, wbytes * height) == 0) {
163 glyph_dupe++;
164 return (gl);
165 }
166 }
167
168 gl = malloc(sizeof *gl);
169 gl->g_data = malloc(wbytes * height);
170 memcpy(gl->g_data, bytes, wbytes * height);
171 if (fallback)
172 TAILQ_INSERT_HEAD(&glyphs[map_idx], gl, g_list);
173 else
174 TAILQ_INSERT_TAIL(&glyphs[map_idx], gl, g_list);
175 SLIST_INSERT_HEAD(&glyph_hash[hash], gl, g_hash);
176
177 glyph_unique++;
178 return (gl);
179}
180
181static int
182add_char(unsigned curchar, unsigned map_idx, uint8_t *bytes, uint8_t *bytes_r)
183{
184 struct glyph *gl;
185
186 /* Prevent adding two glyphs for 0xFFFD */
187 if (curchar == 0xFFFD) {
188 if (map_idx < VFNT_MAP_BOLD)
189 gl = add_glyph(bytes, 0, 1);
190 } else if (curchar >= 0x20) {
191 gl = add_glyph(bytes, map_idx, 0);
192 if (add_mapping(gl, curchar, map_idx) != 0)
193 return (1);
194 if (bytes_r != NULL) {
195 gl = add_glyph(bytes_r, map_idx + 1, 0);
196 if (add_mapping(gl, curchar,
197 map_idx + 1) != 0)
198 return (1);
199 }
200 }
201 return (0);
202}
203
204
205static int
206parse_bitmap_line(uint8_t *left, uint8_t *right, unsigned int line,
207 unsigned int dwidth)
208{
209 uint8_t *p;
210 unsigned int i, subline;
211
212 if (dwidth != width && dwidth != width * 2) {
213 errx(1,
214 "Bitmap with unsupported width %u!\n", dwidth);
215 return (1);
216 }
217
218 /* Move pixel data right to simplify splitting double characters. */
219 line >>= (howmany(dwidth, 8) * 8) - dwidth;
220
221 for (i = dwidth / width; i > 0; i--) {
222 p = (i == 2) ? right : left;
223
224 subline = line & ((1 << width) - 1);
225 subline <<= (howmany(width, 8) * 8) - width;
226
227 if (wbytes == 1) {
228 *p = subline;
229 } else if (wbytes == 2) {
230 *p++ = subline >> 8;
231 *p = subline;
232 } else {
233 errx(1,
234 "Unsupported wbytes %u!\n", wbytes);
235 return (1);
236 }
237
238 line >>= width;
239 }
240
241 return (0);
242}
243
244static int
245parse_bdf(FILE *fp, unsigned int map_idx)
246{
247 char *ln;
248 size_t length;
249 uint8_t bytes[wbytes * height], bytes_r[wbytes * height];
250 unsigned int curchar = 0, dwidth = 0, i, line;
251
252 while ((ln = fgetln(fp, &length)) != NULL) {
253 ln[length - 1] = '\0';
254
255 if (strncmp(ln, "ENCODING ", 9) == 0) {
256 curchar = atoi(ln + 9);
257 }
258
259 if (strncmp(ln, "DWIDTH ", 7) == 0) {
260 dwidth = atoi(ln + 7);
261 }
262
263 if (strncmp(ln, "BITMAP", 6) == 0 &&
264 (ln[6] == ' ' || ln[6] == '\0')) {
265 for (i = 0; i < height; i++) {
266 if ((ln = fgetln(fp, &length)) == NULL) {
267 errx(1, "Unexpected EOF!\n");
268 return (1);
269 }
270 ln[length - 1] = '\0';
271 sscanf(ln, "%x", &line);
272 if (parse_bitmap_line(bytes + i * wbytes,
273 bytes_r + i * wbytes, line, dwidth) != 0)
274 return (1);
275 }
276
277 if (add_char(curchar, map_idx, bytes,
278 dwidth == width * 2 ? bytes_r : NULL) != 0)
279 return (1);
280 }
281 }
282
283 return (0);
284}
285
286static int
287parse_hex(FILE *fp, unsigned int map_idx)
288{
289 char *ln, *p;
290 char fmt_str[8];
291 size_t length;
292 uint8_t bytes[wbytes * height], bytes_r[wbytes * height];
293 unsigned curchar = 0, i, line, chars_per_row, dwidth;
294
295 while ((ln = fgetln(fp, &length)) != NULL) {
296 ln[length - 1] = '\0';
297
298 if (strncmp(ln, "# Height: ", 10) == 0) {
299 height = atoi(ln + 10);
300 } else if (strncmp(ln, "# Width: ", 9) == 0) {
301 width = atoi(ln + 9);
302 } else if (sscanf(ln, "%4x:", &curchar)) {
303 p = ln + 5;
304 chars_per_row = strlen(p) / height;
305 dwidth = width;
306 if (chars_per_row / 2 > width / 8)
307 dwidth *= 2; /* Double-width character. */
308 snprintf(fmt_str, sizeof(fmt_str), "%%%ux",
309 chars_per_row);
310
311 for (i = 0; i < height; i++) {
312 sscanf(p, fmt_str, &line);
313 p += chars_per_row;
314 if (parse_bitmap_line(bytes + i * wbytes,
315 bytes_r + i * wbytes, line, dwidth) != 0)
316 return (1);
317 }
318
319 if (add_char(curchar, map_idx, bytes,
320 dwidth == width * 2 ? bytes_r : NULL) != 0)
321 return (1);
322 }
323 }
324 return (0);
325}
326
327static int
328parse_file(const char *filename, unsigned int map_idx)
329{
330 FILE *fp;
331 size_t len;
332
333 fp = fopen(filename, "r");
334 if (fp == NULL) {
335 perror(filename);
336 return (1);
337 }
338 len = strlen(filename);
339 if (len > 4 && strcasecmp(filename + len - 4, ".hex") == 0)
340 return parse_hex(fp, map_idx);
341 return parse_bdf(fp, map_idx);
342}
343
344static void
345number_glyphs(void)
346{
347 struct glyph *gl;
348 unsigned int i, idx = 0;
349
350 for (i = 0; i < VFNT_MAPS; i++)
351 TAILQ_FOREACH(gl, &glyphs[i], g_list)
352 gl->g_index = idx++;
353}
354
355static void
356write_glyphs(FILE *fp)
357{
358 struct glyph *gl;
359 unsigned int i;
360
361 for (i = 0; i < VFNT_MAPS; i++) {
362 TAILQ_FOREACH(gl, &glyphs[i], g_list)
363 fwrite(gl->g_data, wbytes * height, 1, fp);
364 }
365}
366
367static void
368fold_mappings(unsigned int map_idx)
369{
370 struct mapping_list *ml = &maps[map_idx];
371 struct mapping *mn, *mp, *mbase;
372
373 mp = mbase = TAILQ_FIRST(ml);
374 for (mp = mbase = TAILQ_FIRST(ml); mp != NULL; mp = mn) {
375 mn = TAILQ_NEXT(mp, m_list);
376 if (mn != NULL && mn->m_char == mp->m_char + 1 &&
377 mn->m_glyph->g_index == mp->m_glyph->g_index + 1)
378 continue;
379 mbase->m_length = mp->m_char - mbase->m_char + 1;
380 mbase = mp = mn;
381 map_folded_count[map_idx]++;
382 }
383}
384
385struct file_mapping {
386 uint32_t source;
387 uint16_t destination;
388 uint16_t length;
389} __packed;
390
391static void
392write_mappings(FILE *fp, unsigned int map_idx)
393{
394 struct mapping_list *ml = &maps[map_idx];
395 struct mapping *mp;
396 struct file_mapping fm;
397 unsigned int i = 0, j = 0;
398
399 TAILQ_FOREACH(mp, ml, m_list) {
400 j++;
401 if (mp->m_length > 0) {
402 i += mp->m_length;
403 fm.source = htobe32(mp->m_char);
404 fm.destination = htobe16(mp->m_glyph->g_index);
405 fm.length = htobe16(mp->m_length - 1);
406 fwrite(&fm, sizeof fm, 1, fp);
407 }
408 }
409 assert(i == j);
410}
411
412struct file_header {
413 uint8_t magic[8];
414 uint8_t width;
415 uint8_t height;
416 uint16_t pad;
417 uint32_t glyph_count;
418 uint32_t map_count[4];
419} __packed;
420
421static int
422write_fnt(const char *filename)
423{
424 FILE *fp;
425 struct file_header fh = {
426 .magic = "VFNT0002",
427 };
428
429 fp = fopen(filename, "wb");
430 if (fp == NULL) {
431 perror(filename);
432 return (1);
433 }
434
435 fh.width = width;
436 fh.height = height;
437 fh.glyph_count = htobe32(glyph_unique);
438 fh.map_count[0] = htobe32(map_folded_count[0]);
439 fh.map_count[1] = htobe32(map_folded_count[1]);
440 fh.map_count[2] = htobe32(map_folded_count[2]);
441 fh.map_count[3] = htobe32(map_folded_count[3]);
442 fwrite(&fh, sizeof fh, 1, fp);
443
444 write_glyphs(fp);
445 write_mappings(fp, VFNT_MAP_NORMAL);
446 write_mappings(fp, 1);
447 write_mappings(fp, VFNT_MAP_BOLD);
448 write_mappings(fp, 3);
449
450 return (0);
451}
452
453static void
454print_font_info(void)
455{
456 printf(
457"Statistics:\n"
458"- glyph_total: %5u\n"
459"- glyph_normal: %5u\n"
460"- glyph_normal_right: %5u\n"
461"- glyph_bold: %5u\n"
462"- glyph_bold_right: %5u\n"
463"- glyph_unique: %5u\n"
464"- glyph_dupe: %5u\n"
465"- mapping_total: %5u\n"
466"- mapping_normal: %5u\n"
467"- mapping_normal_folded: %5u\n"
468"- mapping_normal_right: %5u\n"
469"- mapping_normal_right_folded: %5u\n"
470"- mapping_bold: %5u\n"
471"- mapping_bold_folded: %5u\n"
472"- mapping_bold_right: %5u\n"
473"- mapping_bold_right_folded: %5u\n"
474"- mapping_unique: %5u\n"
475"- mapping_dupe: %5u\n",
476 glyph_total,
477 glyph_count[0],
478 glyph_count[1],
479 glyph_count[2],
480 glyph_count[3],
481 glyph_unique, glyph_dupe,
482 mapping_total,
483 map_count[0], map_folded_count[0],
484 map_count[1], map_folded_count[1],
485 map_count[2], map_folded_count[2],
486 map_count[3], map_folded_count[3],
487 mapping_unique, mapping_dupe);
488}
489
490int
491main(int argc, char *argv[])
492{
493 int ch, val, verbose = 0;
494
495 assert(sizeof(struct file_header) == 32);
496 assert(sizeof(struct file_mapping) == 8);
497
498 while ((ch = getopt(argc, argv, "h:w:")) != -1) {
499 switch (ch) {
500 case 'h':
501 val = atoi(optarg);
502 if (val <= 0 || val > 128) {
503 errx(1, "Invalid height %d", val);
504 return (1);
505 }
506 height = val;
507 break;
508 case 'v':
509 verbose = 1;
510 break;
511 case 'w':
512 val = atoi(optarg);
513 if (val <= 0 || val > 128) {
514 errx(1, "Invalid width %d", val);
515 return (1);
516 }
517 width = val;
518 break;
519 case '?':
520 default:
521 usage();
522 }
523 }
524 argc -= optind;
525 argv += optind;
526
527 if (argc < 2 || argc > 3)
528 usage();
529
530 wbytes = howmany(width, 8);
531
532 if (parse_file(argv[0], VFNT_MAP_NORMAL) != 0)
533 return (1);
534 argc--;
535 argv++;
536 if (argc == 2) {
537 if (parse_file(argv[0], VFNT_MAP_BOLD) != 0)
538 return (1);
539 argc--;
540 argv++;
541 }
542 number_glyphs();
153static struct glyph *
154add_glyph(const uint8_t *bytes, unsigned int map_idx, int fallback)
155{
156 struct glyph *gl;
157 int hash;
158
159 glyph_total++;
160 glyph_count[map_idx]++;
161
162 hash = fnv_32_buf(bytes, wbytes * height, FNV1_32_INIT) % FONTCVT_NHASH;
163 SLIST_FOREACH(gl, &glyph_hash[hash], g_hash) {
164 if (memcmp(gl->g_data, bytes, wbytes * height) == 0) {
165 glyph_dupe++;
166 return (gl);
167 }
168 }
169
170 gl = malloc(sizeof *gl);
171 gl->g_data = malloc(wbytes * height);
172 memcpy(gl->g_data, bytes, wbytes * height);
173 if (fallback)
174 TAILQ_INSERT_HEAD(&glyphs[map_idx], gl, g_list);
175 else
176 TAILQ_INSERT_TAIL(&glyphs[map_idx], gl, g_list);
177 SLIST_INSERT_HEAD(&glyph_hash[hash], gl, g_hash);
178
179 glyph_unique++;
180 return (gl);
181}
182
183static int
184add_char(unsigned curchar, unsigned map_idx, uint8_t *bytes, uint8_t *bytes_r)
185{
186 struct glyph *gl;
187
188 /* Prevent adding two glyphs for 0xFFFD */
189 if (curchar == 0xFFFD) {
190 if (map_idx < VFNT_MAP_BOLD)
191 gl = add_glyph(bytes, 0, 1);
192 } else if (curchar >= 0x20) {
193 gl = add_glyph(bytes, map_idx, 0);
194 if (add_mapping(gl, curchar, map_idx) != 0)
195 return (1);
196 if (bytes_r != NULL) {
197 gl = add_glyph(bytes_r, map_idx + 1, 0);
198 if (add_mapping(gl, curchar,
199 map_idx + 1) != 0)
200 return (1);
201 }
202 }
203 return (0);
204}
205
206
207static int
208parse_bitmap_line(uint8_t *left, uint8_t *right, unsigned int line,
209 unsigned int dwidth)
210{
211 uint8_t *p;
212 unsigned int i, subline;
213
214 if (dwidth != width && dwidth != width * 2) {
215 errx(1,
216 "Bitmap with unsupported width %u!\n", dwidth);
217 return (1);
218 }
219
220 /* Move pixel data right to simplify splitting double characters. */
221 line >>= (howmany(dwidth, 8) * 8) - dwidth;
222
223 for (i = dwidth / width; i > 0; i--) {
224 p = (i == 2) ? right : left;
225
226 subline = line & ((1 << width) - 1);
227 subline <<= (howmany(width, 8) * 8) - width;
228
229 if (wbytes == 1) {
230 *p = subline;
231 } else if (wbytes == 2) {
232 *p++ = subline >> 8;
233 *p = subline;
234 } else {
235 errx(1,
236 "Unsupported wbytes %u!\n", wbytes);
237 return (1);
238 }
239
240 line >>= width;
241 }
242
243 return (0);
244}
245
246static int
247parse_bdf(FILE *fp, unsigned int map_idx)
248{
249 char *ln;
250 size_t length;
251 uint8_t bytes[wbytes * height], bytes_r[wbytes * height];
252 unsigned int curchar = 0, dwidth = 0, i, line;
253
254 while ((ln = fgetln(fp, &length)) != NULL) {
255 ln[length - 1] = '\0';
256
257 if (strncmp(ln, "ENCODING ", 9) == 0) {
258 curchar = atoi(ln + 9);
259 }
260
261 if (strncmp(ln, "DWIDTH ", 7) == 0) {
262 dwidth = atoi(ln + 7);
263 }
264
265 if (strncmp(ln, "BITMAP", 6) == 0 &&
266 (ln[6] == ' ' || ln[6] == '\0')) {
267 for (i = 0; i < height; i++) {
268 if ((ln = fgetln(fp, &length)) == NULL) {
269 errx(1, "Unexpected EOF!\n");
270 return (1);
271 }
272 ln[length - 1] = '\0';
273 sscanf(ln, "%x", &line);
274 if (parse_bitmap_line(bytes + i * wbytes,
275 bytes_r + i * wbytes, line, dwidth) != 0)
276 return (1);
277 }
278
279 if (add_char(curchar, map_idx, bytes,
280 dwidth == width * 2 ? bytes_r : NULL) != 0)
281 return (1);
282 }
283 }
284
285 return (0);
286}
287
288static int
289parse_hex(FILE *fp, unsigned int map_idx)
290{
291 char *ln, *p;
292 char fmt_str[8];
293 size_t length;
294 uint8_t bytes[wbytes * height], bytes_r[wbytes * height];
295 unsigned curchar = 0, i, line, chars_per_row, dwidth;
296
297 while ((ln = fgetln(fp, &length)) != NULL) {
298 ln[length - 1] = '\0';
299
300 if (strncmp(ln, "# Height: ", 10) == 0) {
301 height = atoi(ln + 10);
302 } else if (strncmp(ln, "# Width: ", 9) == 0) {
303 width = atoi(ln + 9);
304 } else if (sscanf(ln, "%4x:", &curchar)) {
305 p = ln + 5;
306 chars_per_row = strlen(p) / height;
307 dwidth = width;
308 if (chars_per_row / 2 > width / 8)
309 dwidth *= 2; /* Double-width character. */
310 snprintf(fmt_str, sizeof(fmt_str), "%%%ux",
311 chars_per_row);
312
313 for (i = 0; i < height; i++) {
314 sscanf(p, fmt_str, &line);
315 p += chars_per_row;
316 if (parse_bitmap_line(bytes + i * wbytes,
317 bytes_r + i * wbytes, line, dwidth) != 0)
318 return (1);
319 }
320
321 if (add_char(curchar, map_idx, bytes,
322 dwidth == width * 2 ? bytes_r : NULL) != 0)
323 return (1);
324 }
325 }
326 return (0);
327}
328
329static int
330parse_file(const char *filename, unsigned int map_idx)
331{
332 FILE *fp;
333 size_t len;
334
335 fp = fopen(filename, "r");
336 if (fp == NULL) {
337 perror(filename);
338 return (1);
339 }
340 len = strlen(filename);
341 if (len > 4 && strcasecmp(filename + len - 4, ".hex") == 0)
342 return parse_hex(fp, map_idx);
343 return parse_bdf(fp, map_idx);
344}
345
346static void
347number_glyphs(void)
348{
349 struct glyph *gl;
350 unsigned int i, idx = 0;
351
352 for (i = 0; i < VFNT_MAPS; i++)
353 TAILQ_FOREACH(gl, &glyphs[i], g_list)
354 gl->g_index = idx++;
355}
356
357static void
358write_glyphs(FILE *fp)
359{
360 struct glyph *gl;
361 unsigned int i;
362
363 for (i = 0; i < VFNT_MAPS; i++) {
364 TAILQ_FOREACH(gl, &glyphs[i], g_list)
365 fwrite(gl->g_data, wbytes * height, 1, fp);
366 }
367}
368
369static void
370fold_mappings(unsigned int map_idx)
371{
372 struct mapping_list *ml = &maps[map_idx];
373 struct mapping *mn, *mp, *mbase;
374
375 mp = mbase = TAILQ_FIRST(ml);
376 for (mp = mbase = TAILQ_FIRST(ml); mp != NULL; mp = mn) {
377 mn = TAILQ_NEXT(mp, m_list);
378 if (mn != NULL && mn->m_char == mp->m_char + 1 &&
379 mn->m_glyph->g_index == mp->m_glyph->g_index + 1)
380 continue;
381 mbase->m_length = mp->m_char - mbase->m_char + 1;
382 mbase = mp = mn;
383 map_folded_count[map_idx]++;
384 }
385}
386
387struct file_mapping {
388 uint32_t source;
389 uint16_t destination;
390 uint16_t length;
391} __packed;
392
393static void
394write_mappings(FILE *fp, unsigned int map_idx)
395{
396 struct mapping_list *ml = &maps[map_idx];
397 struct mapping *mp;
398 struct file_mapping fm;
399 unsigned int i = 0, j = 0;
400
401 TAILQ_FOREACH(mp, ml, m_list) {
402 j++;
403 if (mp->m_length > 0) {
404 i += mp->m_length;
405 fm.source = htobe32(mp->m_char);
406 fm.destination = htobe16(mp->m_glyph->g_index);
407 fm.length = htobe16(mp->m_length - 1);
408 fwrite(&fm, sizeof fm, 1, fp);
409 }
410 }
411 assert(i == j);
412}
413
414struct file_header {
415 uint8_t magic[8];
416 uint8_t width;
417 uint8_t height;
418 uint16_t pad;
419 uint32_t glyph_count;
420 uint32_t map_count[4];
421} __packed;
422
423static int
424write_fnt(const char *filename)
425{
426 FILE *fp;
427 struct file_header fh = {
428 .magic = "VFNT0002",
429 };
430
431 fp = fopen(filename, "wb");
432 if (fp == NULL) {
433 perror(filename);
434 return (1);
435 }
436
437 fh.width = width;
438 fh.height = height;
439 fh.glyph_count = htobe32(glyph_unique);
440 fh.map_count[0] = htobe32(map_folded_count[0]);
441 fh.map_count[1] = htobe32(map_folded_count[1]);
442 fh.map_count[2] = htobe32(map_folded_count[2]);
443 fh.map_count[3] = htobe32(map_folded_count[3]);
444 fwrite(&fh, sizeof fh, 1, fp);
445
446 write_glyphs(fp);
447 write_mappings(fp, VFNT_MAP_NORMAL);
448 write_mappings(fp, 1);
449 write_mappings(fp, VFNT_MAP_BOLD);
450 write_mappings(fp, 3);
451
452 return (0);
453}
454
455static void
456print_font_info(void)
457{
458 printf(
459"Statistics:\n"
460"- glyph_total: %5u\n"
461"- glyph_normal: %5u\n"
462"- glyph_normal_right: %5u\n"
463"- glyph_bold: %5u\n"
464"- glyph_bold_right: %5u\n"
465"- glyph_unique: %5u\n"
466"- glyph_dupe: %5u\n"
467"- mapping_total: %5u\n"
468"- mapping_normal: %5u\n"
469"- mapping_normal_folded: %5u\n"
470"- mapping_normal_right: %5u\n"
471"- mapping_normal_right_folded: %5u\n"
472"- mapping_bold: %5u\n"
473"- mapping_bold_folded: %5u\n"
474"- mapping_bold_right: %5u\n"
475"- mapping_bold_right_folded: %5u\n"
476"- mapping_unique: %5u\n"
477"- mapping_dupe: %5u\n",
478 glyph_total,
479 glyph_count[0],
480 glyph_count[1],
481 glyph_count[2],
482 glyph_count[3],
483 glyph_unique, glyph_dupe,
484 mapping_total,
485 map_count[0], map_folded_count[0],
486 map_count[1], map_folded_count[1],
487 map_count[2], map_folded_count[2],
488 map_count[3], map_folded_count[3],
489 mapping_unique, mapping_dupe);
490}
491
492int
493main(int argc, char *argv[])
494{
495 int ch, val, verbose = 0;
496
497 assert(sizeof(struct file_header) == 32);
498 assert(sizeof(struct file_mapping) == 8);
499
500 while ((ch = getopt(argc, argv, "h:w:")) != -1) {
501 switch (ch) {
502 case 'h':
503 val = atoi(optarg);
504 if (val <= 0 || val > 128) {
505 errx(1, "Invalid height %d", val);
506 return (1);
507 }
508 height = val;
509 break;
510 case 'v':
511 verbose = 1;
512 break;
513 case 'w':
514 val = atoi(optarg);
515 if (val <= 0 || val > 128) {
516 errx(1, "Invalid width %d", val);
517 return (1);
518 }
519 width = val;
520 break;
521 case '?':
522 default:
523 usage();
524 }
525 }
526 argc -= optind;
527 argv += optind;
528
529 if (argc < 2 || argc > 3)
530 usage();
531
532 wbytes = howmany(width, 8);
533
534 if (parse_file(argv[0], VFNT_MAP_NORMAL) != 0)
535 return (1);
536 argc--;
537 argv++;
538 if (argc == 2) {
539 if (parse_file(argv[0], VFNT_MAP_BOLD) != 0)
540 return (1);
541 argc--;
542 argv++;
543 }
544 number_glyphs();
545 dedup_mapping(VFNT_MAP_BOLD);
546 dedup_mapping(VFNT_MAP_BOLD_RH);
543 fold_mappings(0);
544 fold_mappings(1);
545 fold_mappings(2);
546 fold_mappings(3);
547 if (write_fnt(argv[0]) != 0)
548 return (1);
549
550 if (verbose)
551 print_font_info();
552
553 return (0);
554}
547 fold_mappings(0);
548 fold_mappings(1);
549 fold_mappings(2);
550 fold_mappings(3);
551 if (write_fnt(argv[0]) != 0)
552 return (1);
553
554 if (verbose)
555 print_font_info();
556
557 return (0);
558}