mkfontscale.c revision 8b165ca7
1/* 2 Copyright (c) 2002-2003 by Juliusz Chroboczek 3 4 Permission is hereby granted, free of charge, to any person obtaining a copy 5 of this software and associated documentation files (the "Software"), to deal 6 in the Software without restriction, including without limitation the rights 7 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 copies of the Software, and to permit persons to whom the Software is 9 furnished to do so, subject to the following conditions: 10 11 The above copyright notice and this permission notice shall be included in 12 all copies or substantial portions of the Software. 13 14 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 THE SOFTWARE. 21*/ 22 23#ifdef HAVE_CONFIG_H 24#include "config.h" 25#endif 26 27#include <stdio.h> 28#include <stdlib.h> 29#include <string.h> 30 31#include <sys/types.h> 32#include <sys/stat.h> 33#include <dirent.h> 34#include <unistd.h> 35#include <errno.h> 36#include <ctype.h> 37 38#include <X11/Xos.h> 39#include <X11/Xfuncproto.h> 40#include <X11/fonts/fontenc.h> 41#include <ft2build.h> 42#include FT_FREETYPE_H 43#include FT_SFNT_NAMES_H 44#include FT_TRUETYPE_TABLES_H 45#include FT_TRUETYPE_IDS_H 46#include FT_TYPE1_TABLES_H 47#include FT_BDF_H 48#include FT_XFREE86_H 49 50#include "list.h" 51#include "constlist.h" 52#include "hash.h" 53#include "data.h" 54#include "ident.h" 55 56#define NPREFIX 1024 57 58#ifndef MAXFONTFILENAMELEN 59#define MAXFONTFILENAMELEN 1024 60#endif 61#ifndef MAXFONTNAMELEN 62#define MAXFONTNAMELEN 1024 63#endif 64 65/* Two levels of macro calls are needed so that we stringify the value 66 of MAXFONT... and not the string "MAXFONT..." */ 67#define QUOTE(x) #x 68#define STRINGIFY(x) QUOTE(x) 69 70static const char *encodings_array[] = 71 { "ascii-0", 72 "iso8859-1", "iso8859-2", "iso8859-3", "iso8859-4", "iso8859-5", 73 "iso8859-6", "iso8859-6.8", "iso8859-6.8x", "iso8859-6.16", 74 "iso8859-7", "iso8859-8", "iso8859-9", "iso8859-10", 75 "iso8859-11", "iso8859-12", "iso8859-13", "iso8859-14", 76 "iso8859-15", "iso8859-16", 77 "ansi-1251", "koi8-r", "koi8-u", "koi8-ru", "koi8-e", "koi8-uni", 78 "tis620-2", 79 "sun.unicode.india-0", "suneu-greek", 80 "adobe-standard", "adobe-symbol", 81 "ibm-cp437", "ibm-cp850", "ibm-cp852", "ibm-cp866", "microsoft-cp1252", 82 /* But not "adobe-dingbats", as it uses generic glyph names. */ 83 "cns11643-1", "cns11643-2", "cns11643-3", 84 "jisx0201.1976-0", "jisx0208.1983-0", "jisx0208.1990-0", 85 "jisx0212.1990-0", "big5-0", "big5.eten-0", "big5hkscs-0", 86 "gb2312.1980-0", "gb18030.2000-0", "gb18030.2000-1", 87 "ksc5601.1987-0", "ksc5601.1992-3"}; 88 89static const char *extra_encodings_array[] = 90 { "iso10646-1", "adobe-fontspecific", "microsoft-symbol" }; 91 92static ConstListPtr encodings, extra_encodings; 93static const char *outfilename; 94 95#define countof(_a) (sizeof(_a)/sizeof((_a)[0])) 96 97static int doDirectory(const char*, int, ListPtr); 98static int checkEncoding(FT_Face face, const char *encoding_name); 99static int checkExtraEncoding(FT_Face face, const char *encoding_name, int found); 100static int find_cmap(int type, int pid, int eid, FT_Face face); 101static const char* notice_foundry(const char *notice); 102static const char* vendor_foundry(const signed char *vendor); 103static int readFontScale(HashTablePtr entries, char *dirname); 104ListPtr makeXLFD(char *filename, FT_Face face, int); 105static int readEncodings(ListPtr encodings, char *dirname); 106 107static FT_Library ft_library; 108static float bigEncodingFuzz = 0.02; 109 110static int relative; 111static int doScalable; 112static int doBitmaps; 113static int doISO10646_1_encoding; 114static int onlyEncodings; 115static ListPtr encodingsToDo; 116static int reencodeLegacy; 117static char *encodingPrefix; 118static char *exclusionSuffix; 119static char *ProgramName; 120 121static void _X_NORETURN _X_COLD 122usage(void) 123{ 124 fprintf(stderr, "Usage:\n" 125 "mkfontscale [ -b ] [ -s ] [ -o filename ] [-x suffix ]\n" 126 " [ -a encoding ] [ -f fuzz ] [ -l ]\n" 127 " [ -e directory ] [ -p prefix ] [ -n ] [ -r ] \n" 128 " [-u] [-U] [-v] [ directory ]...\n"); 129 exit(1); 130} 131 132static void _X_NORETURN _X_COLD 133missing_arg (const char *option) 134{ 135 fprintf(stderr, "%s: %s requires an argument\n", ProgramName, option); 136 usage(); 137} 138 139int 140main(int argc, char **argv) 141{ 142 int argn; 143 FT_Error ftrc; 144 int rc, ll = 0; 145 char prefix[NPREFIX]; 146 147 ProgramName = argv[0]; 148 encodingPrefix = NULL; 149 exclusionSuffix = NULL; 150 151 if(getcwd(prefix, NPREFIX - 1) == NULL) { 152 perror("Couldn't get cwd"); 153 exit(1); 154 } 155 if(prefix[strlen(prefix) - 1] != '/') 156 encodingPrefix = dsprintf("%s/", prefix); 157 else 158 encodingPrefix = strdup(prefix); 159 160 outfilename = NULL; 161 162 encodings = makeConstList(encodings_array, countof(encodings_array), NULL, 0); 163 164 extra_encodings = makeConstList(extra_encodings_array, 165 countof(extra_encodings_array), 166 NULL, 0); 167 doBitmaps = 0; 168 doISO10646_1_encoding = 1; 169 doScalable = 1; 170 onlyEncodings = 0; 171 relative = 0; 172 reencodeLegacy = 1; 173 encodingsToDo = NULL; 174 175 argn = 1; 176 while(argn < argc) { 177 if(argv[argn][0] == '\0' || argv[argn][0] != '-') 178 break; 179 if(argv[argn][1] == '-') { 180 argn++; 181 break; 182 } else if (strcmp(argv[argn], "-x") == 0) { 183 if(argn >= argc - 1) { 184 missing_arg("-x"); 185 } 186 exclusionSuffix = argv[argn + 1]; 187 argn += 2; 188 } else if(strcmp(argv[argn], "-a") == 0) { 189 if(argn >= argc - 1) { 190 missing_arg("-a"); 191 } 192 makeConstList((const char **)&argv[argn + 1], 1, encodings, 0); 193 argn += 2; 194 } else if(strcmp(argv[argn], "-p") == 0) { 195 if(argn >= argc - 1) { 196 missing_arg("-p"); 197 } 198 if(strlen(argv[argn + 1]) > NPREFIX - 1) { 199 fprintf(stderr, "%s: argument to -p cannot be longer than " 200 "%d characters\n", ProgramName, NPREFIX - 1); 201 usage(); 202 } 203 free(encodingPrefix); 204 encodingPrefix = strdup(argv[argn + 1]); 205 argn += 2; 206 } else if(strcmp(argv[argn], "-e") == 0) { 207 if(argn >= argc - 1) { 208 missing_arg("-e"); 209 } 210 rc = readEncodings(encodingsToDo, argv[argn + 1]); 211 if(rc < 0) 212 exit(1); 213 argn += 2; 214 } else if(strcmp(argv[argn], "-b") == 0) { 215 doBitmaps = 1; 216 argn++; 217 } else if(strcmp(argv[argn], "-u") == 0) { 218 doISO10646_1_encoding = 0; 219 argn++; 220 } else if(strcmp(argv[argn], "-U") == 0) { 221 doISO10646_1_encoding = 1; 222 argn++; 223 } else if(strcmp(argv[argn], "-s") == 0) { 224 doScalable = 0; 225 argn++; 226 } else if(strcmp(argv[argn], "-n") == 0) { 227 onlyEncodings = 1; 228 argn++; 229 } else if(strcmp(argv[argn], "-r") == 0) { 230 relative = 1; 231 argn++; 232 } else if(strcmp(argv[argn], "-l") == 0) { 233 reencodeLegacy = !reencodeLegacy; 234 argn++; 235 } else if(strcmp(argv[argn], "-o") == 0) { 236 if(argn >= argc - 1) { 237 missing_arg("-o"); 238 } 239 outfilename = argv[argn + 1]; 240 argn += 2; 241 } else if(strcmp(argv[argn], "-f") == 0) { 242 if(argn >= argc - 1) { 243 missing_arg("-f"); 244 } 245 bigEncodingFuzz = atof(argv[argn + 1]) / 100.0; 246 argn += 2; 247 } else if (strcmp(argv[argn], "-v") == 0) { 248 printf("%s\n", PACKAGE_STRING); 249 exit(0); 250 } else { 251 usage(); 252 } 253 } 254 255 if(outfilename == NULL) { 256 if(doBitmaps) 257 outfilename = "fonts.dir"; 258 else 259 outfilename = "fonts.scale"; 260 } 261 262 ftrc = FT_Init_FreeType(&ft_library); 263 if(ftrc) { 264 fprintf(stderr, "Could not initialise FreeType library: %d\n", ftrc); 265 exit(1); 266 } 267 268 ll = listLength(encodingsToDo); 269 270 if (argn == argc) 271 doDirectory(".", ll, encodingsToDo); 272 else 273 while(argn < argc) { 274 doDirectory(argv[argn], ll, encodingsToDo); 275 argn++; 276 } 277 return 0; 278} 279 280static int 281getNameHelper(FT_Face face, int nid, int pid, int eid, 282 FT_SfntName *name_return) 283{ 284 FT_SfntName name; 285 int n, i; 286 287 n = FT_Get_Sfnt_Name_Count(face); 288 if(n <= 0) 289 return 0; 290 291 for(i = 0; i < n; i++) { 292 if(FT_Get_Sfnt_Name(face, i, &name)) 293 continue; 294 if(name.name_id == nid && 295 name.platform_id == pid && 296 (eid < 0 || name.encoding_id == eid)) { 297 switch(name.platform_id) { 298 case TT_PLATFORM_APPLE_UNICODE: 299 case TT_PLATFORM_MACINTOSH: 300 if(name.language_id != TT_MAC_LANGID_ENGLISH) 301 continue; 302 break; 303 case TT_PLATFORM_MICROSOFT: 304 if(name.language_id != TT_MS_LANGID_ENGLISH_UNITED_STATES && 305 name.language_id != TT_MS_LANGID_ENGLISH_UNITED_KINGDOM) 306 continue; 307 break; 308 default: 309 continue; 310 } 311 if(name.string_len > 0) { 312 *name_return = name; 313 return 1; 314 } 315 } 316 } 317 return 0; 318} 319 320static char * 321getName(FT_Face face, int nid) 322{ 323 FT_SfntName name; 324 char *string; 325 unsigned int i; 326 327 if(getNameHelper(face, nid, 328 TT_PLATFORM_MICROSOFT, TT_MS_ID_UNICODE_CS, &name) || 329 getNameHelper(face, nid, 330 TT_PLATFORM_APPLE_UNICODE, -1, &name)) { 331 string = malloc(name.string_len / 2 + 1); 332 if(string == NULL) { 333 fprintf(stderr, "Couldn't allocate name\n"); 334 exit(1); 335 } 336 for(i = 0; i < name.string_len / 2; i++) { 337 if(name.string[2 * i] != 0) 338 string[i] = '?'; 339 else 340 string[i] = name.string[2 * i + 1]; 341 } 342 string[i] = '\0'; 343 return string; 344 } 345 346 /* Pretend that Apple Roman is ISO 8859-1. */ 347 if(getNameHelper(face, nid, TT_PLATFORM_MACINTOSH, TT_MAC_ID_ROMAN, 348 &name)) { 349 string = malloc(name.string_len + 1); 350 if(string == NULL) { 351 fprintf(stderr, "Couldn't allocate name\n"); 352 exit(1); 353 } 354 memcpy(string, name.string, name.string_len); 355 string[name.string_len] = '\0'; 356 return string; 357 } 358 359 return NULL; 360} 361 362static const char* 363os2Weight(int weight) 364{ 365 if(weight < 150) 366 return "thin"; 367 else if(weight < 250) 368 return "extralight"; 369 else if(weight < 350) 370 return "light"; 371 else if(weight < 450) 372 return "medium"; /* officially "normal" */ 373 else if(weight < 550) 374 return "medium"; 375 else if(weight < 650) 376 return "semibold"; 377 else if(weight < 750) 378 return "bold"; 379 else if(weight < 850) 380 return "extrabold"; 381 else 382 return "black"; 383} 384 385static const char* 386os2Width(int width) 387{ 388 if(width <= 1) 389 return "ultracondensed"; 390 else if(width <= 2) 391 return "extracondensed"; 392 else if(width <= 3) 393 return "condensed"; 394 else if(width <= 4) 395 return "semicondensed"; 396 else if(width <= 5) 397 return "normal"; 398 else if(width <= 6) 399 return "semiexpanded"; 400 else if(width <= 7) 401 return "expanded"; 402 else if(width <= 8) 403 return "extraexpanded"; 404 else 405 return "ultraexpanded"; 406} 407 408static const char *widths[] = { 409 "ultracondensed", "extracondensed", "condensed", "semicondensed", 410 "normal", "semiexpanded", "expanded", "extraexpanded", "ultraexpanded" 411}; 412 413#define NUMWIDTHS (sizeof(widths) / sizeof(widths[0])) 414 415static const char* 416nameWidth(const char *name) 417{ 418 char buf[500]; 419 unsigned int i; 420 size_t n = strlen(name); 421 422 if(n >= 499) return NULL; 423 for(i = 0; i < n; i++) 424 buf[i] = tolower(name[i]); 425 buf[i] = '\0'; 426 427 for(i = 0; i < NUMWIDTHS; i++) 428 if(strstr(buf, widths[i])) 429 return widths[i]; 430 return NULL; 431} 432 433static const char* 434t1Weight(const char *weight) 435{ 436 if(!weight) 437 return NULL; 438 if(strcmp(weight, "Thin") == 0) 439 return "thin"; 440 if(strcmp(weight, "ExtraLight") == 0) /* FontForge uses this for 200*/ 441 return "extralight"; 442 if(strcmp(weight, "Light") == 0) 443 return "light"; 444 if(strcmp(weight, "Regular") == 0) 445 return "medium"; 446 if(strcmp(weight, "Normal") == 0) 447 return "medium"; 448 if(strcmp(weight, "Medium") == 0) 449 return "medium"; 450 if(strcmp(weight, "Book") == 0) 451 return "medium"; 452 if(strcmp(weight, "Roman") == 0) /* Some URW++ fonts do that! */ 453 return "medium"; 454 if(strcmp(weight, "Demi") == 0) 455 return "semibold"; 456 if(strcmp(weight, "DemiBold") == 0) 457 return "semibold"; 458 if(strcmp(weight, "SemiBold") == 0) /* some TeX fonts apparently do that */ 459 return "semibold"; 460 else if(strcmp(weight, "Bold") == 0) 461 return "bold"; 462 else if(strcmp(weight, "Heavy") == 0) /* FontForge uses this for 800*/ 463 return "extrabold"; 464 else if(strcmp(weight, "Black") == 0) 465 return "black"; 466 else { 467 fprintf(stderr, "Unknown Type 1 weight \"%s\"\n", weight); 468 return NULL; 469 } 470} 471 472static int 473unsafe(char c) 474{ 475 return 476 c < 0x20 || c > 0x7E || 477 c == '[' || c == ']' || c == '(' || c == ')' || c == '\\' || c == '-'; 478} 479 480static const char * 481safe(const char* s) 482{ 483 int i, len, safe_flag = 1; 484 char *t; 485 486 i = 0; 487 while(s[i] != '\0') { 488 if(unsafe(s[i])) 489 safe_flag = 0; 490 i++; 491 } 492 493 if(safe_flag) return strdup(s); 494 495 len = i; 496 t = malloc(len + 1); 497 if(t == NULL) { 498 perror("Couldn't allocate string"); 499 exit(1); 500 } 501 502 for(i = 0; i < len; i++) { 503 if(unsafe(s[i])) 504 t[i] = ' '; 505 else 506 t[i] = s[i]; 507 } 508 t[i] = '\0'; 509 return t; 510} 511 512ListPtr 513makeXLFD(char *filename, FT_Face face, int isBitmap) 514{ 515 ListPtr xlfd = NULL; 516 const char *foundry, *family, *weight, *slant, *sWidth, *adstyle, 517 *spacing, *full_name, *tmp; 518 TT_Header *head; 519 TT_HoriHeader *hhea; 520 TT_OS2 *os2; 521 TT_Postscript *post; 522 PS_FontInfoRec *t1info, t1info_rec; 523 int rc; 524 525 foundry = NULL; 526 family = NULL; 527 weight = NULL; 528 slant = NULL; 529 sWidth = NULL; 530 adstyle = NULL; 531 spacing = NULL; 532 full_name = NULL; 533 534 head = FT_Get_Sfnt_Table(face, ft_sfnt_head); 535 hhea = FT_Get_Sfnt_Table(face, ft_sfnt_hhea); 536 os2 = FT_Get_Sfnt_Table(face, ft_sfnt_os2); 537 post = FT_Get_Sfnt_Table(face, ft_sfnt_post); 538 539 rc = FT_Get_PS_Font_Info(face, &t1info_rec); 540 if(rc == 0) 541 t1info = &t1info_rec; 542 else 543 t1info = NULL; 544 545 if(!family) 546 family = getName(face, TT_NAME_ID_FONT_FAMILY); 547 if(!family) 548 family = getName(face, TT_NAME_ID_FULL_NAME); 549 if(!family) 550 family = getName(face, TT_NAME_ID_PS_NAME); 551 552 if(!full_name) 553 full_name = getName(face, TT_NAME_ID_FULL_NAME); 554 if(!full_name) 555 full_name = getName(face, TT_NAME_ID_PS_NAME); 556 557 if(os2 && os2->version != 0xFFFF) { 558 if(!weight) 559 weight = os2Weight(os2->usWeightClass); 560 if(!sWidth) 561 sWidth = os2Width(os2->usWidthClass); 562 if(!foundry) 563 foundry = vendor_foundry(os2->achVendID); 564 if(!slant) 565 slant = os2->fsSelection & 1 ? "i" : "r"; 566 } 567 568 if(post) { 569 if(!spacing) { 570 if(post->isFixedPitch) { 571 if(hhea->min_Left_Side_Bearing >= 0 && 572 hhea->xMax_Extent <= hhea->advance_Width_Max) { 573 spacing = "c"; 574 } else { 575 spacing = "m"; 576 } 577 } else { 578 spacing = "p"; 579 } 580 } 581 } 582 583 if(t1info) { 584 if(!family && t1info->family_name) 585 family = strdup(t1info->family_name); 586 if(!family && t1info->full_name) 587 family = strdup(t1info->full_name); 588 /* Hershey fonts miss /FamilyName */ 589 if(!family && face->family_name) 590 family = strdup(face->family_name); 591 if(!full_name && t1info->full_name) 592 full_name = strdup(t1info->full_name); 593 if(!foundry) 594 foundry = notice_foundry(t1info->notice); 595 if(!weight) 596 weight = t1Weight(t1info->weight); 597 if(!spacing) 598 spacing = t1info->is_fixed_pitch ? "m" : "p"; 599 if(!slant) { 600 /* Bitstream fonts have positive italic angle. */ 601 slant = 602 t1info->italic_angle <= -4 || t1info->italic_angle >= 4 ? 603 "i" : "r"; 604 } 605 } 606 607 if(!full_name) { 608 fprintf(stderr, "Couldn't determine full name for %s\n", filename); 609 full_name = strdup(filename); 610 } 611 612 if(head) { 613 if(!slant) 614 slant = head->Mac_Style & 2 ? "i" : "r"; 615 if(!weight) 616 weight = head->Mac_Style & 1 ? "bold" : "medium"; 617 } 618 619 if(!slant) { 620 fprintf(stderr, "Couldn't determine slant for %s\n", filename); 621 slant = "r"; 622 } 623 624 if(!weight) { 625 fprintf(stderr, "Couldn't determine weight for %s\n", filename); 626 weight = "medium"; 627 } 628 629 if(!foundry) { 630 char *notice; 631 notice = getName(face, TT_NAME_ID_TRADEMARK); 632 if(notice) { 633 foundry = notice_foundry(notice); 634 free(notice); 635 } 636 if(!foundry) { 637 notice = getName(face, TT_NAME_ID_MANUFACTURER); 638 if(notice) { 639 foundry = notice_foundry(notice); 640 free(notice); 641 } 642 } 643 } 644 645 if(strcmp(slant, "i") == 0) { 646 if(strstr(full_name, "Oblique")) 647 slant = "o"; 648 if(strstr(full_name, "Slanted")) 649 slant = "o"; 650 } 651 652 if(!sWidth) 653 sWidth = nameWidth(full_name); 654 655 if(!foundry) foundry = "misc"; 656 if(!family) { 657 fprintf(stderr, "Couldn't get family name for %s\n", filename); 658 family = strdup(filename); 659 } 660 661 if(!weight) weight = "medium"; 662 if(!slant) slant = "r"; 663 if(!sWidth) sWidth = "normal"; 664 if(!adstyle) adstyle = ""; 665 if(!spacing) spacing = "p"; 666 667 foundry = safe(foundry); 668 669 tmp = family; 670 family = safe(family); 671 free((void *)tmp); 672 673 if(!isBitmap) { 674 xlfd = listConsF(xlfd, 675 "-%s-%s-%s-%s-%s-%s-0-0-0-0-%s-0", 676 foundry, family, 677 weight, slant, sWidth, adstyle, spacing); 678 } else { 679 int i, w, h, xres, yres; 680 for(i = 0; i < face->num_fixed_sizes; i++) { 681 w = face->available_sizes[i].width; 682 h = face->available_sizes[i].height; 683 xres = 75; 684 yres = (double)h / w * xres; 685 xlfd = listConsF(xlfd, 686 "-%s-%s-%s-%s-%s-%s-%d-%d-%d-%d-%s-%d", 687 foundry, family, 688 weight, slant, sWidth, adstyle, 689 h, (int)(h / (double)yres * 72.27 * 10 + 0.5), 690 xres, yres, 691 spacing, 60); 692 } 693 } 694 695 free((void *)family); 696 free((void *)foundry); 697 free((void *)full_name); 698 return xlfd; 699} 700 701static int 702readFontScale(HashTablePtr entries, char *dirname) 703{ 704 size_t n = strlen(dirname); 705 char *filename; 706 FILE *in; 707 int rc, count, i; 708 char file[MAXFONTFILENAMELEN+1], font[MAXFONTNAMELEN+1]; 709 710 if(dirname[n - 1] == '/') 711 filename = dsprintf("%sfonts.scale", dirname); 712 else 713 filename = dsprintf("%s/fonts.scale", dirname); 714 if(filename == NULL) 715 return -1; 716 717 in = fopen(filename, "r"); 718 free(filename); 719 if(in == NULL) { 720 if(errno != ENOENT) 721 perror("open(fonts.scale)"); 722 return -1; 723 } 724 725 rc = fscanf(in, "%d\n", &count); 726 if(rc != 1) { 727 fprintf(stderr, "Invalid fonts.scale in %s.\n", dirname); 728 fclose(in); 729 return -1; 730 } 731 732 for(i = 0; i < count; i++) { 733 rc = fscanf(in, 734 "%" STRINGIFY(MAXFONTFILENAMELEN) "s " 735 "%" STRINGIFY(MAXFONTNAMELEN) "[^\n]\n", 736 file, font); 737 if(rc != 2) 738 break; 739 putHash(entries, font, file, 100); 740 } 741 fclose(in); 742 return 1; 743} 744 745static int 746filePrio(char *filename) 747{ 748 size_t n = strlen(filename); 749 if(n < 4) 750 return 0; 751 if(strcmp(filename + n - 4, ".otf") == 0) 752 return 6; 753 if(strcmp(filename + n - 4, ".OTF") == 0) 754 return 6; 755 if(strcmp(filename + n - 4, ".ttf") == 0) 756 return 5; 757 if(strcmp(filename + n - 4, ".TTF") == 0) 758 return 5; 759 if(strcmp(filename + n - 4, ".pcf") == 0) 760 return 4; 761 if(strcmp(filename + n - 4, ".PCF") == 0) 762 return 4; 763 if(strcmp(filename + n - 3, ".gz") == 0) 764 return 3; 765#ifdef X_BZIP2_FONT_COMPRESSION 766 if(strcmp(filename + n - 4, ".bz2") == 0) 767 return 2; 768#endif 769 if(strcmp(filename + n - 2, ".Z") == 0) 770 return 2; 771 if(strcmp(filename + n - 4, ".bdf") == 0) 772 return 1; 773 if(strcmp(filename + n - 4, ".BDF") == 0) 774 return 1; 775 return 0; 776} 777 778static int 779doDirectory(const char *dirname_given, int numEncodings, ListPtr encodingsToDo) 780{ 781 char *dirname, *fontscale_name, *filename, *encdir; 782 FILE *fontscale, *encfile; 783 struct dirent** namelist; 784 FT_Error ftrc; 785 FT_Face face; 786 ConstListPtr encoding; 787 ListPtr xlfd, lp; 788 HashTablePtr entries; 789 HashBucketPtr *array; 790 int i, n, dirn, diri, found, rc; 791 int isBitmap=0; 792 size_t d, xl=0; 793 794 if (exclusionSuffix) 795 xl = strlen (exclusionSuffix); 796 797 d = strlen(dirname_given); 798 if(d == 0) 799 dirname = dsprintf("./"); 800 else if(dirname_given[d - 1] != '/') 801 dirname = dsprintf("%s/", dirname_given); 802 else 803 dirname = strdup(dirname_given); 804 805 if(dirname == NULL) { 806 perror("dirname"); 807 exit(1); 808 } 809 810 if (onlyEncodings) 811 goto encodings; 812 813 entries = makeHashTable(); 814 if(doBitmaps && !doScalable) { 815 readFontScale(entries, dirname); 816 } 817 818 if(strcmp(outfilename, "-") == 0) 819 fontscale_name = NULL; 820 else { 821 if(outfilename[0] == '/') 822 fontscale_name = strdup(outfilename); 823 else 824 fontscale_name = dsprintf("%s%s", dirname, outfilename); 825 if(fontscale_name == NULL) { 826 perror("fontscale_name"); 827 exit(1); 828 } 829 } 830 831 dirn = scandir(dirname, &namelist, NULL, alphasort); 832 if(dirn < 0) { 833 fprintf(stderr, "%s: ", dirname); 834 perror("scandir"); 835 return 0; 836 } 837 838 if(fontscale_name == NULL) 839 fontscale = stdout; 840 else 841 fontscale = fopen(fontscale_name, "wb"); 842 843 if(fontscale == NULL) { 844 fprintf(stderr, "%s: ", fontscale_name); 845 perror("fopen(w)"); 846 return 0; 847 } 848 849 for(diri = dirn - 1; diri >= 0; diri--) { 850 struct dirent *entry = namelist[diri]; 851 int have_face = 0; 852 char *xlfd_name = NULL; 853 struct stat f_stat; 854 int tprio = 1; 855 856 xlfd = NULL; 857 858 if (xl) { 859 size_t dl = strlen (entry->d_name); 860 if (strcmp (entry->d_name + dl - xl, exclusionSuffix) == 0) 861 continue; 862 } 863 864 filename = dsprintf("%s%s", dirname, entry->d_name); 865 866#define PRIO(x) ((x << 1) + tprio) 867#ifdef DT_LNK 868 if (entry->d_type != DT_UNKNOWN) { 869 if (entry->d_type == DT_LNK) 870 tprio = 0; 871 } else 872#endif 873#ifdef S_ISLNK 874 { 875 if (lstat(filename, &f_stat)) 876 goto done; 877 if (S_ISLNK(f_stat.st_mode)) 878 tprio = 0; 879 } 880#else 881 ; 882#endif 883 if(doBitmaps) 884 rc = bitmapIdentify(filename, &xlfd_name); 885 else 886 rc = 0; 887 888 if(rc < 0) 889 goto done; 890 891 if(rc == 0) { 892 ftrc = FT_New_Face(ft_library, filename, 0, &face); 893 if(ftrc) 894 goto done; 895 have_face = 1; 896 897 isBitmap = ((face->face_flags & FT_FACE_FLAG_SCALABLE) == 0); 898 899 if(!isBitmap) { 900 /* Workaround for bitmap-only SFNT fonts */ 901 if(FT_IS_SFNT(face) && face->num_fixed_sizes > 0 && 902 strcmp(FT_Get_X11_Font_Format(face), "TrueType") == 0) { 903 TT_MaxProfile *maxp; 904 maxp = FT_Get_Sfnt_Table(face, ft_sfnt_maxp); 905 if(maxp != NULL && maxp->maxContours == 0) 906 isBitmap = 1; 907 } 908 } 909 910 if(isBitmap) { 911 if(!doBitmaps) 912 goto done; 913 } else { 914 if(!doScalable) 915 goto done; 916 } 917 918 if(isBitmap) { 919 BDF_PropertyRec prop; 920 rc = FT_Get_BDF_Property(face, "FONT", &prop); 921 if(rc == 0 && prop.type == BDF_PROPERTY_TYPE_ATOM) { 922 xlfd_name = strdup(prop.u.atom); 923 if(xlfd_name == NULL) 924 goto done; 925 } 926 } 927 } 928 929 if(xlfd_name) { 930 /* We know it's a bitmap font, and we know its XLFD */ 931 size_t l = strlen(xlfd_name); 932 if(reencodeLegacy && 933 l >= 12 && strcasecmp(xlfd_name + l - 11, "-iso10646-1") == 0) { 934 char *s; 935 936 s = malloc(l - 10); 937 memcpy(s, xlfd_name, l - 11); 938 s[l - 11] = '\0'; 939 xlfd = listCons(s, xlfd); 940 } else { 941 /* Not a reencodable font -- skip all the rest of the loop body */ 942 putHash(entries, xlfd_name, entry->d_name, PRIO(filePrio(entry->d_name))); 943 goto done; 944 } 945 } 946 947 if(!have_face) { 948 ftrc = FT_New_Face(ft_library, filename, 0, &face); 949 if(ftrc) 950 goto done; 951 have_face = 1; 952 isBitmap = ((face->face_flags & FT_FACE_FLAG_SCALABLE) == 0); 953 954 if(!isBitmap) { 955 if(face->num_fixed_sizes > 0) { 956 TT_MaxProfile *maxp; 957 maxp = FT_Get_Sfnt_Table(face, ft_sfnt_maxp); 958 if(maxp != NULL && maxp->maxContours == 0) 959 isBitmap = 1; 960 } 961 } 962 } 963 964 if(xlfd == NULL) 965 xlfd = makeXLFD(entry->d_name, face, isBitmap); 966 967 found = 0; 968 969 for(lp = xlfd; lp; lp = lp->next) { 970 char buf[MAXFONTNAMELEN]; 971 for(encoding = encodings; encoding; encoding = encoding->next) { 972 if(checkEncoding(face, encoding->value)) { 973 found = 1; 974 snprintf(buf, MAXFONTNAMELEN, "%s-%s", 975 lp->value, encoding->value); 976 putHash(entries, buf, entry->d_name, PRIO(filePrio(entry->d_name))); 977 } 978 } 979 for(encoding = extra_encodings; encoding; 980 encoding = encoding->next) { 981 if(checkExtraEncoding(face, encoding->value, found)) { 982 /* Do not set found! */ 983 snprintf(buf, MAXFONTNAMELEN, "%s-%s", 984 lp->value, encoding->value); 985 putHash(entries, buf, entry->d_name, PRIO(filePrio(entry->d_name))); 986 } 987 } 988 } 989 done: 990 if(have_face) 991 FT_Done_Face(face); 992 deepDestroyList(xlfd); 993 xlfd = NULL; 994 free(filename); 995#undef PRIO 996 } 997 998 while(dirn--) 999 free(namelist[dirn]); 1000 free(namelist); 1001 n = hashElements(entries); 1002 fprintf(fontscale, "%d\n", n); 1003 array = hashArray(entries, 1); 1004 for(i = 0; i < n; i++) 1005 fprintf(fontscale, "%s %s\n", array[i]->value, array[i]->key); 1006 destroyHashArray(array); 1007 entries = NULL; 1008 if(fontscale_name) { 1009 fclose(fontscale); 1010 free(fontscale_name); 1011 } 1012 1013 encodings: 1014 encdir = dsprintf("%s%s", dirname, "encodings.dir"); 1015 1016 if(encdir == NULL) { 1017 perror("encodings"); 1018 exit(1); 1019 } 1020 unlink(encdir); 1021 1022 if (numEncodings) { 1023 encfile = fopen(encdir, "w"); 1024 if(encfile == NULL) { 1025 perror("open(encodings.dir)"); 1026 exit(1); 1027 } 1028 fprintf(encfile, "%d\n", numEncodings); 1029 encodingsToDo = sortList(encodingsToDo); 1030 for(lp = encodingsToDo; lp; lp = lp->next) { 1031 fprintf(encfile, "%s\n", lp->value); 1032 } 1033 fclose (encfile); 1034 } 1035 1036 free(dirname); 1037 return 1; 1038} 1039 1040#define CODE_IGNORED(c) ((c) < 0x20 || \ 1041 ((c) >= 0x7F && (c) <= 0xA0) || \ 1042 (c) == 0xAD || (c) == 0xF71B) 1043 1044static int 1045checkEncoding(FT_Face face, const char *encoding_name) 1046{ 1047 FontEncPtr encoding; 1048 FontMapPtr mapping; 1049 int i, j, c, koi8; 1050 char *n; 1051 1052 encoding = FontEncFind(encoding_name, NULL); 1053 if(!encoding) 1054 return 0; 1055 1056 /* An encoding is ``small'' if one of the following is true: 1057 - it is linear and has no more than 256 codepoints; or 1058 - it is a matrix encoding and has no more than one column. 1059 1060 For small encodings using Unicode indices, we require perfect 1061 coverage except for CODE_IGNORED and KOI-8 IBM-PC compatibility. 1062 1063 For large encodings, we require coverage up to bigEncodingFuzz. 1064 1065 For encodings using PS names (currently Adobe Standard and 1066 Adobe Symbol only), we require perfect coverage. */ 1067 1068 1069 if(FT_Has_PS_Glyph_Names(face)) { 1070 for(mapping = encoding->mappings; mapping; mapping = mapping->next) { 1071 if(mapping->type == FONT_ENCODING_POSTSCRIPT) { 1072 if(encoding->row_size > 0) { 1073 for(i = encoding->first; i < encoding->size; i++) { 1074 for(j = encoding->first_col; 1075 j < encoding->row_size; 1076 j++) { 1077 n = FontEncName((i<<8) | j, mapping); 1078 if(n && FT_Get_Name_Index(face, n) == 0) { 1079 return 0; 1080 } 1081 } 1082 } 1083 return 1; 1084 } else { 1085 for(i = encoding->first; i < encoding->size; i++) { 1086 n = FontEncName(i, mapping); 1087 if(n && FT_Get_Name_Index(face, n) == 0) { 1088 return 0; 1089 } 1090 } 1091 return 1; 1092 } 1093 } 1094 } 1095 } 1096 1097 for(mapping = encoding->mappings; mapping; mapping = mapping->next) { 1098 if(find_cmap(mapping->type, mapping->pid, mapping->eid, face)) { 1099 int total = 0, failed = 0; 1100 if(encoding->row_size > 0) { 1101 int estimate = 1102 (encoding->size - encoding->first) * 1103 (encoding->row_size - encoding->first_col); 1104 for(i = encoding->first; i < encoding->size; i++) { 1105 for(j = encoding->first_col; 1106 j < encoding->row_size; 1107 j++) { 1108 c = FontEncRecode((i<<8) | j, mapping); 1109 if(CODE_IGNORED(c)) { 1110 continue; 1111 } else { 1112 if(FT_Get_Char_Index(face, c) == 0) { 1113 failed++; 1114 } 1115 total++; 1116 if((encoding->size <= 1 && failed > 0) || 1117 ((float)failed >= bigEncodingFuzz * estimate)) { 1118 return 0; 1119 } 1120 } 1121 } 1122 } 1123 if((float)failed >= total * bigEncodingFuzz) 1124 return 0; 1125 else 1126 return 1; 1127 } else { 1128 int estimate = encoding->size - encoding->first; 1129 /* For the KOI8 encodings, we ignore the lack of 1130 linedrawing and pseudo-math characters */ 1131 if(strncmp(encoding->name, "koi8-", 5) == 0) 1132 koi8 = 1; 1133 else 1134 koi8 = 0; 1135 for(i = encoding->first; i < encoding->size; i++) { 1136 c = FontEncRecode(i, mapping); 1137 if(CODE_IGNORED(c) || 1138 (koi8 && ((c >= 0x2200 && c < 0x2600) || c == 0x00b2))) { 1139 continue; 1140 } else { 1141 if(FT_Get_Char_Index(face, c) == 0) { 1142 failed++; 1143 } 1144 total++; 1145 if((encoding->size <= 256 && failed > 0) || 1146 ((float)failed >= bigEncodingFuzz * estimate)) { 1147 return 0; 1148 } 1149 } 1150 } 1151 if((float)failed >= total * bigEncodingFuzz) 1152 return 0; 1153 else 1154 return 1; 1155 } 1156 } 1157 } 1158 return 0; 1159} 1160 1161static int 1162find_cmap(int type, int pid, int eid, FT_Face face) 1163{ 1164 int i, n, rc; 1165 FT_CharMap cmap = NULL; 1166 1167 n = face->num_charmaps; 1168 1169 switch(type) { 1170 case FONT_ENCODING_TRUETYPE: /* specific cmap */ 1171 for(i=0; i<n; i++) { 1172 cmap = face->charmaps[i]; 1173 if(cmap->platform_id == pid && cmap->encoding_id == eid) { 1174 rc = FT_Set_Charmap(face, cmap); 1175 if(rc == 0) 1176 return 1; 1177 } 1178 } 1179 break; 1180 case FONT_ENCODING_UNICODE: /* any Unicode cmap */ 1181 /* prefer Microsoft Unicode */ 1182 for(i=0; i<n; i++) { 1183 cmap = face->charmaps[i]; 1184 if(cmap->platform_id == TT_PLATFORM_MICROSOFT && 1185 cmap->encoding_id == TT_MS_ID_UNICODE_CS) { 1186 rc = FT_Set_Charmap(face, cmap); 1187 if(rc == 0) 1188 return 1; 1189 } 1190 } 1191 /* Try Apple Unicode */ 1192 for(i=0; i<n; i++) { 1193 cmap = face->charmaps[i]; 1194 if(cmap->platform_id == TT_PLATFORM_APPLE_UNICODE) { 1195 rc = FT_Set_Charmap(face, cmap); 1196 if(rc == 0) 1197 return 1; 1198 } 1199 } 1200 /* ISO Unicode? */ 1201 for(i=0; i<n; i++) { 1202 cmap = face->charmaps[i]; 1203 if(cmap->platform_id == TT_PLATFORM_ISO) { 1204 rc = FT_Set_Charmap(face, cmap); 1205 if(rc == 0) 1206 return 1; 1207 } 1208 } 1209 break; 1210 default: 1211 return 0; 1212 } 1213 return 0; 1214} 1215 1216static int 1217checkExtraEncoding(FT_Face face, const char *encoding_name, int found) 1218{ 1219 int c; 1220 1221 if(strcasecmp(encoding_name, "iso10646-1") == 0) { 1222 if(doISO10646_1_encoding && find_cmap(FONT_ENCODING_UNICODE, -1, -1, face)) { 1223 int cfound = 0; 1224 /* Export as Unicode if there are at least 15 BMP 1225 characters that are not a space or ignored. */ 1226 for(c = 0x21; c < 0x10000; c++) { 1227 if(CODE_IGNORED(c)) 1228 continue; 1229 if(FT_Get_Char_Index(face, c) > 0) 1230 cfound++; 1231 if(cfound >= 15) 1232 return 1; 1233 } 1234 return 0; 1235 } else 1236 return 0; 1237 } else if(strcasecmp(encoding_name, "microsoft-symbol") == 0) { 1238 if(find_cmap(FONT_ENCODING_TRUETYPE, 1239 TT_PLATFORM_MICROSOFT, TT_MS_ID_SYMBOL_CS, 1240 face)) 1241 return 1; 1242 else 1243 return 0; 1244 } else if(strcasecmp(encoding_name, "adobe-fontspecific") == 0) { 1245 if(!found) { 1246 if(FT_Has_PS_Glyph_Names(face)) 1247 return 1; 1248 else 1249 return 0; 1250 } else 1251 return 0; 1252 } else { 1253 fprintf(stderr, "Unknown extra encoding %s\n", encoding_name); 1254 return 0; 1255 } 1256} 1257 1258static const char* 1259notice_foundry(const char *notice) 1260{ 1261 unsigned int i; 1262 for(i = 0; i < countof(notice_foundries); i++) 1263 if(notice && strstr(notice, notice_foundries[i][0])) 1264 return notice_foundries[i][1]; 1265 return NULL; 1266} 1267 1268static int 1269vendor_match(const signed char *vendor, const char *vendor_string) 1270{ 1271 /* vendor is not necessarily NUL-terminated. */ 1272 size_t i, len; 1273 len = strlen(vendor_string); 1274 if(memcmp(vendor, vendor_string, len) != 0) 1275 return 0; 1276 for(i = len; i < 4; i++) 1277 if(vendor[i] != ' ' && vendor[i] != '\0') 1278 return 0; 1279 return 1; 1280} 1281 1282static const char* 1283vendor_foundry(const signed char *vendor) 1284{ 1285 unsigned int i; 1286 for(i = 0; i < countof(vendor_foundries); i++) 1287 if(vendor_match(vendor, vendor_foundries[i][0])) 1288 return vendor_foundries[i][1]; 1289 return NULL; 1290} 1291 1292static int 1293readEncodings(ListPtr encodings, char *dirname) 1294{ 1295 char *fullname; 1296 DIR *dirp; 1297 struct dirent *file; 1298 char **names, **name; 1299 1300 if(strlen(dirname) > 1 && dirname[strlen(dirname) - 1] == '/') 1301 dirname[strlen(dirname) - 1] = '\0'; 1302 1303 dirp = opendir(dirname); 1304 if(dirp == NULL) { 1305 perror("opendir"); 1306 return -1; 1307 } 1308 1309 while((file = readdir(dirp)) != NULL) { 1310 fullname = dsprintf("%s/%s", dirname, file->d_name); 1311 if(fullname == NULL) { 1312 fprintf(stderr, "Couldn't allocate fullname\n"); 1313 closedir(dirp); 1314 return -1; 1315 } 1316 1317 names = FontEncIdentify(fullname); 1318 if(!names) 1319 continue; 1320 1321 for(name = names; *name; name++) { 1322 if(fullname[0] != '/' && !relative) { 1323 char *n; 1324 n = dsprintf("%s%s", encodingPrefix, fullname); 1325 if(n == NULL) { 1326 fprintf(stderr, "Couldn't allocate name\n"); 1327 closedir(dirp); 1328 return -1; 1329 } 1330 encodingsToDo = listConsF(encodingsToDo, "%s %s", *name, n); 1331 free(n); 1332 } else { 1333 encodingsToDo = 1334 listConsF(encodingsToDo, "%s %s", *name, fullname); 1335 } 1336 if(encodingsToDo == NULL) { 1337 fprintf(stderr, "Couldn't allocate encodings\n"); 1338 closedir(dirp); 1339 return -1; 1340 } 1341 } 1342 free(names); /* only the spine */ 1343 } 1344 closedir(dirp); 1345 return 0; 1346} 1347