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