vtfontcvt.c revision 259680
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: head/tools/tools/vt/fontcvt/fontcvt.c 259680 2013-12-21 13:58:55Z emaste $"); 32 33#include <sys/endian.h> 34#include <sys/param.h> 35#include <sys/queue.h> 36 37#include <assert.h> 38#include <stdint.h> 39#include <stdio.h> 40#include <stdlib.h> 41#include <string.h> 42 43#define VFNT_MAPS 4 44#define VFNT_MAP_NORMAL 0 45#define VFNT_MAP_BOLD 2 46 47static unsigned int width, wbytes, height; 48 49struct glyph { 50 TAILQ_ENTRY(glyph) g_list; 51 uint8_t *g_data; 52 unsigned int g_index; 53}; 54 55TAILQ_HEAD(glyph_list, glyph); 56static struct glyph_list glyphs[VFNT_MAPS] = { 57 TAILQ_HEAD_INITIALIZER(glyphs[0]), 58 TAILQ_HEAD_INITIALIZER(glyphs[1]), 59 TAILQ_HEAD_INITIALIZER(glyphs[2]), 60 TAILQ_HEAD_INITIALIZER(glyphs[3]), 61}; 62static unsigned int glyph_total, glyph_count[4], glyph_unique, glyph_dupe; 63 64struct mapping { 65 TAILQ_ENTRY(mapping) m_list; 66 unsigned int m_char; 67 unsigned int m_length; 68 struct glyph *m_glyph; 69}; 70 71TAILQ_HEAD(mapping_list, mapping); 72static struct mapping_list maps[VFNT_MAPS] = { 73 TAILQ_HEAD_INITIALIZER(maps[0]), 74 TAILQ_HEAD_INITIALIZER(maps[1]), 75 TAILQ_HEAD_INITIALIZER(maps[2]), 76 TAILQ_HEAD_INITIALIZER(maps[3]), 77}; 78static unsigned int mapping_total, map_count[4], map_folded_count[4], 79 mapping_unique, mapping_dupe; 80 81static void 82usage(void) 83{ 84 85 fprintf(stderr, 86"usage: fontcvt width height normal.bdf bold.bdf out.fnt\n"); 87 exit(1); 88} 89 90static int 91add_mapping(struct glyph *gl, unsigned int c, unsigned int map_idx) 92{ 93 struct mapping *mp; 94 struct mapping_list *ml; 95 96 mapping_total++; 97 98 if (map_idx >= VFNT_MAP_BOLD) { 99 int found = 0; 100 unsigned normal_map_idx = map_idx - VFNT_MAP_BOLD; 101 102 TAILQ_FOREACH(mp, &maps[normal_map_idx], m_list) { 103 if (mp->m_char < c) 104 continue; 105 else if (mp->m_char > c) 106 break; 107 found = 1; 108 109 /* 110 * No mapping is needed if it's equal to the 111 * normal mapping. 112 */ 113 if (mp->m_glyph == gl) { 114 mapping_dupe++; 115 return (0); 116 } 117 } 118 119 if (!found) { 120 fprintf(stderr, 121 "Character %u not in normal font!\n", c); 122 return (1); 123 } 124 } 125 126 mp = malloc(sizeof *mp); 127 mp->m_char = c; 128 mp->m_glyph = gl; 129 mp->m_length = 0; 130 131 ml = &maps[map_idx]; 132 if (TAILQ_LAST(ml, mapping_list) != NULL && 133 TAILQ_LAST(ml, mapping_list)->m_char >= c) { 134 fprintf(stderr, "Bad ordering at character %u\n", c); 135 return (1); 136 } 137 TAILQ_INSERT_TAIL(ml, mp, m_list); 138 139 map_count[map_idx]++; 140 mapping_unique++; 141 142 return (0); 143} 144 145static struct glyph * 146add_glyph(const uint8_t *bytes, unsigned int map_idx, int fallback) 147{ 148 struct glyph *gl; 149 unsigned int i; 150 151 glyph_total++; 152 glyph_count[map_idx]++; 153 154 for (i = 0; i < VFNT_MAPS; i++) { 155 TAILQ_FOREACH(gl, &glyphs[i], g_list) { 156 if (memcmp(gl->g_data, bytes, wbytes * height) == 0) { 157 glyph_dupe++; 158 return (gl); 159 } 160 } 161 } 162 163 gl = malloc(sizeof *gl); 164 gl->g_data = malloc(wbytes * height); 165 memcpy(gl->g_data, bytes, wbytes * height); 166 if (fallback) 167 TAILQ_INSERT_HEAD(&glyphs[map_idx], gl, g_list); 168 else 169 TAILQ_INSERT_TAIL(&glyphs[map_idx], gl, g_list); 170 171 glyph_unique++; 172 return (gl); 173} 174 175static int 176parse_bitmap_line(uint8_t *left, uint8_t *right, unsigned int line, 177 unsigned int dwidth) 178{ 179 uint8_t *p; 180 unsigned int i, subline; 181 182 if (dwidth != width && dwidth != width * 2) { 183 fprintf(stderr, 184 "Unsupported width %u!\n", dwidth); 185 return (1); 186 } 187 188 /* Move pixel data right to simplify splitting double characters. */ 189 line >>= (howmany(dwidth, 8) * 8) - dwidth; 190 191 for (i = dwidth / width; i > 0; i--) { 192 p = (i == 2) ? right : left; 193 194 subline = line & ((1 << width) - 1); 195 subline <<= (howmany(width, 8) * 8) - width; 196 197 if (wbytes == 1) { 198 *p = subline; 199 } else if (wbytes == 2) { 200 *p++ = subline >> 8; 201 *p = subline; 202 } else { 203 fprintf(stderr, 204 "Unsupported wbytes %u!\n", wbytes); 205 return (1); 206 } 207 208 line >>= width; 209 } 210 211 return (0); 212} 213 214static int 215parse_bdf(const char *filename, unsigned int map_idx) 216{ 217 FILE *fp; 218 char *ln; 219 size_t length; 220 uint8_t bytes[wbytes * height], bytes_r[wbytes * height]; 221 unsigned int curchar = 0, dwidth = 0, i, line; 222 struct glyph *gl; 223 224 fp = fopen(filename, "r"); 225 if (fp == NULL) { 226 perror(filename); 227 return (1); 228 } 229 230 while ((ln = fgetln(fp, &length)) != NULL) { 231 ln[length - 1] = '\0'; 232 233 if (strncmp(ln, "ENCODING ", 9) == 0) { 234 curchar = atoi(ln + 9); 235 } 236 237 if (strncmp(ln, "DWIDTH ", 7) == 0) { 238 dwidth = atoi(ln + 7); 239 } 240 241 if (strcmp(ln, "BITMAP") == 0) { 242 for (i = 0; i < height; i++) { 243 if ((ln = fgetln(fp, &length)) == NULL) { 244 fprintf(stderr, "Unexpected EOF!\n"); 245 return (1); 246 } 247 ln[length - 1] = '\0'; 248 sscanf(ln, "%x", &line); 249 if (parse_bitmap_line(bytes + i * wbytes, 250 bytes_r + i * wbytes, line, dwidth) != 0) 251 return (1); 252 } 253 254 /* Prevent adding two glyphs for 0xFFFD */ 255 if (curchar == 0xFFFD) { 256 if (map_idx < VFNT_MAP_BOLD) 257 gl = add_glyph(bytes, 0, 1); 258 } else if (curchar >= 0x20) { 259 gl = add_glyph(bytes, map_idx, 0); 260 if (add_mapping(gl, curchar, map_idx) != 0) 261 return (1); 262 if (dwidth == width * 2) { 263 gl = add_glyph(bytes_r, map_idx + 1, 0); 264 if (add_mapping(gl, curchar, 265 map_idx + 1) != 0) 266 return (1); 267 } 268 } 269 } 270 } 271 272 return (0); 273} 274 275static void 276number_glyphs(void) 277{ 278 struct glyph *gl; 279 unsigned int i, idx = 0; 280 281 for (i = 0; i < VFNT_MAPS; i++) 282 TAILQ_FOREACH(gl, &glyphs[i], g_list) 283 gl->g_index = idx++; 284} 285 286static void 287write_glyphs(FILE *fp) 288{ 289 struct glyph *gl; 290 unsigned int i; 291 292 for (i = 0; i < VFNT_MAPS; i++) { 293 TAILQ_FOREACH(gl, &glyphs[i], g_list) 294 fwrite(gl->g_data, wbytes * height, 1, fp); 295 } 296} 297 298static void 299fold_mappings(unsigned int map_idx) 300{ 301 struct mapping_list *ml = &maps[map_idx]; 302 struct mapping *mn, *mp, *mbase; 303 304 mp = mbase = TAILQ_FIRST(ml); 305 for (mp = mbase = TAILQ_FIRST(ml); mp != NULL; mp = mn) { 306 mn = TAILQ_NEXT(mp, m_list); 307 if (mn != NULL && mn->m_char == mp->m_char + 1 && 308 mn->m_glyph->g_index == mp->m_glyph->g_index + 1) 309 continue; 310 mbase->m_length = mp->m_char - mbase->m_char + 1; 311 mbase = mp = mn; 312 map_folded_count[map_idx]++; 313 } 314} 315 316struct file_mapping { 317 uint32_t source; 318 uint16_t destination; 319 uint16_t length; 320} __packed; 321 322static void 323write_mappings(FILE *fp, unsigned int map_idx) 324{ 325 struct mapping_list *ml = &maps[map_idx]; 326 struct mapping *mp; 327 struct file_mapping fm; 328 unsigned int i = 0, j = 0; 329 330 TAILQ_FOREACH(mp, ml, m_list) { 331 j++; 332 if (mp->m_length > 0) { 333 i += mp->m_length; 334 fm.source = htobe32(mp->m_char); 335 fm.destination = htobe16(mp->m_glyph->g_index); 336 fm.length = htobe16(mp->m_length - 1); 337 fwrite(&fm, sizeof fm, 1, fp); 338 } 339 } 340 assert(i == j); 341} 342 343struct file_header { 344 uint8_t magic[8]; 345 uint8_t width; 346 uint8_t height; 347 uint16_t pad; 348 uint32_t glyph_count; 349 uint32_t map_count[4]; 350} __packed; 351 352static int 353write_fnt(const char *filename) 354{ 355 FILE *fp; 356 struct file_header fh = { 357 .magic = "VFNT0002", 358 }; 359 360 fp = fopen(filename, "wb"); 361 if (fp == NULL) { 362 perror(filename); 363 return (1); 364 } 365 366 fh.width = width; 367 fh.height = height; 368 fh.glyph_count = htobe32(glyph_unique); 369 fh.map_count[0] = htobe32(map_folded_count[0]); 370 fh.map_count[1] = htobe32(map_folded_count[1]); 371 fh.map_count[2] = htobe32(map_folded_count[2]); 372 fh.map_count[3] = htobe32(map_folded_count[3]); 373 fwrite(&fh, sizeof fh, 1, fp); 374 375 write_glyphs(fp); 376 write_mappings(fp, VFNT_MAP_NORMAL); 377 write_mappings(fp, 1); 378 write_mappings(fp, VFNT_MAP_BOLD); 379 write_mappings(fp, 3); 380 381 return (0); 382} 383 384int 385main(int argc, char *argv[]) 386{ 387 388 assert(sizeof(struct file_header) == 32); 389 assert(sizeof(struct file_mapping) == 8); 390 391 if (argc != 6) 392 usage(); 393 394 width = atoi(argv[1]); 395 wbytes = howmany(width, 8); 396 height = atoi(argv[2]); 397 398 if (parse_bdf(argv[3], VFNT_MAP_NORMAL) != 0) 399 return (1); 400 if (parse_bdf(argv[4], VFNT_MAP_BOLD) != 0) 401 return (1); 402 number_glyphs(); 403 fold_mappings(0); 404 fold_mappings(1); 405 fold_mappings(2); 406 fold_mappings(3); 407 if (write_fnt(argv[5]) != 0) 408 return (1); 409 410 printf( 411"Statistics:\n" 412"- glyph_total: %5u\n" 413"- glyph_normal: %5u\n" 414"- glyph_normal_right: %5u\n" 415"- glyph_bold: %5u\n" 416"- glyph_bold_right: %5u\n" 417"- glyph_unique: %5u\n" 418"- glyph_dupe: %5u\n" 419"- mapping_total: %5u\n" 420"- mapping_normal: %5u\n" 421"- mapping_normal_folded: %5u\n" 422"- mapping_normal_right: %5u\n" 423"- mapping_normal_right_folded: %5u\n" 424"- mapping_bold: %5u\n" 425"- mapping_bold_folded: %5u\n" 426"- mapping_bold_right: %5u\n" 427"- mapping_bold_right_folded: %5u\n" 428"- mapping_unique: %5u\n" 429"- mapping_dupe: %5u\n", 430 glyph_total, 431 glyph_count[0], 432 glyph_count[1], 433 glyph_count[2], 434 glyph_count[3], 435 glyph_unique, glyph_dupe, 436 mapping_total, 437 map_count[0], map_folded_count[0], 438 map_count[1], map_folded_count[1], 439 map_count[2], map_folded_count[2], 440 map_count[3], map_folded_count[3], 441 mapping_unique, mapping_dupe); 442 443 return (0); 444} 445