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