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