]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - tools/tools/vt/fontcvt/fontcvt.c
vt fontcvt: -w sets the width, not height
[FreeBSD/FreeBSD.git] / tools / tools / vt / fontcvt / fontcvt.c
1 /*-
2  * Copyright (c) 2009 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$");
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 <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
48 #define VFNT_MAP_BOLD 2
49
50 static unsigned int width = 8, wbytes, height = 16;
51
52 struct 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
60 TAILQ_HEAD(glyph_list, glyph);
61 static SLIST_HEAD(, glyph) glyph_hash[FONTCVT_NHASH];
62 static 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 };
68 static unsigned int glyph_total, glyph_count[4], glyph_unique, glyph_dupe;
69
70 struct mapping {
71         TAILQ_ENTRY(mapping)     m_list;
72         unsigned int             m_char;
73         unsigned int             m_length;
74         struct glyph            *m_glyph;
75 };
76
77 TAILQ_HEAD(mapping_list, mapping);
78 static 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 };
84 static unsigned int mapping_total, map_count[4], map_folded_count[4],
85     mapping_unique, mapping_dupe;
86
87 static void
88 usage(void)
89 {
90
91         fprintf(stderr,
92 "usage: fontcvt [-w width] [-h height] normal.bdf [bold.bdf] out.fnt\n");
93         exit(1);
94 }
95
96 static int
97 add_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
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                 fprintf(stderr, "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
151 static struct glyph *
152 add_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
181 static int
182 parse_bitmap_line(uint8_t *left, uint8_t *right, unsigned int line,
183     unsigned int dwidth)
184 {
185         uint8_t *p;
186         unsigned int i, subline;
187
188         if (dwidth != width && dwidth != width * 2) {
189                 fprintf(stderr,
190                     "Unsupported width %u!\n", dwidth);
191                 return (1);
192         }
193
194         /* Move pixel data right to simplify splitting double characters. */
195         line >>= (howmany(dwidth, 8) * 8) - dwidth;
196
197         for (i = dwidth / width; i > 0; i--) {
198                 p = (i == 2) ? right : left;
199
200                 subline = line & ((1 << width) - 1);
201                 subline <<= (howmany(width, 8) * 8) - width;
202
203                 if (wbytes == 1) {
204                         *p = subline;
205                 } else if (wbytes == 2) {
206                         *p++ = subline >> 8;
207                         *p = subline;
208                 } else {
209                         fprintf(stderr,
210                             "Unsupported wbytes %u!\n", wbytes);
211                         return (1);
212                 }
213
214                 line >>= width;
215         }
216         
217         return (0);
218 }
219
220 static int
221 parse_bdf(const char *filename, unsigned int map_idx)
222 {
223         FILE *fp;
224         char *ln;
225         size_t length;
226         uint8_t bytes[wbytes * height], bytes_r[wbytes * height];
227         unsigned int curchar = 0, dwidth = 0, i, line;
228         struct glyph *gl;
229
230         fp = fopen(filename, "r");
231         if (fp == NULL) {
232                 perror(filename);
233                 return (1);
234         }
235
236         while ((ln = fgetln(fp, &length)) != NULL) {
237                 ln[length - 1] = '\0';
238
239                 if (strncmp(ln, "ENCODING ", 9) == 0) {
240                         curchar = atoi(ln + 9);
241                 }
242
243                 if (strncmp(ln, "DWIDTH ", 7) == 0) {
244                         dwidth = atoi(ln + 7);
245                 }
246
247                 if (strcmp(ln, "BITMAP") == 0) {
248                         for (i = 0; i < height; i++) {
249                                 if ((ln = fgetln(fp, &length)) == NULL) {
250                                         fprintf(stderr, "Unexpected EOF!\n");
251                                         return (1);
252                                 }
253                                 ln[length - 1] = '\0';
254                                 sscanf(ln, "%x", &line);
255                                 if (parse_bitmap_line(bytes + i * wbytes,
256                                      bytes_r + i * wbytes, line, dwidth) != 0)
257                                         return (1);
258                         }
259
260                         /* Prevent adding two glyphs for 0xFFFD */
261                         if (curchar == 0xFFFD) {
262                                 if (map_idx < VFNT_MAP_BOLD)
263                                         gl = add_glyph(bytes, 0, 1);
264                         } else if (curchar >= 0x20) {
265                                 gl = add_glyph(bytes, map_idx, 0);
266                                 if (add_mapping(gl, curchar, map_idx) != 0)
267                                         return (1);
268                                 if (dwidth == width * 2) {
269                                         gl = add_glyph(bytes_r, map_idx + 1, 0);
270                                         if (add_mapping(gl, curchar,
271                                             map_idx + 1) != 0)
272                                                 return (1);
273                                 }
274                         }
275                 }
276         }
277
278         return (0);
279 }
280
281 static void
282 number_glyphs(void)
283 {
284         struct glyph *gl;
285         unsigned int i, idx = 0;
286
287         for (i = 0; i < VFNT_MAPS; i++)
288                 TAILQ_FOREACH(gl, &glyphs[i], g_list)
289                         gl->g_index = idx++;
290 }
291
292 static void
293 write_glyphs(FILE *fp)
294 {
295         struct glyph *gl;
296         unsigned int i;
297
298         for (i = 0; i < VFNT_MAPS; i++) {
299                 TAILQ_FOREACH(gl, &glyphs[i], g_list)
300                         fwrite(gl->g_data, wbytes * height, 1, fp);
301         }
302 }
303
304 static void
305 fold_mappings(unsigned int map_idx)
306 {
307         struct mapping_list *ml = &maps[map_idx];
308         struct mapping *mn, *mp, *mbase;
309
310         mp = mbase = TAILQ_FIRST(ml);
311         for (mp = mbase = TAILQ_FIRST(ml); mp != NULL; mp = mn) {
312                 mn = TAILQ_NEXT(mp, m_list);
313                 if (mn != NULL && mn->m_char == mp->m_char + 1 &&
314                     mn->m_glyph->g_index == mp->m_glyph->g_index + 1)
315                         continue;
316                 mbase->m_length = mp->m_char - mbase->m_char + 1;
317                 mbase = mp = mn;
318                 map_folded_count[map_idx]++;
319         }
320 }
321
322 struct file_mapping {
323         uint32_t        source;
324         uint16_t        destination;
325         uint16_t        length;
326 } __packed;
327
328 static void
329 write_mappings(FILE *fp, unsigned int map_idx)
330 {
331         struct mapping_list *ml = &maps[map_idx];
332         struct mapping *mp;
333         struct file_mapping fm;
334         unsigned int i = 0, j = 0;
335
336         TAILQ_FOREACH(mp, ml, m_list) {
337                 j++;
338                 if (mp->m_length > 0) {
339                         i += mp->m_length;
340                         fm.source = htobe32(mp->m_char);
341                         fm.destination = htobe16(mp->m_glyph->g_index);
342                         fm.length = htobe16(mp->m_length - 1);
343                         fwrite(&fm, sizeof fm, 1, fp);
344                 }
345         }
346         assert(i == j);
347 }
348
349 struct file_header {
350         uint8_t         magic[8];
351         uint8_t         width;
352         uint8_t         height;
353         uint16_t        pad;
354         uint32_t        glyph_count;
355         uint32_t        map_count[4];
356 } __packed;
357
358 static int
359 write_fnt(const char *filename)
360 {
361         FILE *fp;
362         struct file_header fh = {
363                 .magic = "VFNT0002",
364         };
365
366         fp = fopen(filename, "wb");
367         if (fp == NULL) {
368                 perror(filename);
369                 return (1);
370         }
371
372         fh.width = width;
373         fh.height = height;
374         fh.glyph_count = htobe32(glyph_unique);
375         fh.map_count[0] = htobe32(map_folded_count[0]);
376         fh.map_count[1] = htobe32(map_folded_count[1]);
377         fh.map_count[2] = htobe32(map_folded_count[2]);
378         fh.map_count[3] = htobe32(map_folded_count[3]);
379         fwrite(&fh, sizeof fh, 1, fp);
380         
381         write_glyphs(fp);
382         write_mappings(fp, VFNT_MAP_NORMAL);
383         write_mappings(fp, 1);
384         write_mappings(fp, VFNT_MAP_BOLD);
385         write_mappings(fp, 3);
386
387         return (0);
388 }
389
390 int
391 main(int argc, char *argv[])
392 {
393         int ch;
394
395         assert(sizeof(struct file_header) == 32);
396         assert(sizeof(struct file_mapping) == 8);
397
398         while ((ch = getopt(argc, argv, "h:w:")) != -1) {
399                 switch (ch) {
400                 case 'h':
401                         height = atoi(optarg);
402                         break;
403                 case 'w':
404                         width = atoi(optarg);
405                         break;
406                 case '?':
407                 default:
408                         usage();
409                 }
410         }
411         argc -= optind;
412         argv += optind;
413
414         if (argc < 2 || argc > 3)
415                 usage();
416
417         wbytes = howmany(width, 8);
418
419         if (parse_bdf(argv[0], VFNT_MAP_NORMAL) != 0)
420                 return (1);
421         argc--;
422         argv++;
423         if (argc == 2) {
424                 if (parse_bdf(argv[0], VFNT_MAP_BOLD) != 0)
425                         return (1);
426                 argc--;
427                 argv++;
428         }
429         number_glyphs();
430         fold_mappings(0);
431         fold_mappings(1);
432         fold_mappings(2);
433         fold_mappings(3);
434         if (write_fnt(argv[0]) != 0)
435                 return (1);
436         
437         printf(
438 "Statistics:\n"
439 "- glyph_total:                 %5u\n"
440 "- glyph_normal:                %5u\n"
441 "- glyph_normal_right:          %5u\n"
442 "- glyph_bold:                  %5u\n"
443 "- glyph_bold_right:            %5u\n"
444 "- glyph_unique:                %5u\n"
445 "- glyph_dupe:                  %5u\n"
446 "- mapping_total:               %5u\n"
447 "- mapping_normal:              %5u\n"
448 "- mapping_normal_folded:       %5u\n"
449 "- mapping_normal_right:        %5u\n"
450 "- mapping_normal_right_folded: %5u\n"
451 "- mapping_bold:                %5u\n"
452 "- mapping_bold_folded:         %5u\n"
453 "- mapping_bold_right:          %5u\n"
454 "- mapping_bold_right_folded:   %5u\n"
455 "- mapping_unique:              %5u\n"
456 "- mapping_dupe:                %5u\n",
457             glyph_total,
458             glyph_count[0],
459             glyph_count[1],
460             glyph_count[2],
461             glyph_count[3],
462             glyph_unique, glyph_dupe,
463             mapping_total,
464             map_count[0], map_folded_count[0],
465             map_count[1], map_folded_count[1],
466             map_count[2], map_folded_count[2],
467             map_count[3], map_folded_count[3],
468             mapping_unique, mapping_dupe);
469         
470         return (0);
471 }