Deleted Added
full compact
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>
40#include <err.h>
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
50#define VFNT_MAP_BOLD 2
51#define VFNT_MAP_BOLD_RH 3
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
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
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);
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}