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();
|