mkfontscale.c revision 7978d3cd
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#ifdef X_BZIP2_FONT_COMPRESSION
744    if(strcmp(filename + n - 4, ".bz2") == 0)
745        return 2;
746#endif
747    if(strcmp(filename + n - 2, ".Z") == 0)
748        return 2;
749    if(strcmp(filename + n - 4, ".bdf") == 0)
750        return 1;
751    if(strcmp(filename + n - 4, ".BDF") == 0)
752        return 1;
753    return 0;
754}
755
756static int
757doDirectory(char *dirname_given, int numEncodings, ListPtr encodingsToDo)
758{
759    char *dirname, *fontscale_name, *filename, *encdir;
760    FILE *fontscale, *encfile;
761    DIR *dirp;
762    struct dirent *entry;
763    FT_Error ftrc;
764    FT_Face face;
765    ListPtr encoding, xlfd, lp;
766    HashTablePtr entries;
767    HashBucketPtr *array;
768    int i, n, found, rc;
769    int isBitmap=0,xl=0;
770
771    if (exclusionSuffix)
772        xl = strlen (exclusionSuffix);
773
774    i = strlen(dirname_given);
775    if(i == 0)
776        dirname = dsprintf("./");
777    else if(dirname_given[i - 1] != '/')
778        dirname = dsprintf("%s/", dirname_given);
779    else
780        dirname = dsprintf("%s", dirname_given);
781
782    if(dirname == NULL) {
783        perror("dirname");
784        exit(1);
785    }
786
787    if (onlyEncodings)
788	goto encodings;
789
790    entries = makeHashTable();
791    if(doBitmaps && !doScalable) {
792        readFontScale(entries, dirname);
793    }
794
795    if(strcmp(outfilename, "-") == 0)
796        fontscale_name = NULL;
797    else {
798        if(outfilename[0] == '/')
799            fontscale_name = dsprintf("%s", outfilename);
800        else
801            fontscale_name = dsprintf("%s%s", dirname, outfilename);
802        if(fontscale_name == NULL) {
803            perror("fontscale_name");
804            exit(1);
805        }
806    }
807
808    dirp = opendir(dirname);
809    if(dirp == NULL) {
810        fprintf(stderr, "%s: ", dirname);
811        perror("opendir");
812        return 0;
813    }
814
815    if(fontscale_name == NULL)
816        fontscale = stdout;
817    else
818        fontscale = fopen(fontscale_name, "wb");
819
820    if(fontscale == NULL) {
821        fprintf(stderr, "%s: ", fontscale_name);
822        perror("fopen(w)");
823        return 0;
824    }
825
826    while((entry = readdir(dirp)) != NULL) {
827        int have_face = 0;
828        char *xlfd_name = NULL;
829        xlfd = NULL;
830
831	if (xl) {
832	    int dl = strlen (entry->d_name);
833	    if (strcmp (entry->d_name + dl - xl, exclusionSuffix) == 0)
834		continue;
835	}
836
837        filename = dsprintf("%s%s", dirname, entry->d_name);
838
839        if(doBitmaps)
840            rc = bitmapIdentify(filename, &xlfd_name);
841        else
842            rc = 0;
843
844        if(rc < 0)
845            goto done;
846
847        if(rc == 0) {
848            ftrc = FT_New_Face(ft_library, filename, 0, &face);
849            if(ftrc)
850                goto done;
851            have_face = 1;
852
853            isBitmap = ((face->face_flags & FT_FACE_FLAG_SCALABLE) == 0);
854
855            if(!isBitmap) {
856                /* Workaround for bitmap-only SFNT fonts */
857                if(FT_IS_SFNT(face) && face->num_fixed_sizes > 0 &&
858                   strcmp(FT_Get_X11_Font_Format(face), "TrueType") == 0) {
859                    TT_MaxProfile *maxp;
860                    maxp = FT_Get_Sfnt_Table(face, ft_sfnt_maxp);
861                    if(maxp != NULL && maxp->maxContours == 0)
862                        isBitmap = 1;
863                }
864            }
865
866            if(isBitmap) {
867                if(!doBitmaps)
868                    goto done;
869            } else {
870                if(!doScalable)
871                    goto done;
872            }
873
874            if(isBitmap) {
875                BDF_PropertyRec prop;
876                rc = FT_Get_BDF_Property(face, "FONT", &prop);
877                if(rc == 0 && prop.type == BDF_PROPERTY_TYPE_ATOM) {
878                    xlfd_name = malloc(strlen(prop.u.atom) + 1);
879                    if(xlfd_name == NULL)
880                        goto done;
881                    strcpy(xlfd_name, prop.u.atom);
882                }
883            }
884        }
885
886        if(xlfd_name) {
887            /* We know it's a bitmap font, and we know its XLFD */
888            int n = strlen(xlfd_name);
889            if(reencodeLegacy &&
890               n >= 12 && strcasecmp(xlfd_name + n - 11, "-iso10646-1") == 0) {
891                char *s;
892
893                s = malloc(n - 10);
894                memcpy(s, xlfd_name, n - 11);
895                s[n - 11] = '\0';
896                xlfd = listCons(s, xlfd);
897            } else {
898                /* Not a reencodable font -- skip all the rest of the loop body */
899                putHash(entries, xlfd_name, entry->d_name, filePrio(entry->d_name));
900                goto done;
901            }
902        }
903
904        if(!have_face) {
905            ftrc = FT_New_Face(ft_library, filename, 0, &face);
906            if(ftrc)
907                goto done;
908            have_face = 1;
909            isBitmap = ((face->face_flags & FT_FACE_FLAG_SCALABLE) == 0);
910
911            if(!isBitmap) {
912                if(face->num_fixed_sizes > 0) {
913                    TT_MaxProfile *maxp;
914                    maxp = FT_Get_Sfnt_Table(face, ft_sfnt_maxp);
915                    if(maxp != NULL && maxp->maxContours == 0)
916                        isBitmap = 1;
917                }
918            }
919        }
920
921        if(xlfd == NULL)
922            xlfd = makeXLFD(entry->d_name, face, isBitmap);
923
924        found = 0;
925
926        for(lp = xlfd; lp; lp = lp->next) {
927            char buf[MAXFONTNAMELEN];
928            for(encoding = encodings; encoding; encoding = encoding->next) {
929                if(checkEncoding(face, encoding->value)) {
930                    found = 1;
931                    snprintf(buf, MAXFONTNAMELEN, "%s-%s",
932                            lp->value, encoding->value);
933                    putHash(entries, buf, entry->d_name, filePrio(entry->d_name));
934                }
935            }
936            for(encoding = extra_encodings; encoding;
937                encoding = encoding->next) {
938                if(checkExtraEncoding(face, encoding->value, found)) {
939                    /* Do not set found! */
940                    snprintf(buf, MAXFONTNAMELEN, "%s-%s",
941                            lp->value, encoding->value);
942                    putHash(entries, buf, entry->d_name, filePrio(entry->d_name));
943                }
944            }
945        }
946    done:
947        if(have_face) {
948            FT_Done_Face(face);
949            have_face = 0;
950        }
951        deepDestroyList(xlfd);
952        xlfd = NULL;
953        free(filename);
954    }
955
956    closedir(dirp);
957    n = hashElements(entries);
958    fprintf(fontscale, "%d\n", n);
959    array = hashArray(entries, 1);
960    for(i = 0; i < n; i++)
961        fprintf(fontscale, "%s %s\n", array[i]->value, array[i]->key);
962    destroyHashArray(array);
963    entries = NULL;
964    if(fontscale_name) {
965        fclose(fontscale);
966        free(fontscale_name);
967    }
968
969 encodings:
970    encdir = dsprintf("%s%s", dirname, "encodings.dir");
971
972    if(encdir == NULL) {
973	perror("encodings");
974	exit(1);
975    }
976    unlink(encdir);
977
978    if (numEncodings) {
979	encfile = fopen(encdir, "w");
980	if(encfile == NULL) {
981	    perror("open(encodings.dir)");
982	    exit(1);
983	}
984        fprintf(encfile, "%d\n", numEncodings);
985        for(lp = encodingsToDo; lp; lp = lp->next) {
986            fprintf(encfile, "%s\n", lp->value);
987        }
988	fclose (encfile);
989    }
990
991    free(dirname);
992    return 1;
993}
994
995#define CODE_IGNORED(c) ((c) < 0x20 || \
996                         ((c) >= 0x7F && (c) <= 0xA0) || \
997                         (c) == 0xAD || (c) == 0xF71B)
998
999static int
1000checkEncoding(FT_Face face, char *encoding_name)
1001{
1002    FontEncPtr encoding;
1003    FontMapPtr mapping;
1004    int i, j, c, koi8;
1005    char *n;
1006
1007    encoding = FontEncFind(encoding_name, NULL);
1008    if(!encoding)
1009        return 0;
1010
1011    /* An encoding is ``small'' if one of the following is true:
1012         - it is linear and has no more than 256 codepoints; or
1013         - it is a matrix encoding and has no more than one column.
1014
1015       For small encodings using Unicode indices, we require perfect
1016       coverage except for CODE_IGNORED and KOI-8 IBM-PC compatibility.
1017
1018       For large encodings, we require coverage up to bigEncodingFuzz.
1019
1020       For encodings using PS names (currently Adobe Standard and
1021       Adobe Symbol only), we require perfect coverage. */
1022
1023
1024    if(FT_Has_PS_Glyph_Names(face)) {
1025        for(mapping = encoding->mappings; mapping; mapping = mapping->next) {
1026            if(mapping->type == FONT_ENCODING_POSTSCRIPT) {
1027                if(encoding->row_size > 0) {
1028                    for(i = encoding->first; i < encoding->size; i++) {
1029                        for(j = encoding->first_col;
1030                            j < encoding->row_size;
1031                            j++) {
1032                            n = FontEncName((i<<8) | j, mapping);
1033                            if(n && FT_Get_Name_Index(face, n) == 0) {
1034                                return 0;
1035                            }
1036                        }
1037                    }
1038                    return 1;
1039                } else {
1040                    for(i = encoding->first; i < encoding->size; i++) {
1041                        n = FontEncName(i, mapping);
1042                        if(n && FT_Get_Name_Index(face, n) == 0) {
1043                            return 0;
1044                        }
1045                    }
1046                    return 1;
1047                }
1048            }
1049        }
1050    }
1051
1052    for(mapping = encoding->mappings; mapping; mapping = mapping->next) {
1053        if(find_cmap(mapping->type, mapping->pid, mapping->eid, face)) {
1054            int total = 0, failed = 0;
1055            if(encoding->row_size > 0) {
1056                int estimate =
1057                    (encoding->size - encoding->first) *
1058                    (encoding->row_size - encoding->first_col);
1059                for(i = encoding->first; i < encoding->size; i++) {
1060                    for(j = encoding->first_col;
1061                        j < encoding->row_size;
1062                        j++) {
1063                        c = FontEncRecode((i<<8) | j, mapping);
1064                        if(CODE_IGNORED(c)) {
1065                            continue;
1066                        } else {
1067                            if(FT_Get_Char_Index(face, c) == 0) {
1068                                failed++;
1069                            }
1070                            total++;
1071                            if((encoding->size <= 1 && failed > 0) ||
1072                               ((float)failed >= bigEncodingFuzz * estimate)) {
1073                                return 0;
1074                            }
1075                        }
1076                    }
1077                }
1078                if((float)failed >= total * bigEncodingFuzz)
1079                    return 0;
1080                else
1081                    return 1;
1082            } else {
1083                int estimate = encoding->size - encoding->first;
1084                /* For the KOI8 encodings, we ignore the lack of
1085                   linedrawing and pseudo-math characters */
1086                if(strncmp(encoding->name, "koi8-", 5) == 0)
1087                    koi8 = 1;
1088                else
1089                    koi8 = 0;
1090                for(i = encoding->first; i < encoding->size; i++) {
1091                    c = FontEncRecode(i, mapping);
1092                    if(CODE_IGNORED(c) ||
1093                       (koi8 && ((c >= 0x2200 && c < 0x2600) || c == 0x00b2))) {
1094                        continue;
1095                    } else {
1096                        if(FT_Get_Char_Index(face, c) == 0) {
1097                            failed++;
1098                        }
1099                        total++;
1100                        if((encoding->size <= 256 && failed > 0) ||
1101                           ((float)failed >= bigEncodingFuzz * estimate)) {
1102                            return 0;
1103                        }
1104                    }
1105                }
1106                if((float)failed >= total * bigEncodingFuzz)
1107                    return 0;
1108                else
1109                    return 1;
1110            }
1111        }
1112    }
1113    return 0;
1114}
1115
1116static int
1117find_cmap(int type, int pid, int eid, FT_Face face)
1118{
1119    int i, n, rc;
1120    FT_CharMap cmap = NULL;
1121
1122    n = face->num_charmaps;
1123
1124    switch(type) {
1125    case FONT_ENCODING_TRUETYPE:  /* specific cmap */
1126        for(i=0; i<n; i++) {
1127            cmap = face->charmaps[i];
1128            if(cmap->platform_id == pid && cmap->encoding_id == eid) {
1129                rc = FT_Set_Charmap(face, cmap);
1130                if(rc == 0)
1131                    return 1;
1132            }
1133        }
1134        break;
1135    case FONT_ENCODING_UNICODE:   /* any Unicode cmap */
1136        /* prefer Microsoft Unicode */
1137        for(i=0; i<n; i++) {
1138            cmap = face->charmaps[i];
1139            if(cmap->platform_id == TT_PLATFORM_MICROSOFT &&
1140               cmap->encoding_id == TT_MS_ID_UNICODE_CS) {
1141                rc = FT_Set_Charmap(face, cmap);
1142                if(rc == 0)
1143                    return 1;
1144            }
1145        }
1146        /* Try Apple Unicode */
1147        for(i=0; i<n; i++) {
1148            cmap = face->charmaps[i];
1149            if(cmap->platform_id == TT_PLATFORM_APPLE_UNICODE) {
1150                rc = FT_Set_Charmap(face, cmap);
1151                if(rc == 0)
1152                    return 1;
1153            }
1154        }
1155        /* ISO Unicode? */
1156        for(i=0; i<n; i++) {
1157            cmap = face->charmaps[i];
1158            if(cmap->platform_id == TT_PLATFORM_ISO) {
1159                rc = FT_Set_Charmap(face, cmap);
1160                if(rc == 0)
1161                    return 1;
1162            }
1163        }
1164        break;
1165    default:
1166        return 0;
1167    }
1168    return 0;
1169}
1170
1171static int
1172checkExtraEncoding(FT_Face face, char *encoding_name, int found)
1173{
1174    int c;
1175
1176    if(strcasecmp(encoding_name, "iso10646-1") == 0) {
1177        if(doISO10646_1_encoding && find_cmap(FONT_ENCODING_UNICODE, -1, -1, face)) {
1178            int found = 0;
1179             /* Export as Unicode if there are at least 15 BMP
1180               characters that are not a space or ignored. */
1181            for(c = 0x21; c < 0x10000; c++) {
1182                if(CODE_IGNORED(c))
1183                    continue;
1184                if(FT_Get_Char_Index(face, c) > 0)
1185                    found++;
1186                if(found >= 15)
1187                    return 1;
1188            }
1189            return 0;
1190        } else
1191            return 0;
1192    } else if(strcasecmp(encoding_name, "microsoft-symbol") == 0) {
1193        if(find_cmap(FONT_ENCODING_TRUETYPE,
1194                     TT_PLATFORM_MICROSOFT, TT_MS_ID_SYMBOL_CS,
1195                     face))
1196            return 1;
1197        else
1198            return 0;
1199    } else if(strcasecmp(encoding_name, "adobe-fontspecific") == 0) {
1200        if(!found) {
1201            if(FT_Has_PS_Glyph_Names(face))
1202                return 1;
1203            else
1204                return 0;
1205        } else
1206            return 0;
1207    } else {
1208        fprintf(stderr, "Unknown extra encoding %s\n", encoding_name);
1209        return 0;
1210    }
1211}
1212
1213static char*
1214notice_foundry(char *notice)
1215{
1216    int i;
1217    for(i = 0; i < countof(notice_foundries); i++)
1218        if(notice && strstr(notice, notice_foundries[i][0]))
1219            return notice_foundries[i][1];
1220    return NULL;
1221}
1222
1223static int
1224vendor_match(signed char *vendor, char *vendor_string)
1225{
1226    /* vendor is not necessarily NUL-terminated. */
1227    int i, len;
1228    len = strlen(vendor_string);
1229    if(memcmp(vendor, vendor_string, len) != 0)
1230        return 0;
1231    for(i = len; i < 4; i++)
1232        if(vendor[i] != ' ' && vendor[i] != '\0')
1233            return 0;
1234    return 1;
1235}
1236
1237static char*
1238vendor_foundry(signed char *vendor)
1239{
1240    int i;
1241    for(i = 0; i < countof(vendor_foundries); i++)
1242        if(vendor_match(vendor, vendor_foundries[i][0]))
1243            return vendor_foundries[i][1];
1244    return NULL;
1245}
1246
1247static int
1248readEncodings(ListPtr encodings, char *dirname)
1249{
1250    char *fullname;
1251    DIR *dirp;
1252    struct dirent *file;
1253    char **names, **name;
1254
1255    if(strlen(dirname) > 1 && dirname[strlen(dirname) - 1] == '/')
1256        dirname[strlen(dirname) - 1] = '\0';
1257
1258    dirp = opendir(dirname);
1259    if(dirp == NULL) {
1260        perror("opendir");
1261        return -1;
1262    }
1263
1264    while((file = readdir(dirp)) != NULL) {
1265        fullname = dsprintf("%s/%s", dirname, file->d_name);
1266        if(fullname == NULL) {
1267            fprintf(stderr, "Couldn't allocate fullname\n");
1268            closedir(dirp);
1269            return -1;
1270        }
1271
1272        names = FontEncIdentify(fullname);
1273        if(!names)
1274            continue;
1275
1276        for(name = names; *name; name++) {
1277            if(fullname[0] != '/' && !relative) {
1278                char *n;
1279                n = dsprintf("%s%s", encodingPrefix, fullname);
1280                if(n == NULL) {
1281                    fprintf(stderr, "Couldn't allocate name\n");
1282                    closedir(dirp);
1283                    return -1;
1284                }
1285                encodingsToDo = listConsF(encodingsToDo, "%s %s", *name, n);
1286                free(n);
1287            } else {
1288                encodingsToDo =
1289                    listConsF(encodingsToDo, "%s %s", *name, fullname);
1290            }
1291            if(encodingsToDo == NULL) {
1292                fprintf(stderr, "Couldn't allocate encodings\n");
1293                closedir(dirp);
1294                return -1;
1295            }
1296        }
1297        free(names);            /* only the spine */
1298    }
1299    closedir(dirp);
1300    return 0;
1301}
1302