mkfontscale.c revision 19108c71
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(strcasecmp(weight, "Thin") == 0) 439 return "thin"; 440 if(strcasecmp(weight, "ExtraLight") == 0) /* FontForge uses this for 200*/ 441 return "extralight"; 442 if(strcasecmp(weight, "Light") == 0) 443 return "light"; 444 if(strcasecmp(weight, "Regular") == 0) 445 return "medium"; 446 if(strcasecmp(weight, "Plain") == 0) 447 return "medium"; 448 if(strcasecmp(weight, "Normal") == 0) 449 return "medium"; 450 if(strcasecmp(weight, "Medium") == 0) 451 return "medium"; 452 if(strcasecmp(weight, "Book") == 0) 453 return "medium"; 454 if(strcasecmp(weight, "Roman") == 0) /* Some URW++ fonts do that! */ 455 return "medium"; 456 if(strcasecmp(weight, "Demi") == 0) 457 return "semibold"; 458 if(strcasecmp(weight, "DemiBold") == 0) 459 return "semibold"; 460 if(strcasecmp(weight, "SemiBold") == 0) /* some TeX fonts apparently do that */ 461 return "semibold"; 462 else if(strcasecmp(weight, "Bold") == 0) 463 return "bold"; 464 else if(strcasecmp(weight, "Heavy") == 0) /* FontForge uses this for 800*/ 465 return "extrabold"; 466 else if(strcasecmp(weight, "ExtraBold") == 0) /* freefonts uses this */ 467 return "extrabold"; 468 else if(strcasecmp(weight, "Black") == 0) 469 return "black"; 470 else { 471 fprintf(stderr, "Unknown Type 1 weight \"%s\"\n", weight); 472 return NULL; 473 } 474} 475 476static int 477unsafe(char c) 478{ 479 return 480 c < 0x20 || c > 0x7E || 481 c == '[' || c == ']' || c == '(' || c == ')' || c == '\\' || c == '-'; 482} 483 484static const char * 485safe(const char* s) 486{ 487 int i, len, safe_flag = 1; 488 char *t; 489 490 i = 0; 491 while(s[i] != '\0') { 492 if(unsafe(s[i])) 493 safe_flag = 0; 494 i++; 495 } 496 497 if(safe_flag) return strdup(s); 498 499 len = i; 500 t = malloc(len + 1); 501 if(t == NULL) { 502 perror("Couldn't allocate string"); 503 exit(1); 504 } 505 506 for(i = 0; i < len; i++) { 507 if(unsafe(s[i])) 508 t[i] = ' '; 509 else 510 t[i] = s[i]; 511 } 512 t[i] = '\0'; 513 return t; 514} 515 516ListPtr 517makeXLFD(char *filename, FT_Face face, int isBitmap) 518{ 519 ListPtr xlfd = NULL; 520 const char *foundry, *family, *weight, *slant, *sWidth, *adstyle, 521 *spacing, *full_name, *tmp; 522 TT_Header *head; 523 TT_HoriHeader *hhea; 524 TT_OS2 *os2; 525 TT_Postscript *post; 526 PS_FontInfoRec *t1info, t1info_rec; 527 int rc; 528 529 foundry = NULL; 530 family = NULL; 531 weight = NULL; 532 slant = NULL; 533 sWidth = NULL; 534 adstyle = NULL; 535 spacing = NULL; 536 full_name = NULL; 537 538 head = FT_Get_Sfnt_Table(face, ft_sfnt_head); 539 hhea = FT_Get_Sfnt_Table(face, ft_sfnt_hhea); 540 os2 = FT_Get_Sfnt_Table(face, ft_sfnt_os2); 541 post = FT_Get_Sfnt_Table(face, ft_sfnt_post); 542 543 rc = FT_Get_PS_Font_Info(face, &t1info_rec); 544 if(rc == 0) 545 t1info = &t1info_rec; 546 else 547 t1info = NULL; 548 549 if(!family) 550 family = getName(face, TT_NAME_ID_FONT_FAMILY); 551 if(!family) 552 family = getName(face, TT_NAME_ID_FULL_NAME); 553 if(!family) 554 family = getName(face, TT_NAME_ID_PS_NAME); 555 556 if(!full_name) 557 full_name = getName(face, TT_NAME_ID_FULL_NAME); 558 if(!full_name) 559 full_name = getName(face, TT_NAME_ID_PS_NAME); 560 561 if(os2 && os2->version != 0xFFFF) { 562 if(!weight) 563 weight = os2Weight(os2->usWeightClass); 564 if(!sWidth) 565 sWidth = os2Width(os2->usWidthClass); 566 if(!foundry) 567 foundry = vendor_foundry(os2->achVendID); 568 if(!slant) 569 slant = os2->fsSelection & 1 ? "i" : "r"; 570 } 571 572 if(post) { 573 if(!spacing) { 574 if(post->isFixedPitch) { 575 if(hhea->min_Left_Side_Bearing >= 0 && 576 hhea->xMax_Extent <= hhea->advance_Width_Max) { 577 spacing = "c"; 578 } else { 579 spacing = "m"; 580 } 581 } else { 582 spacing = "p"; 583 } 584 } 585 } 586 587 if(t1info) { 588 if(!family && t1info->family_name) 589 family = strdup(t1info->family_name); 590 if(!family && t1info->full_name) 591 family = strdup(t1info->full_name); 592 /* Hershey fonts miss /FamilyName */ 593 if(!family && face->family_name) 594 family = strdup(face->family_name); 595 if(!full_name && t1info->full_name) 596 full_name = strdup(t1info->full_name); 597 if(!foundry) 598 foundry = notice_foundry(t1info->notice); 599 if(!weight) 600 weight = t1Weight(t1info->weight); 601 if(!spacing) 602 spacing = t1info->is_fixed_pitch ? "m" : "p"; 603 if(!slant) { 604 /* Bitstream fonts have positive italic angle. */ 605 slant = 606 t1info->italic_angle <= -4 || t1info->italic_angle >= 4 ? 607 "i" : "r"; 608 } 609 } 610 611 if(!full_name) { 612 fprintf(stderr, "Couldn't determine full name for %s\n", filename); 613 full_name = strdup(filename); 614 } 615 616 if(head) { 617 if(!slant) 618 slant = head->Mac_Style & 2 ? "i" : "r"; 619 if(!weight) 620 weight = head->Mac_Style & 1 ? "bold" : "medium"; 621 } 622 623 if(!slant) { 624 fprintf(stderr, "Couldn't determine slant for %s\n", filename); 625 slant = "r"; 626 } 627 628 if(!weight) { 629 fprintf(stderr, "Couldn't determine weight for %s\n", filename); 630 weight = "medium"; 631 } 632 633 if(!foundry) { 634 char *notice; 635 notice = getName(face, TT_NAME_ID_TRADEMARK); 636 if(notice) { 637 foundry = notice_foundry(notice); 638 free(notice); 639 } 640 if(!foundry) { 641 notice = getName(face, TT_NAME_ID_MANUFACTURER); 642 if(notice) { 643 foundry = notice_foundry(notice); 644 free(notice); 645 } 646 } 647 } 648 649 if(strcmp(slant, "i") == 0) { 650 if(strstr(full_name, "Oblique")) 651 slant = "o"; 652 if(strstr(full_name, "Slanted")) 653 slant = "o"; 654 } 655 656 if(!sWidth) 657 sWidth = nameWidth(full_name); 658 659 if(!foundry) foundry = "misc"; 660 if(!family) { 661 fprintf(stderr, "Couldn't get family name for %s\n", filename); 662 family = strdup(filename); 663 } 664 665 if(!weight) weight = "medium"; 666 if(!slant) slant = "r"; 667 if(!sWidth) sWidth = "normal"; 668 if(!adstyle) adstyle = ""; 669 if(!spacing) spacing = "p"; 670 671 foundry = safe(foundry); 672 673 tmp = family; 674 family = safe(family); 675 free((void *)tmp); 676 677 if(!isBitmap) { 678 xlfd = listConsF(xlfd, 679 "-%s-%s-%s-%s-%s-%s-0-0-0-0-%s-0", 680 foundry, family, 681 weight, slant, sWidth, adstyle, spacing); 682 } else { 683 int i, w, h, xres, yres; 684 for(i = 0; i < face->num_fixed_sizes; i++) { 685 w = face->available_sizes[i].width; 686 h = face->available_sizes[i].height; 687 xres = 75; 688 yres = (double)h / w * xres; 689 xlfd = listConsF(xlfd, 690 "-%s-%s-%s-%s-%s-%s-%d-%d-%d-%d-%s-%d", 691 foundry, family, 692 weight, slant, sWidth, adstyle, 693 h, (int)(h / (double)yres * 72.27 * 10 + 0.5), 694 xres, yres, 695 spacing, 60); 696 } 697 } 698 699 free((void *)family); 700 free((void *)foundry); 701 free((void *)full_name); 702 return xlfd; 703} 704 705static int 706readFontScale(HashTablePtr entries, char *dirname) 707{ 708 size_t n = strlen(dirname); 709 char *filename; 710 FILE *in; 711 int rc, count, i; 712 char file[MAXFONTFILENAMELEN+1], font[MAXFONTNAMELEN+1]; 713 714 if(dirname[n - 1] == '/') 715 filename = dsprintf("%sfonts.scale", dirname); 716 else 717 filename = dsprintf("%s/fonts.scale", dirname); 718 if(filename == NULL) 719 return -1; 720 721 in = fopen(filename, "r"); 722 free(filename); 723 if(in == NULL) { 724 if(errno != ENOENT) 725 perror("open(fonts.scale)"); 726 return -1; 727 } 728 729 rc = fscanf(in, "%d\n", &count); 730 if(rc != 1) { 731 fprintf(stderr, "Invalid fonts.scale in %s.\n", dirname); 732 fclose(in); 733 return -1; 734 } 735 736 for(i = 0; i < count; i++) { 737 rc = fscanf(in, 738 "%" STRINGIFY(MAXFONTFILENAMELEN) "s " 739 "%" STRINGIFY(MAXFONTNAMELEN) "[^\n]\n", 740 file, font); 741 if(rc != 2) 742 break; 743 putHash(entries, font, file, 100); 744 } 745 fclose(in); 746 return 1; 747} 748 749static int 750filePrio(char *filename) 751{ 752 size_t n = strlen(filename); 753 if(n < 4) 754 return 0; 755 if(strcmp(filename + n - 4, ".otf") == 0) 756 return 6; 757 if(strcmp(filename + n - 4, ".OTF") == 0) 758 return 6; 759 if(strcmp(filename + n - 4, ".ttf") == 0) 760 return 5; 761 if(strcmp(filename + n - 4, ".TTF") == 0) 762 return 5; 763 if(strcmp(filename + n - 4, ".pcf") == 0) 764 return 4; 765 if(strcmp(filename + n - 4, ".PCF") == 0) 766 return 4; 767 if(strcmp(filename + n - 3, ".gz") == 0) 768 return 3; 769#ifdef X_BZIP2_FONT_COMPRESSION 770 if(strcmp(filename + n - 4, ".bz2") == 0) 771 return 2; 772#endif 773 if(strcmp(filename + n - 2, ".Z") == 0) 774 return 2; 775 if(strcmp(filename + n - 4, ".bdf") == 0) 776 return 1; 777 if(strcmp(filename + n - 4, ".BDF") == 0) 778 return 1; 779 return 0; 780} 781 782static int 783doDirectory(const char *dirname_given, int numEncodings, ListPtr encodingsToDo) 784{ 785 char *dirname, *fontscale_name, *filename, *encdir; 786 FILE *fontscale, *encfile; 787 struct dirent** namelist; 788 FT_Error ftrc; 789 FT_Face face; 790 ConstListPtr encoding; 791 ListPtr xlfd, lp; 792 HashTablePtr entries; 793 HashBucketPtr *array; 794 int i, n, dirn, diri, found, rc; 795 int isBitmap=0; 796 size_t d, xl=0; 797 798 if (exclusionSuffix) 799 xl = strlen (exclusionSuffix); 800 801 d = strlen(dirname_given); 802 if(d == 0) 803 dirname = dsprintf("./"); 804 else if(dirname_given[d - 1] != '/') 805 dirname = dsprintf("%s/", dirname_given); 806 else 807 dirname = strdup(dirname_given); 808 809 if(dirname == NULL) { 810 perror("dirname"); 811 exit(1); 812 } 813 814 if (onlyEncodings) 815 goto encodings; 816 817 entries = makeHashTable(); 818 if(doBitmaps && !doScalable) { 819 readFontScale(entries, dirname); 820 } 821 822 if(strcmp(outfilename, "-") == 0) 823 fontscale_name = NULL; 824 else { 825 if(outfilename[0] == '/') 826 fontscale_name = strdup(outfilename); 827 else 828 fontscale_name = dsprintf("%s%s", dirname, outfilename); 829 if(fontscale_name == NULL) { 830 perror("fontscale_name"); 831 exit(1); 832 } 833 } 834 835 dirn = scandir(dirname, &namelist, NULL, alphasort); 836 if(dirn < 0) { 837 fprintf(stderr, "%s: ", dirname); 838 perror("scandir"); 839 return 0; 840 } 841 842 if(fontscale_name == NULL) 843 fontscale = stdout; 844 else 845 fontscale = fopen(fontscale_name, "wb"); 846 847 if(fontscale == NULL) { 848 fprintf(stderr, "%s: ", fontscale_name); 849 perror("fopen(w)"); 850 return 0; 851 } 852 853 for(diri = dirn - 1; diri >= 0; diri--) { 854 struct dirent *entry = namelist[diri]; 855 int have_face = 0; 856 char *xlfd_name = NULL; 857 struct stat f_stat; 858 int tprio = 1; 859 860 xlfd = NULL; 861 862 if (xl) { 863 size_t dl = strlen (entry->d_name); 864 if (strcmp (entry->d_name + dl - xl, exclusionSuffix) == 0) 865 continue; 866 } 867 868 filename = dsprintf("%s%s", dirname, entry->d_name); 869 870#define PRIO(x) ((x << 1) + tprio) 871#ifdef DT_LNK 872 if (entry->d_type != DT_UNKNOWN) { 873 if (entry->d_type == DT_LNK) 874 tprio = 0; 875 } else 876#endif 877#ifdef S_ISLNK 878 { 879 if (lstat(filename, &f_stat)) 880 goto done; 881 if (S_ISLNK(f_stat.st_mode)) 882 tprio = 0; 883 } 884#else 885 ; 886#endif 887 if(doBitmaps) 888 rc = bitmapIdentify(filename, &xlfd_name); 889 else 890 rc = 0; 891 892 if(rc < 0) 893 goto done; 894 895 if(rc == 0) { 896 ftrc = FT_New_Face(ft_library, filename, 0, &face); 897 if(ftrc) 898 goto done; 899 have_face = 1; 900 901 isBitmap = ((face->face_flags & FT_FACE_FLAG_SCALABLE) == 0); 902 903 if(!isBitmap) { 904 /* Workaround for bitmap-only SFNT fonts */ 905 if(FT_IS_SFNT(face) && face->num_fixed_sizes > 0 && 906 strcmp(FT_Get_X11_Font_Format(face), "TrueType") == 0) { 907 TT_MaxProfile *maxp; 908 maxp = FT_Get_Sfnt_Table(face, ft_sfnt_maxp); 909 if(maxp != NULL && maxp->maxContours == 0) 910 isBitmap = 1; 911 } 912 } 913 914 if(isBitmap) { 915 if(!doBitmaps) 916 goto done; 917 } else { 918 if(!doScalable) 919 goto done; 920 } 921 922 if(isBitmap) { 923 BDF_PropertyRec prop; 924 rc = FT_Get_BDF_Property(face, "FONT", &prop); 925 if(rc == 0 && prop.type == BDF_PROPERTY_TYPE_ATOM) { 926 xlfd_name = strdup(prop.u.atom); 927 if(xlfd_name == NULL) 928 goto done; 929 } 930 } 931 } 932 933 if(xlfd_name) { 934 /* We know it's a bitmap font, and we know its XLFD */ 935 size_t l = strlen(xlfd_name); 936 if(reencodeLegacy && 937 l >= 12 && strcasecmp(xlfd_name + l - 11, "-iso10646-1") == 0) { 938 char *s; 939 940 s = malloc(l - 10); 941 memcpy(s, xlfd_name, l - 11); 942 s[l - 11] = '\0'; 943 xlfd = listCons(s, xlfd); 944 } else { 945 /* Not a reencodable font -- skip all the rest of the loop body */ 946 putHash(entries, xlfd_name, entry->d_name, PRIO(filePrio(entry->d_name))); 947 goto done; 948 } 949 } 950 951 if(!have_face) { 952 ftrc = FT_New_Face(ft_library, filename, 0, &face); 953 if(ftrc) 954 goto done; 955 have_face = 1; 956 isBitmap = ((face->face_flags & FT_FACE_FLAG_SCALABLE) == 0); 957 958 if(!isBitmap) { 959 if(face->num_fixed_sizes > 0) { 960 TT_MaxProfile *maxp; 961 maxp = FT_Get_Sfnt_Table(face, ft_sfnt_maxp); 962 if(maxp != NULL && maxp->maxContours == 0) 963 isBitmap = 1; 964 } 965 } 966 } 967 968 if(xlfd == NULL) 969 xlfd = makeXLFD(entry->d_name, face, isBitmap); 970 971 found = 0; 972 973 for(lp = xlfd; lp; lp = lp->next) { 974 char buf[MAXFONTNAMELEN]; 975 for(encoding = encodings; encoding; encoding = encoding->next) { 976 if(checkEncoding(face, encoding->value)) { 977 found = 1; 978 snprintf(buf, MAXFONTNAMELEN, "%s-%s", 979 lp->value, encoding->value); 980 putHash(entries, buf, entry->d_name, PRIO(filePrio(entry->d_name))); 981 } 982 } 983 for(encoding = extra_encodings; encoding; 984 encoding = encoding->next) { 985 if(checkExtraEncoding(face, encoding->value, found)) { 986 /* Do not set found! */ 987 snprintf(buf, MAXFONTNAMELEN, "%s-%s", 988 lp->value, encoding->value); 989 putHash(entries, buf, entry->d_name, PRIO(filePrio(entry->d_name))); 990 } 991 } 992 } 993 done: 994 if(have_face) 995 FT_Done_Face(face); 996 deepDestroyList(xlfd); 997 xlfd = NULL; 998 free(filename); 999#undef PRIO 1000 } 1001 1002 while(dirn--) 1003 free(namelist[dirn]); 1004 free(namelist); 1005 n = hashElements(entries); 1006 fprintf(fontscale, "%d\n", n); 1007 array = hashArray(entries, 1); 1008 for(i = 0; i < n; i++) 1009 fprintf(fontscale, "%s %s\n", array[i]->value, array[i]->key); 1010 destroyHashArray(array); 1011 entries = NULL; 1012 if(fontscale_name) { 1013 fclose(fontscale); 1014 free(fontscale_name); 1015 } 1016 1017 encodings: 1018 encdir = dsprintf("%s%s", dirname, "encodings.dir"); 1019 1020 if(encdir == NULL) { 1021 perror("encodings"); 1022 exit(1); 1023 } 1024 unlink(encdir); 1025 1026 if (numEncodings) { 1027 encfile = fopen(encdir, "w"); 1028 if(encfile == NULL) { 1029 perror("open(encodings.dir)"); 1030 exit(1); 1031 } 1032 fprintf(encfile, "%d\n", numEncodings); 1033 encodingsToDo = sortList(encodingsToDo); 1034 for(lp = encodingsToDo; lp; lp = lp->next) { 1035 fprintf(encfile, "%s\n", lp->value); 1036 } 1037 fclose (encfile); 1038 } 1039 1040 free(dirname); 1041 return 1; 1042} 1043 1044#define CODE_IGNORED(c) ((c) < 0x20 || \ 1045 ((c) >= 0x7F && (c) <= 0xA0) || \ 1046 (c) == 0xAD || (c) == 0xF71B) 1047 1048static int 1049checkEncoding(FT_Face face, const char *encoding_name) 1050{ 1051 FontEncPtr encoding; 1052 FontMapPtr mapping; 1053 int i, j, c, koi8; 1054 char *n; 1055 1056 encoding = FontEncFind(encoding_name, NULL); 1057 if(!encoding) 1058 return 0; 1059 1060 /* An encoding is ``small'' if one of the following is true: 1061 - it is linear and has no more than 256 codepoints; or 1062 - it is a matrix encoding and has no more than one column. 1063 1064 For small encodings using Unicode indices, we require perfect 1065 coverage except for CODE_IGNORED and KOI-8 IBM-PC compatibility. 1066 1067 For large encodings, we require coverage up to bigEncodingFuzz. 1068 1069 For encodings using PS names (currently Adobe Standard and 1070 Adobe Symbol only), we require perfect coverage. */ 1071 1072 1073 if(FT_Has_PS_Glyph_Names(face)) { 1074 for(mapping = encoding->mappings; mapping; mapping = mapping->next) { 1075 if(mapping->type == FONT_ENCODING_POSTSCRIPT) { 1076 if(encoding->row_size > 0) { 1077 for(i = encoding->first; i < encoding->size; i++) { 1078 for(j = encoding->first_col; 1079 j < encoding->row_size; 1080 j++) { 1081 n = FontEncName((i<<8) | j, mapping); 1082 if(n && FT_Get_Name_Index(face, n) == 0) { 1083 return 0; 1084 } 1085 } 1086 } 1087 return 1; 1088 } else { 1089 for(i = encoding->first; i < encoding->size; i++) { 1090 n = FontEncName(i, mapping); 1091 if(n && FT_Get_Name_Index(face, n) == 0) { 1092 return 0; 1093 } 1094 } 1095 return 1; 1096 } 1097 } 1098 } 1099 } 1100 1101 for(mapping = encoding->mappings; mapping; mapping = mapping->next) { 1102 if(find_cmap(mapping->type, mapping->pid, mapping->eid, face)) { 1103 int total = 0, failed = 0; 1104 if(encoding->row_size > 0) { 1105 int estimate = 1106 (encoding->size - encoding->first) * 1107 (encoding->row_size - encoding->first_col); 1108 for(i = encoding->first; i < encoding->size; i++) { 1109 for(j = encoding->first_col; 1110 j < encoding->row_size; 1111 j++) { 1112 c = FontEncRecode((i<<8) | j, mapping); 1113 if(CODE_IGNORED(c)) { 1114 continue; 1115 } else { 1116 if(FT_Get_Char_Index(face, c) == 0) { 1117 failed++; 1118 } 1119 total++; 1120 if((encoding->size <= 1 && failed > 0) || 1121 ((float)failed >= bigEncodingFuzz * estimate)) { 1122 return 0; 1123 } 1124 } 1125 } 1126 } 1127 if((float)failed >= total * bigEncodingFuzz) 1128 return 0; 1129 else 1130 return 1; 1131 } else { 1132 int estimate = encoding->size - encoding->first; 1133 /* For the KOI8 encodings, we ignore the lack of 1134 linedrawing and pseudo-math characters */ 1135 if(strncmp(encoding->name, "koi8-", 5) == 0) 1136 koi8 = 1; 1137 else 1138 koi8 = 0; 1139 for(i = encoding->first; i < encoding->size; i++) { 1140 c = FontEncRecode(i, mapping); 1141 if(CODE_IGNORED(c) || 1142 (koi8 && ((c >= 0x2200 && c < 0x2600) || c == 0x00b2))) { 1143 continue; 1144 } else { 1145 if(FT_Get_Char_Index(face, c) == 0) { 1146 failed++; 1147 } 1148 total++; 1149 if((encoding->size <= 256 && failed > 0) || 1150 ((float)failed >= bigEncodingFuzz * estimate)) { 1151 return 0; 1152 } 1153 } 1154 } 1155 if((float)failed >= total * bigEncodingFuzz) 1156 return 0; 1157 else 1158 return 1; 1159 } 1160 } 1161 } 1162 return 0; 1163} 1164 1165static int 1166find_cmap(int type, int pid, int eid, FT_Face face) 1167{ 1168 int i, n, rc; 1169 FT_CharMap cmap = NULL; 1170 1171 n = face->num_charmaps; 1172 1173 switch(type) { 1174 case FONT_ENCODING_TRUETYPE: /* specific cmap */ 1175 for(i=0; i<n; i++) { 1176 cmap = face->charmaps[i]; 1177 if(cmap->platform_id == pid && cmap->encoding_id == eid) { 1178 rc = FT_Set_Charmap(face, cmap); 1179 if(rc == 0) 1180 return 1; 1181 } 1182 } 1183 break; 1184 case FONT_ENCODING_UNICODE: /* any Unicode cmap */ 1185 /* prefer Microsoft Unicode */ 1186 for(i=0; i<n; i++) { 1187 cmap = face->charmaps[i]; 1188 if(cmap->platform_id == TT_PLATFORM_MICROSOFT && 1189 cmap->encoding_id == TT_MS_ID_UNICODE_CS) { 1190 rc = FT_Set_Charmap(face, cmap); 1191 if(rc == 0) 1192 return 1; 1193 } 1194 } 1195 /* Try Apple Unicode */ 1196 for(i=0; i<n; i++) { 1197 cmap = face->charmaps[i]; 1198 if(cmap->platform_id == TT_PLATFORM_APPLE_UNICODE) { 1199 rc = FT_Set_Charmap(face, cmap); 1200 if(rc == 0) 1201 return 1; 1202 } 1203 } 1204 /* ISO Unicode? */ 1205 for(i=0; i<n; i++) { 1206 cmap = face->charmaps[i]; 1207 if(cmap->platform_id == TT_PLATFORM_ISO) { 1208 rc = FT_Set_Charmap(face, cmap); 1209 if(rc == 0) 1210 return 1; 1211 } 1212 } 1213 break; 1214 default: 1215 return 0; 1216 } 1217 return 0; 1218} 1219 1220static int 1221checkExtraEncoding(FT_Face face, const char *encoding_name, int found) 1222{ 1223 int c; 1224 1225 if(strcasecmp(encoding_name, "iso10646-1") == 0) { 1226 if(doISO10646_1_encoding && find_cmap(FONT_ENCODING_UNICODE, -1, -1, face)) { 1227 int cfound = 0; 1228 /* Export as Unicode if there are at least 15 BMP 1229 characters that are not a space or ignored. */ 1230 for(c = 0x21; c < 0x10000; c++) { 1231 if(CODE_IGNORED(c)) 1232 continue; 1233 if(FT_Get_Char_Index(face, c) > 0) 1234 cfound++; 1235 if(cfound >= 15) 1236 return 1; 1237 } 1238 return 0; 1239 } else 1240 return 0; 1241 } else if(strcasecmp(encoding_name, "microsoft-symbol") == 0) { 1242 if(find_cmap(FONT_ENCODING_TRUETYPE, 1243 TT_PLATFORM_MICROSOFT, TT_MS_ID_SYMBOL_CS, 1244 face)) 1245 return 1; 1246 else 1247 return 0; 1248 } else if(strcasecmp(encoding_name, "adobe-fontspecific") == 0) { 1249 if(!found) { 1250 if(FT_Has_PS_Glyph_Names(face)) 1251 return 1; 1252 else 1253 return 0; 1254 } else 1255 return 0; 1256 } else { 1257 fprintf(stderr, "Unknown extra encoding %s\n", encoding_name); 1258 return 0; 1259 } 1260} 1261 1262static const char* 1263notice_foundry(const char *notice) 1264{ 1265 unsigned int i; 1266 for(i = 0; i < countof(notice_foundries); i++) 1267 if(notice && strstr(notice, notice_foundries[i][0])) 1268 return notice_foundries[i][1]; 1269 return NULL; 1270} 1271 1272static int 1273vendor_match(const signed char *vendor, const char *vendor_string) 1274{ 1275 /* vendor is not necessarily NUL-terminated. */ 1276 size_t i, len; 1277 len = strlen(vendor_string); 1278 if(memcmp(vendor, vendor_string, len) != 0) 1279 return 0; 1280 for(i = len; i < 4; i++) 1281 if(vendor[i] != ' ' && vendor[i] != '\0') 1282 return 0; 1283 return 1; 1284} 1285 1286static const char* 1287vendor_foundry(const signed char *vendor) 1288{ 1289 unsigned int i; 1290 for(i = 0; i < countof(vendor_foundries); i++) 1291 if(vendor_match(vendor, vendor_foundries[i][0])) 1292 return vendor_foundries[i][1]; 1293 return NULL; 1294} 1295 1296static int 1297readEncodings(ListPtr encodings, char *dirname) 1298{ 1299 char *fullname; 1300 DIR *dirp; 1301 struct dirent *file; 1302 char **names, **name; 1303 1304 if(strlen(dirname) > 1 && dirname[strlen(dirname) - 1] == '/') 1305 dirname[strlen(dirname) - 1] = '\0'; 1306 1307 dirp = opendir(dirname); 1308 if(dirp == NULL) { 1309 perror("opendir"); 1310 return -1; 1311 } 1312 1313 while((file = readdir(dirp)) != NULL) { 1314 fullname = dsprintf("%s/%s", dirname, file->d_name); 1315 if(fullname == NULL) { 1316 fprintf(stderr, "Couldn't allocate fullname\n"); 1317 closedir(dirp); 1318 return -1; 1319 } 1320 1321 names = FontEncIdentify(fullname); 1322 if(!names) 1323 continue; 1324 1325 for(name = names; *name; name++) { 1326 if(fullname[0] != '/' && !relative) { 1327 char *n; 1328 n = dsprintf("%s%s", encodingPrefix, fullname); 1329 if(n == NULL) { 1330 fprintf(stderr, "Couldn't allocate name\n"); 1331 closedir(dirp); 1332 return -1; 1333 } 1334 encodingsToDo = listConsF(encodingsToDo, "%s %s", *name, n); 1335 free(n); 1336 } else { 1337 encodingsToDo = 1338 listConsF(encodingsToDo, "%s %s", *name, fullname); 1339 } 1340 if(encodingsToDo == NULL) { 1341 fprintf(stderr, "Couldn't allocate encodings\n"); 1342 closedir(dirp); 1343 return -1; 1344 } 1345 } 1346 free(names); /* only the spine */ 1347 } 1348 closedir(dirp); 1349 return 0; 1350} 1351