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$"); 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 53 static unsigned int width = 8, wbytes, height = 16; 54 55 struct 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 63 TAILQ_HEAD(glyph_list, glyph); 64 static SLIST_HEAD(, glyph) glyph_hash[FONTCVT_NHASH]; 65 static 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 }; 71 static unsigned int glyph_total, glyph_count[4], glyph_unique, glyph_dupe; 72 73 struct mapping { 74 TAILQ_ENTRY(mapping) m_list; 75 unsigned int m_char; 76 unsigned int m_length; 77 struct glyph *m_glyph; 78 }; 79 80 TAILQ_HEAD(mapping_list, mapping); 81 static 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 }; 87 static unsigned int mapping_total, map_count[4], map_folded_count[4], 88 mapping_unique, mapping_dupe; 89 90 static void 91 usage(void) 92 { 93 94 (void)fprintf(stderr, 95 "usage: vtfontcvt [-w width] [-h height] [-v] normal.bdf [bold.bdf] out.fnt\n"); 96 exit(1); 97 } 98 99 static int 100 add_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 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 TAILQ_INSERT_TAIL(ml, mp, m_list); 117 118 map_count[map_idx]++; 119 mapping_unique++; 120 121 return (0); 122 } 123 124 static int 125 dedup_mapping(unsigned int map_idx) 126 { 127 struct mapping *mp_bold, *mp_normal, *mp_temp; 128 unsigned normal_map_idx = map_idx - VFNT_MAP_BOLD; 129 130 assert(map_idx == VFNT_MAP_BOLD || map_idx == VFNT_MAP_BOLD_RH); 131 mp_normal = TAILQ_FIRST(&maps[normal_map_idx]); 132 TAILQ_FOREACH_SAFE(mp_bold, &maps[map_idx], m_list, mp_temp) { 133 while (mp_normal->m_char < mp_bold->m_char) 134 mp_normal = TAILQ_NEXT(mp_normal, m_list); 135 if (mp_bold->m_char != mp_normal->m_char) 136 errx(1, "Character %u not in normal font!\n", 137 mp_bold->m_char); 138 if (mp_bold->m_glyph != mp_normal->m_glyph) 139 continue; 140 141 /* No mapping is needed if it's equal to the normal mapping. */ 142 TAILQ_REMOVE(&maps[map_idx], mp_bold, m_list); 143 free(mp_bold); 144 mapping_dupe++; 145 } 146 return (0); 147 } 148 149 static struct glyph * 150 add_glyph(const uint8_t *bytes, unsigned int map_idx, int fallback) 151 { 152 struct glyph *gl; 153 int hash; 154 155 glyph_total++; 156 glyph_count[map_idx]++; 157 158 hash = fnv_32_buf(bytes, wbytes * height, FNV1_32_INIT) % FONTCVT_NHASH; 159 SLIST_FOREACH(gl, &glyph_hash[hash], g_hash) { 160 if (memcmp(gl->g_data, bytes, wbytes * height) == 0) { 161 glyph_dupe++; 162 return (gl); 163 } 164 } 165 166 gl = malloc(sizeof *gl); 167 gl->g_data = malloc(wbytes * height); 168 memcpy(gl->g_data, bytes, wbytes * height); 169 if (fallback) 170 TAILQ_INSERT_HEAD(&glyphs[map_idx], gl, g_list); 171 else 172 TAILQ_INSERT_TAIL(&glyphs[map_idx], gl, g_list); 173 SLIST_INSERT_HEAD(&glyph_hash[hash], gl, g_hash); 174 175 glyph_unique++; 176 return (gl); 177 } 178 179 static int 180 add_char(unsigned curchar, unsigned map_idx, uint8_t *bytes, uint8_t *bytes_r) 181 { 182 struct glyph *gl; 183 184 /* Prevent adding two glyphs for 0xFFFD */ 185 if (curchar == 0xFFFD) { 186 if (map_idx < VFNT_MAP_BOLD) 187 gl = add_glyph(bytes, 0, 1); 188 } else if (curchar >= 0x20) { 189 gl = add_glyph(bytes, map_idx, 0); 190 if (add_mapping(gl, curchar, map_idx) != 0) 191 return (1); 192 if (bytes_r != NULL) { 193 gl = add_glyph(bytes_r, map_idx + 1, 0); 194 if (add_mapping(gl, curchar, 195 map_idx + 1) != 0) 196 return (1); 197 } 198 } 199 return (0); 200 } 201 202 203 static int 204 parse_bitmap_line(uint8_t *left, uint8_t *right, unsigned int line, 205 unsigned int dwidth) 206 { 207 uint8_t *p; 208 unsigned int i, subline; 209 210 if (dwidth != width && dwidth != width * 2) 211 errx(1, "Bitmap with unsupported width %u!\n", dwidth); 212 213 /* Move pixel data right to simplify splitting double characters. */ 214 line >>= (howmany(dwidth, 8) * 8) - dwidth; 215 216 for (i = dwidth / width; i > 0; i--) { 217 p = (i == 2) ? right : left; 218 219 subline = line & ((1 << width) - 1); 220 subline <<= (howmany(width, 8) * 8) - width; 221 222 if (wbytes == 1) { 223 *p = subline; 224 } else if (wbytes == 2) { 225 *p++ = subline >> 8; 226 *p = subline; 227 } else { 228 errx(1, "Unsupported wbytes %u!\n", wbytes); 229 } 230 231 line >>= width; 232 } 233 234 return (0); 235 } 236 237 static int 238 parse_bdf(FILE *fp, unsigned int map_idx) 239 { 240 char *ln; 241 size_t length; 242 uint8_t bytes[wbytes * height], bytes_r[wbytes * height]; 243 unsigned int curchar = 0, dwidth = 0, i, line; 244 245 while ((ln = fgetln(fp, &length)) != NULL) { 246 ln[length - 1] = '\0'; 247 248 if (strncmp(ln, "ENCODING ", 9) == 0) { 249 curchar = atoi(ln + 9); 250 } 251 252 if (strncmp(ln, "DWIDTH ", 7) == 0) { 253 dwidth = atoi(ln + 7); 254 } 255 256 if (strncmp(ln, "BITMAP", 6) == 0 && 257 (ln[6] == ' ' || ln[6] == '\0')) { 258 for (i = 0; i < height; i++) { 259 if ((ln = fgetln(fp, &length)) == NULL) 260 errx(1, "Unexpected EOF!\n"); 261 ln[length - 1] = '\0'; 262 sscanf(ln, "%x", &line); 263 if (parse_bitmap_line(bytes + i * wbytes, 264 bytes_r + i * wbytes, line, dwidth) != 0) 265 return (1); 266 } 267 268 if (add_char(curchar, map_idx, bytes, 269 dwidth == width * 2 ? bytes_r : NULL) != 0) 270 return (1); 271 } 272 } 273 274 return (0); 275 } 276 277 static void 278 set_width(int w) 279 { 280 281 if (w <= 0 || w > 128) 282 errx(1, "invalid width %d", w); 283 width = w; 284 wbytes = howmany(width, 8); 285 } 286 287 static int 288 parse_hex(FILE *fp, unsigned int map_idx) 289 { 290 char *ln, *p; 291 char fmt_str[8]; 292 size_t length; 293 uint8_t bytes[wbytes * height], bytes_r[wbytes * height]; 294 unsigned curchar = 0, i, line, chars_per_row, dwidth; 295 296 while ((ln = fgetln(fp, &length)) != NULL) { 297 ln[length - 1] = '\0'; 298 299 if (strncmp(ln, "# Height: ", 10) == 0) { 300 height = atoi(ln + 10); 301 } else if (strncmp(ln, "# Width: ", 9) == 0) { 302 set_width(atoi(ln + 9)); 303 } else if (sscanf(ln, "%4x:", &curchar)) { 304 p = ln + 5; 305 chars_per_row = strlen(p) / height; 306 dwidth = width; 307 if (chars_per_row / 2 > (width + 7) / 8) 308 dwidth *= 2; /* Double-width character. */ 309 snprintf(fmt_str, sizeof(fmt_str), "%%%ux", 310 chars_per_row); 311 312 for (i = 0; i < height; i++) { 313 sscanf(p, fmt_str, &line); 314 p += chars_per_row; 315 if (parse_bitmap_line(bytes + i * wbytes, 316 bytes_r + i * wbytes, line, dwidth) != 0) 317 return (1); 318 } 319 320 if (add_char(curchar, map_idx, bytes, 321 dwidth == width * 2 ? bytes_r : NULL) != 0) 322 return (1); 323 } 324 } 325 return (0); 326 } 327 328 static int 329 parse_file(const char *filename, unsigned int map_idx) 330 { 331 FILE *fp; 332 size_t len; 333 int rv; 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 rv = parse_hex(fp, map_idx); 343 else 344 rv = parse_bdf(fp, map_idx); 345 fclose(fp); 346 return (rv); 347 } 348 349 static void 350 number_glyphs(void) 351 { 352 struct glyph *gl; 353 unsigned int i, idx = 0; 354 355 for (i = 0; i < VFNT_MAPS; i++) 356 TAILQ_FOREACH(gl, &glyphs[i], g_list) 357 gl->g_index = idx++; 358 } 359 360 static int 361 write_glyphs(FILE *fp) 362 { 363 struct glyph *gl; 364 unsigned int i; 365 366 for (i = 0; i < VFNT_MAPS; i++) { 367 TAILQ_FOREACH(gl, &glyphs[i], g_list) 368 if (fwrite(gl->g_data, wbytes * height, 1, fp) != 1) 369 return (1); 370 } 371 return (0); 372 } 373 374 static void 375 fold_mappings(unsigned int map_idx) 376 { 377 struct mapping_list *ml = &maps[map_idx]; 378 struct mapping *mn, *mp, *mbase; 379 380 mp = mbase = TAILQ_FIRST(ml); 381 for (mp = mbase = TAILQ_FIRST(ml); mp != NULL; mp = mn) { 382 mn = TAILQ_NEXT(mp, m_list); 383 if (mn != NULL && mn->m_char == mp->m_char + 1 && 384 mn->m_glyph->g_index == mp->m_glyph->g_index + 1) 385 continue; 386 mbase->m_length = mp->m_char - mbase->m_char + 1; 387 mbase = mp = mn; 388 map_folded_count[map_idx]++; 389 } 390 } 391 392 struct file_mapping { 393 uint32_t source; 394 uint16_t destination; 395 uint16_t length; 396 } __packed; 397 398 static int 399 write_mappings(FILE *fp, unsigned int map_idx) 400 { 401 struct mapping_list *ml = &maps[map_idx]; 402 struct mapping *mp; 403 struct file_mapping fm; 404 unsigned int i = 0, j = 0; 405 406 TAILQ_FOREACH(mp, ml, m_list) { 407 j++; 408 if (mp->m_length > 0) { 409 i += mp->m_length; 410 fm.source = htobe32(mp->m_char); 411 fm.destination = htobe16(mp->m_glyph->g_index); 412 fm.length = htobe16(mp->m_length - 1); 413 if (fwrite(&fm, sizeof fm, 1, fp) != 1) 414 return (1); 415 } 416 } 417 assert(i == j); 418 return (0); 419 } 420 421 struct file_header { 422 uint8_t magic[8]; 423 uint8_t width; 424 uint8_t height; 425 uint16_t pad; 426 uint32_t glyph_count; 427 uint32_t map_count[4]; 428 } __packed; 429 430 static int 431 write_fnt(const char *filename) 432 { 433 FILE *fp; 434 struct file_header fh = { 435 .magic = "VFNT0002", 436 }; 437 438 fp = fopen(filename, "wb"); 439 if (fp == NULL) { 440 perror(filename); 441 return (1); 442 } 443 444 fh.width = width; 445 fh.height = height; 446 fh.glyph_count = htobe32(glyph_unique); 447 fh.map_count[0] = htobe32(map_folded_count[0]); 448 fh.map_count[1] = htobe32(map_folded_count[1]); 449 fh.map_count[2] = htobe32(map_folded_count[2]); 450 fh.map_count[3] = htobe32(map_folded_count[3]); 451 if (fwrite(&fh, sizeof fh, 1, fp) != 1) { 452 perror(filename); 453 fclose(fp); 454 return (1); 455 } 456 457 if (write_glyphs(fp) != 0 || 458 write_mappings(fp, VFNT_MAP_NORMAL) != 0 || 459 write_mappings(fp, 1) != 0 || 460 write_mappings(fp, VFNT_MAP_BOLD) != 0 || 461 write_mappings(fp, 3) != 0) { 462 perror(filename); 463 fclose(fp); 464 return (1); 465 } 466 467 fclose(fp); 468 return (0); 469 } 470 471 static void 472 print_font_info(void) 473 { 474 printf( 475 "Statistics:\n" 476 "- glyph_total: %6u\n" 477 "- glyph_normal: %6u\n" 478 "- glyph_normal_right: %6u\n" 479 "- glyph_bold: %6u\n" 480 "- glyph_bold_right: %6u\n" 481 "- glyph_unique: %6u\n" 482 "- glyph_dupe: %6u\n" 483 "- mapping_total: %6u\n" 484 "- mapping_normal: %6u\n" 485 "- mapping_normal_folded: %6u\n" 486 "- mapping_normal_right: %6u\n" 487 "- mapping_normal_right_folded: %6u\n" 488 "- mapping_bold: %6u\n" 489 "- mapping_bold_folded: %6u\n" 490 "- mapping_bold_right: %6u\n" 491 "- mapping_bold_right_folded: %6u\n" 492 "- mapping_unique: %6u\n" 493 "- mapping_dupe: %6u\n", 494 glyph_total, 495 glyph_count[0], 496 glyph_count[1], 497 glyph_count[2], 498 glyph_count[3], 499 glyph_unique, glyph_dupe, 500 mapping_total, 501 map_count[0], map_folded_count[0], 502 map_count[1], map_folded_count[1], 503 map_count[2], map_folded_count[2], 504 map_count[3], map_folded_count[3], 505 mapping_unique, mapping_dupe); 506 } 507 508 int 509 main(int argc, char *argv[]) 510 { 511 int ch, val, verbose = 0; 512 513 assert(sizeof(struct file_header) == 32); 514 assert(sizeof(struct file_mapping) == 8); 515 516 while ((ch = getopt(argc, argv, "h:vw:")) != -1) { 517 switch (ch) { 518 case 'h': 519 val = atoi(optarg); 520 if (val <= 0 || val > 128) 521 errx(1, "Invalid height %d", val); 522 height = val; 523 break; 524 case 'v': 525 verbose = 1; 526 break; 527 case 'w': 528 set_width(atoi(optarg)); 529 break; 530 case '?': 531 default: 532 usage(); 533 } 534 } 535 argc -= optind; 536 argv += optind; 537 538 if (argc < 2 || argc > 3) 539 usage(); 540 541 wbytes = howmany(width, 8); 542 543 if (parse_file(argv[0], VFNT_MAP_NORMAL) != 0) 544 return (1); 545 argc--; 546 argv++; 547 if (argc == 2) { 548 if (parse_file(argv[0], VFNT_MAP_BOLD) != 0) 549 return (1); 550 argc--; 551 argv++; 552 } 553 number_glyphs(); 554 dedup_mapping(VFNT_MAP_BOLD); 555 dedup_mapping(VFNT_MAP_BOLD_RH); 556 fold_mappings(0); 557 fold_mappings(1); 558 fold_mappings(2); 559 fold_mappings(3); 560 if (write_fnt(argv[0]) != 0) 561 return (1); 562 563 if (verbose) 564 print_font_info(); 565 566 return (0); 567 } 568