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