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