mkfontscale.c revision 2d6e8b77
1/*
2  Copyright (c) 2002-2003 by Juliusz Chroboczek
3
4  Permission is hereby granted, free of charge, to any person obtaining a copy
5  of this software and associated documentation files (the "Software"), to deal
6  in the Software without restriction, including without limitation the rights
7  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8  copies of the Software, and to permit persons to whom the Software is
9  furnished to do so, subject to the following conditions:
10
11  The above copyright notice and this permission notice shall be included in
12  all copies or substantial portions of the Software.
13
14  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
17  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
20  THE SOFTWARE.
21*/
22
23#include "config.h"
24
25#include <stdio.h>
26#include <stdlib.h>
27#include <string.h>
28
29#include <sys/types.h>
30#include <sys/stat.h>
31#include <dirent.h>
32#include <unistd.h>
33#include <errno.h>
34#include <ctype.h>
35
36#include <X11/Xos.h>
37#include <X11/fonts/fontenc.h>
38#include <ft2build.h>
39#include FT_FREETYPE_H
40#include FT_SFNT_NAMES_H
41#include FT_TRUETYPE_TABLES_H
42#include FT_TRUETYPE_IDS_H
43#include FT_TYPE1_TABLES_H
44#include FT_BDF_H
45#include FT_XFREE86_H
46
47#include "list.h"
48#include "hash.h"
49#include "data.h"
50#include "ident.h"
51
52#define NPREFIX 1024
53
54#ifndef MAXFONTFILENAMELEN
55#define MAXFONTFILENAMELEN 1024
56#endif
57#ifndef MAXFONTNAMELEN
58#define MAXFONTNAMELEN 1024
59#endif
60
61/* Two levels of macro calls are needed so that we stringify the value
62   of MAXFONT... and not the string "MAXFONT..." */
63#define QUOTE(x)	#x
64#define STRINGIFY(x)	QUOTE(x)
65
66static char *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
85static char *extra_encodings_array[] =
86    { "iso10646-1", "adobe-fontspecific", "microsoft-symbol" };
87
88static ListPtr encodings, extra_encodings;
89static const char *outfilename;
90
91#define countof(_a) (sizeof(_a)/sizeof((_a)[0]))
92
93static int doDirectory(const 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 const char* notice_foundry(const char *notice);
98static const char* vendor_foundry(const 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 const 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 const 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 const 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 const char*
408nameWidth(const 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 const char*
426t1Weight(const char *weight)
427{
428    if(!weight)
429        return NULL;
430    if(strcmp(weight, "Thin") == 0)
431        return "thin";
432    if(strcmp(weight, "ExtraLight") == 0) /* FontForge uses this for 200*/
433        return "extralight";
434    if(strcmp(weight, "Light") == 0)
435        return "light";
436    if(strcmp(weight, "Regular") == 0)
437        return "medium";
438    if(strcmp(weight, "Normal") == 0)
439        return "medium";
440    if(strcmp(weight, "Medium") == 0)
441        return "medium";
442    if(strcmp(weight, "Book") == 0)
443        return "medium";
444    if(strcmp(weight, "Roman") == 0) /* Some URW++ fonts do that! */
445        return "medium";
446    if(strcmp(weight, "Demi") == 0)
447        return "semibold";
448    if(strcmp(weight, "DemiBold") == 0)
449        return "semibold";
450    if(strcmp(weight, "SemiBold") == 0) /* some TeX fonts apparently do that */
451        return "semibold";
452    else if(strcmp(weight, "Bold") == 0)
453        return "bold";
454    else if(strcmp(weight, "Heavy") == 0) /* FontForge uses this for 800*/
455        return "extrabold";
456    else if(strcmp(weight, "Black") == 0)
457        return "black";
458    else {
459        fprintf(stderr, "Unknown Type 1 weight \"%s\"\n", weight);
460        return NULL;
461    }
462}
463
464static int
465unsafe(char c)
466{
467    return
468        c < 0x20 || c > 0x7E ||
469        c == '[' || c == ']' || c == '(' || c == ')' || c == '\\' || c == '-';
470}
471
472static const char *
473safe(const char* s)
474{
475    int i, len, safe_flag = 1;
476    char *t;
477
478    i = 0;
479    while(s[i] != '\0') {
480        if(unsafe(s[i]))
481            safe_flag = 0;
482        i++;
483    }
484
485    if(safe_flag) return s;
486
487    len = i;
488    t = malloc(len + 1);
489    if(t == NULL) {
490        perror("Couldn't allocate string");
491        exit(1);
492    }
493
494    for(i = 0; i < len; i++) {
495        if(unsafe(s[i]))
496            t[i] = ' ';
497        else
498            t[i] = s[i];
499    }
500    t[i] = '\0';
501    return t;
502}
503
504ListPtr
505makeXLFD(char *filename, FT_Face face, int isBitmap)
506{
507    ListPtr xlfd = NULL;
508    const char *foundry, *family, *weight, *slant, *sWidth, *adstyle,
509        *spacing, *full_name;
510    TT_Header *head;
511    TT_HoriHeader *hhea;
512    TT_OS2 *os2;
513    TT_Postscript *post;
514    PS_FontInfoRec *t1info, t1info_rec;
515    int rc;
516
517    foundry = NULL;
518    family = NULL;
519    weight = NULL;
520    slant = NULL;
521    sWidth = NULL;
522    adstyle = NULL;
523    spacing = NULL;
524    full_name = NULL;
525
526    head = FT_Get_Sfnt_Table(face, ft_sfnt_head);
527    hhea = FT_Get_Sfnt_Table(face, ft_sfnt_hhea);
528    os2 = FT_Get_Sfnt_Table(face, ft_sfnt_os2);
529    post = FT_Get_Sfnt_Table(face, ft_sfnt_post);
530
531    rc = FT_Get_PS_Font_Info(face, &t1info_rec);
532    if(rc == 0)
533        t1info = &t1info_rec;
534    else
535        t1info = NULL;
536
537    if(!family)
538        family = getName(face, TT_NAME_ID_FONT_FAMILY);
539    if(!family)
540        family = getName(face, TT_NAME_ID_FULL_NAME);
541    if(!family)
542        family = getName(face, TT_NAME_ID_PS_NAME);
543
544    if(!full_name)
545        full_name = getName(face, TT_NAME_ID_FULL_NAME);
546    if(!full_name)
547        full_name = getName(face, TT_NAME_ID_PS_NAME);
548
549    if(os2 && os2->version != 0xFFFF) {
550        if(!weight)
551            weight = os2Weight(os2->usWeightClass);
552        if(!sWidth)
553            sWidth = os2Width(os2->usWidthClass);
554        if(!foundry)
555            foundry = vendor_foundry(os2->achVendID);
556        if(!slant)
557            slant = os2->fsSelection & 1 ? "i" : "r";
558    }
559
560    if(post) {
561        if(!spacing) {
562            if(post->isFixedPitch) {
563                if(hhea->min_Left_Side_Bearing >= 0 &&
564                   hhea->xMax_Extent <= hhea->advance_Width_Max) {
565                    spacing = "c";
566                } else {
567                    spacing = "m";
568                }
569            } else {
570                spacing = "p";
571            }
572        }
573    }
574
575    if(t1info) {
576        if(!family)
577            family = t1info->family_name;
578        if(!family)
579            family = t1info->full_name;
580        if(!full_name)
581            full_name = t1info->full_name;
582        if(!foundry)
583            foundry = notice_foundry(t1info->notice);
584        if(!weight)
585            weight = t1Weight(t1info->weight);
586        if(!spacing)
587            spacing = t1info->is_fixed_pitch ? "m" : "p";
588        if(!slant) {
589            /* Bitstream fonts have positive italic angle. */
590            slant =
591                t1info->italic_angle <= -4 || t1info->italic_angle >= 4 ?
592                "i" : "r";
593        }
594    }
595
596    if(!full_name) {
597        fprintf(stderr, "Couldn't determine full name for %s\n", filename);
598        full_name = filename;
599    }
600
601    if(head) {
602        if(!slant)
603            slant = head->Mac_Style & 2 ? "i" : "r";
604        if(!weight)
605            weight = head->Mac_Style & 1 ? "bold" : "medium";
606    }
607
608    if(!slant) {
609        fprintf(stderr, "Couldn't determine slant for %s\n", filename);
610        slant = "r";
611    }
612
613    if(!weight) {
614        fprintf(stderr, "Couldn't determine weight for %s\n", filename);
615        weight = "medium";
616    }
617
618    if(!foundry) {
619        char *notice;
620        notice = getName(face, TT_NAME_ID_TRADEMARK);
621        if(notice) {
622            foundry = notice_foundry(notice);
623        }
624        if(!foundry) {
625            notice = getName(face, TT_NAME_ID_MANUFACTURER);
626            if(notice) {
627                foundry = notice_foundry(notice);
628            }
629        }
630    }
631
632    if(strcmp(slant, "i") == 0) {
633        if(strstr(full_name, "Oblique"))
634            slant = "o";
635        if(strstr(full_name, "Slanted"))
636            slant = "o";
637    }
638
639    if(!sWidth)
640        sWidth = nameWidth(full_name);
641
642    if(!foundry) foundry = "misc";
643    if(!family) {
644        fprintf(stderr, "Couldn't get family name for %s\n", filename);
645        family = filename;
646    }
647
648    if(!weight) weight = "medium";
649    if(!slant) slant = "r";
650    if(!sWidth) sWidth = "normal";
651    if(!adstyle) adstyle = "";
652    if(!spacing) spacing = "p";
653
654    /* Yes, it's a memory leak. */
655    foundry = safe(foundry);
656    family = safe(family);
657
658    if(!isBitmap) {
659        xlfd = listConsF(xlfd,
660                         "-%s-%s-%s-%s-%s-%s-0-0-0-0-%s-0",
661                         foundry, family,
662                         weight, slant, sWidth, adstyle, spacing);
663    } else {
664        int i, w, h, xres, yres;
665        for(i = 0; i < face->num_fixed_sizes; i++) {
666            w = face->available_sizes[i].width;
667            h = face->available_sizes[i].height;
668            xres = 75;
669            yres = (double)h / w * xres;
670            xlfd = listConsF(xlfd,
671                             "-%s-%s-%s-%s-%s-%s-%d-%d-%d-%d-%s-%d",
672                             foundry, family,
673                             weight, slant, sWidth, adstyle,
674                             h, (int)(h / (double)yres * 72.27 * 10 + 0.5),
675                             xres, yres,
676                             spacing, 60);
677        }
678    }
679    return xlfd;
680}
681
682static int
683readFontScale(HashTablePtr entries, char *dirname)
684{
685    int n = strlen(dirname);
686    char *filename;
687    FILE *in;
688    int rc, count, i;
689    char file[MAXFONTFILENAMELEN+1], font[MAXFONTNAMELEN+1];
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,
715		    "%" STRINGIFY(MAXFONTFILENAMELEN) "s "
716		    "%" STRINGIFY(MAXFONTNAMELEN) "[^\n]\n",
717		    file, font);
718        if(rc != 2)
719            break;
720        putHash(entries, font, file, 100);
721    }
722    fclose(in);
723    return 1;
724}
725
726static int
727filePrio(char *filename)
728{
729    int n = strlen(filename);
730    if(n < 4)
731        return 0;
732    if(strcmp(filename + n - 4, ".otf") == 0)
733        return 6;
734    if(strcmp(filename + n - 4, ".OTF") == 0)
735        return 6;
736    if(strcmp(filename + n - 4, ".ttf") == 0)
737        return 5;
738    if(strcmp(filename + n - 4, ".TTF") == 0)
739        return 5;
740    if(strcmp(filename + n - 4, ".pcf") == 0)
741        return 4;
742    if(strcmp(filename + n - 4, ".PCF") == 0)
743        return 4;
744    if(strcmp(filename + n - 3, ".gz") == 0)
745        return 3;
746#ifdef X_BZIP2_FONT_COMPRESSION
747    if(strcmp(filename + n - 4, ".bz2") == 0)
748        return 2;
749#endif
750    if(strcmp(filename + n - 2, ".Z") == 0)
751        return 2;
752    if(strcmp(filename + n - 4, ".bdf") == 0)
753        return 1;
754    if(strcmp(filename + n - 4, ".BDF") == 0)
755        return 1;
756    return 0;
757}
758
759static int
760doDirectory(const char *dirname_given, int numEncodings, ListPtr encodingsToDo)
761{
762    char *dirname, *fontscale_name, *filename, *encdir;
763    FILE *fontscale, *encfile;
764    DIR *dirp;
765    struct dirent *entry;
766    FT_Error ftrc;
767    FT_Face face;
768    ListPtr encoding, xlfd, lp;
769    HashTablePtr entries;
770    HashBucketPtr *array;
771    int i, n, found, rc;
772    int isBitmap=0,xl=0;
773
774    if (exclusionSuffix)
775        xl = strlen (exclusionSuffix);
776
777    i = strlen(dirname_given);
778    if(i == 0)
779        dirname = dsprintf("./");
780    else if(dirname_given[i - 1] != '/')
781        dirname = dsprintf("%s/", dirname_given);
782    else
783        dirname = dsprintf("%s", dirname_given);
784
785    if(dirname == NULL) {
786        perror("dirname");
787        exit(1);
788    }
789
790    if (onlyEncodings)
791	goto encodings;
792
793    entries = makeHashTable();
794    if(doBitmaps && !doScalable) {
795        readFontScale(entries, dirname);
796    }
797
798    if(strcmp(outfilename, "-") == 0)
799        fontscale_name = NULL;
800    else {
801        if(outfilename[0] == '/')
802            fontscale_name = dsprintf("%s", outfilename);
803        else
804            fontscale_name = dsprintf("%s%s", dirname, outfilename);
805        if(fontscale_name == NULL) {
806            perror("fontscale_name");
807            exit(1);
808        }
809    }
810
811    dirp = opendir(dirname);
812    if(dirp == NULL) {
813        fprintf(stderr, "%s: ", dirname);
814        perror("opendir");
815        return 0;
816    }
817
818    if(fontscale_name == NULL)
819        fontscale = stdout;
820    else
821        fontscale = fopen(fontscale_name, "wb");
822
823    if(fontscale == NULL) {
824        fprintf(stderr, "%s: ", fontscale_name);
825        perror("fopen(w)");
826        return 0;
827    }
828
829    while((entry = readdir(dirp)) != NULL) {
830        int have_face = 0;
831        char *xlfd_name = NULL;
832	struct stat f_stat;
833	int tprio = 1;
834
835        xlfd = NULL;
836
837	if (xl) {
838	    int dl = strlen (entry->d_name);
839	    if (strcmp (entry->d_name + dl - xl, exclusionSuffix) == 0)
840		continue;
841	}
842
843        filename = dsprintf("%s%s", dirname, entry->d_name);
844
845#define PRIO(x) ((x << 1) + tprio)
846#ifdef DT_LNK
847	if (entry->d_type != DT_UNKNOWN) {
848	    if (entry->d_type == DT_LNK)
849		tprio = 0;
850	} else
851#endif
852#ifdef S_ISLNK
853	{
854	    if (lstat(filename, &f_stat))
855		goto done;
856	    if (S_ISLNK(f_stat.st_mode))
857		tprio = 0;
858	}
859#else
860	;
861#endif
862        if(doBitmaps)
863            rc = bitmapIdentify(filename, &xlfd_name);
864        else
865            rc = 0;
866
867        if(rc < 0)
868            goto done;
869
870        if(rc == 0) {
871            ftrc = FT_New_Face(ft_library, filename, 0, &face);
872            if(ftrc)
873                goto done;
874            have_face = 1;
875
876            isBitmap = ((face->face_flags & FT_FACE_FLAG_SCALABLE) == 0);
877
878            if(!isBitmap) {
879                /* Workaround for bitmap-only SFNT fonts */
880                if(FT_IS_SFNT(face) && face->num_fixed_sizes > 0 &&
881                   strcmp(FT_Get_X11_Font_Format(face), "TrueType") == 0) {
882                    TT_MaxProfile *maxp;
883                    maxp = FT_Get_Sfnt_Table(face, ft_sfnt_maxp);
884                    if(maxp != NULL && maxp->maxContours == 0)
885                        isBitmap = 1;
886                }
887            }
888
889            if(isBitmap) {
890                if(!doBitmaps)
891                    goto done;
892            } else {
893                if(!doScalable)
894                    goto done;
895            }
896
897            if(isBitmap) {
898                BDF_PropertyRec prop;
899                rc = FT_Get_BDF_Property(face, "FONT", &prop);
900                if(rc == 0 && prop.type == BDF_PROPERTY_TYPE_ATOM) {
901                    xlfd_name = strdup(prop.u.atom);
902                    if(xlfd_name == NULL)
903                        goto done;
904                }
905            }
906        }
907
908        if(xlfd_name) {
909            /* We know it's a bitmap font, and we know its XLFD */
910            int n = strlen(xlfd_name);
911            if(reencodeLegacy &&
912               n >= 12 && strcasecmp(xlfd_name + n - 11, "-iso10646-1") == 0) {
913                char *s;
914
915                s = malloc(n - 10);
916                memcpy(s, xlfd_name, n - 11);
917                s[n - 11] = '\0';
918                xlfd = listCons(s, xlfd);
919            } else {
920                /* Not a reencodable font -- skip all the rest of the loop body */
921                putHash(entries, xlfd_name, entry->d_name, PRIO(filePrio(entry->d_name)));
922                goto done;
923            }
924        }
925
926        if(!have_face) {
927            ftrc = FT_New_Face(ft_library, filename, 0, &face);
928            if(ftrc)
929                goto done;
930            have_face = 1;
931            isBitmap = ((face->face_flags & FT_FACE_FLAG_SCALABLE) == 0);
932
933            if(!isBitmap) {
934                if(face->num_fixed_sizes > 0) {
935                    TT_MaxProfile *maxp;
936                    maxp = FT_Get_Sfnt_Table(face, ft_sfnt_maxp);
937                    if(maxp != NULL && maxp->maxContours == 0)
938                        isBitmap = 1;
939                }
940            }
941        }
942
943        if(xlfd == NULL)
944            xlfd = makeXLFD(entry->d_name, face, isBitmap);
945
946        found = 0;
947
948        for(lp = xlfd; lp; lp = lp->next) {
949            char buf[MAXFONTNAMELEN];
950            for(encoding = encodings; encoding; encoding = encoding->next) {
951                if(checkEncoding(face, encoding->value)) {
952                    found = 1;
953                    snprintf(buf, MAXFONTNAMELEN, "%s-%s",
954                            lp->value, encoding->value);
955                    putHash(entries, buf, entry->d_name, PRIO(filePrio(entry->d_name)));
956                }
957            }
958            for(encoding = extra_encodings; encoding;
959                encoding = encoding->next) {
960                if(checkExtraEncoding(face, encoding->value, found)) {
961                    /* Do not set found! */
962                    snprintf(buf, MAXFONTNAMELEN, "%s-%s",
963                            lp->value, encoding->value);
964                    putHash(entries, buf, entry->d_name, PRIO(filePrio(entry->d_name)));
965                }
966            }
967        }
968    done:
969        if(have_face)
970            FT_Done_Face(face);
971        deepDestroyList(xlfd);
972        xlfd = NULL;
973        free(filename);
974#undef PRIO
975    }
976
977    closedir(dirp);
978    n = hashElements(entries);
979    fprintf(fontscale, "%d\n", n);
980    array = hashArray(entries, 1);
981    for(i = 0; i < n; i++)
982        fprintf(fontscale, "%s %s\n", array[i]->value, array[i]->key);
983    destroyHashArray(array);
984    entries = NULL;
985    if(fontscale_name) {
986        fclose(fontscale);
987        free(fontscale_name);
988    }
989
990 encodings:
991    encdir = dsprintf("%s%s", dirname, "encodings.dir");
992
993    if(encdir == NULL) {
994	perror("encodings");
995	exit(1);
996    }
997    unlink(encdir);
998
999    if (numEncodings) {
1000	encfile = fopen(encdir, "w");
1001	if(encfile == NULL) {
1002	    perror("open(encodings.dir)");
1003	    exit(1);
1004	}
1005        fprintf(encfile, "%d\n", numEncodings);
1006        encodingsToDo = sortList(encodingsToDo);
1007        for(lp = encodingsToDo; lp; lp = lp->next) {
1008            fprintf(encfile, "%s\n", lp->value);
1009        }
1010	fclose (encfile);
1011    }
1012
1013    free(dirname);
1014    return 1;
1015}
1016
1017#define CODE_IGNORED(c) ((c) < 0x20 || \
1018                         ((c) >= 0x7F && (c) <= 0xA0) || \
1019                         (c) == 0xAD || (c) == 0xF71B)
1020
1021static int
1022checkEncoding(FT_Face face, char *encoding_name)
1023{
1024    FontEncPtr encoding;
1025    FontMapPtr mapping;
1026    int i, j, c, koi8;
1027    char *n;
1028
1029    encoding = FontEncFind(encoding_name, NULL);
1030    if(!encoding)
1031        return 0;
1032
1033    /* An encoding is ``small'' if one of the following is true:
1034         - it is linear and has no more than 256 codepoints; or
1035         - it is a matrix encoding and has no more than one column.
1036
1037       For small encodings using Unicode indices, we require perfect
1038       coverage except for CODE_IGNORED and KOI-8 IBM-PC compatibility.
1039
1040       For large encodings, we require coverage up to bigEncodingFuzz.
1041
1042       For encodings using PS names (currently Adobe Standard and
1043       Adobe Symbol only), we require perfect coverage. */
1044
1045
1046    if(FT_Has_PS_Glyph_Names(face)) {
1047        for(mapping = encoding->mappings; mapping; mapping = mapping->next) {
1048            if(mapping->type == FONT_ENCODING_POSTSCRIPT) {
1049                if(encoding->row_size > 0) {
1050                    for(i = encoding->first; i < encoding->size; i++) {
1051                        for(j = encoding->first_col;
1052                            j < encoding->row_size;
1053                            j++) {
1054                            n = FontEncName((i<<8) | j, mapping);
1055                            if(n && FT_Get_Name_Index(face, n) == 0) {
1056                                return 0;
1057                            }
1058                        }
1059                    }
1060                    return 1;
1061                } else {
1062                    for(i = encoding->first; i < encoding->size; i++) {
1063                        n = FontEncName(i, mapping);
1064                        if(n && FT_Get_Name_Index(face, n) == 0) {
1065                            return 0;
1066                        }
1067                    }
1068                    return 1;
1069                }
1070            }
1071        }
1072    }
1073
1074    for(mapping = encoding->mappings; mapping; mapping = mapping->next) {
1075        if(find_cmap(mapping->type, mapping->pid, mapping->eid, face)) {
1076            int total = 0, failed = 0;
1077            if(encoding->row_size > 0) {
1078                int estimate =
1079                    (encoding->size - encoding->first) *
1080                    (encoding->row_size - encoding->first_col);
1081                for(i = encoding->first; i < encoding->size; i++) {
1082                    for(j = encoding->first_col;
1083                        j < encoding->row_size;
1084                        j++) {
1085                        c = FontEncRecode((i<<8) | j, mapping);
1086                        if(CODE_IGNORED(c)) {
1087                            continue;
1088                        } else {
1089                            if(FT_Get_Char_Index(face, c) == 0) {
1090                                failed++;
1091                            }
1092                            total++;
1093                            if((encoding->size <= 1 && failed > 0) ||
1094                               ((float)failed >= bigEncodingFuzz * estimate)) {
1095                                return 0;
1096                            }
1097                        }
1098                    }
1099                }
1100                if((float)failed >= total * bigEncodingFuzz)
1101                    return 0;
1102                else
1103                    return 1;
1104            } else {
1105                int estimate = encoding->size - encoding->first;
1106                /* For the KOI8 encodings, we ignore the lack of
1107                   linedrawing and pseudo-math characters */
1108                if(strncmp(encoding->name, "koi8-", 5) == 0)
1109                    koi8 = 1;
1110                else
1111                    koi8 = 0;
1112                for(i = encoding->first; i < encoding->size; i++) {
1113                    c = FontEncRecode(i, mapping);
1114                    if(CODE_IGNORED(c) ||
1115                       (koi8 && ((c >= 0x2200 && c < 0x2600) || c == 0x00b2))) {
1116                        continue;
1117                    } else {
1118                        if(FT_Get_Char_Index(face, c) == 0) {
1119                            failed++;
1120                        }
1121                        total++;
1122                        if((encoding->size <= 256 && failed > 0) ||
1123                           ((float)failed >= bigEncodingFuzz * estimate)) {
1124                            return 0;
1125                        }
1126                    }
1127                }
1128                if((float)failed >= total * bigEncodingFuzz)
1129                    return 0;
1130                else
1131                    return 1;
1132            }
1133        }
1134    }
1135    return 0;
1136}
1137
1138static int
1139find_cmap(int type, int pid, int eid, FT_Face face)
1140{
1141    int i, n, rc;
1142    FT_CharMap cmap = NULL;
1143
1144    n = face->num_charmaps;
1145
1146    switch(type) {
1147    case FONT_ENCODING_TRUETYPE:  /* specific cmap */
1148        for(i=0; i<n; i++) {
1149            cmap = face->charmaps[i];
1150            if(cmap->platform_id == pid && cmap->encoding_id == eid) {
1151                rc = FT_Set_Charmap(face, cmap);
1152                if(rc == 0)
1153                    return 1;
1154            }
1155        }
1156        break;
1157    case FONT_ENCODING_UNICODE:   /* any Unicode cmap */
1158        /* prefer Microsoft Unicode */
1159        for(i=0; i<n; i++) {
1160            cmap = face->charmaps[i];
1161            if(cmap->platform_id == TT_PLATFORM_MICROSOFT &&
1162               cmap->encoding_id == TT_MS_ID_UNICODE_CS) {
1163                rc = FT_Set_Charmap(face, cmap);
1164                if(rc == 0)
1165                    return 1;
1166            }
1167        }
1168        /* Try Apple Unicode */
1169        for(i=0; i<n; i++) {
1170            cmap = face->charmaps[i];
1171            if(cmap->platform_id == TT_PLATFORM_APPLE_UNICODE) {
1172                rc = FT_Set_Charmap(face, cmap);
1173                if(rc == 0)
1174                    return 1;
1175            }
1176        }
1177        /* ISO Unicode? */
1178        for(i=0; i<n; i++) {
1179            cmap = face->charmaps[i];
1180            if(cmap->platform_id == TT_PLATFORM_ISO) {
1181                rc = FT_Set_Charmap(face, cmap);
1182                if(rc == 0)
1183                    return 1;
1184            }
1185        }
1186        break;
1187    default:
1188        return 0;
1189    }
1190    return 0;
1191}
1192
1193static int
1194checkExtraEncoding(FT_Face face, char *encoding_name, int found)
1195{
1196    int c;
1197
1198    if(strcasecmp(encoding_name, "iso10646-1") == 0) {
1199        if(doISO10646_1_encoding && find_cmap(FONT_ENCODING_UNICODE, -1, -1, face)) {
1200            int found = 0;
1201             /* Export as Unicode if there are at least 15 BMP
1202               characters that are not a space or ignored. */
1203            for(c = 0x21; c < 0x10000; c++) {
1204                if(CODE_IGNORED(c))
1205                    continue;
1206                if(FT_Get_Char_Index(face, c) > 0)
1207                    found++;
1208                if(found >= 15)
1209                    return 1;
1210            }
1211            return 0;
1212        } else
1213            return 0;
1214    } else if(strcasecmp(encoding_name, "microsoft-symbol") == 0) {
1215        if(find_cmap(FONT_ENCODING_TRUETYPE,
1216                     TT_PLATFORM_MICROSOFT, TT_MS_ID_SYMBOL_CS,
1217                     face))
1218            return 1;
1219        else
1220            return 0;
1221    } else if(strcasecmp(encoding_name, "adobe-fontspecific") == 0) {
1222        if(!found) {
1223            if(FT_Has_PS_Glyph_Names(face))
1224                return 1;
1225            else
1226                return 0;
1227        } else
1228            return 0;
1229    } else {
1230        fprintf(stderr, "Unknown extra encoding %s\n", encoding_name);
1231        return 0;
1232    }
1233}
1234
1235static const char*
1236notice_foundry(const char *notice)
1237{
1238    int i;
1239    for(i = 0; i < countof(notice_foundries); i++)
1240        if(notice && strstr(notice, notice_foundries[i][0]))
1241            return notice_foundries[i][1];
1242    return NULL;
1243}
1244
1245static int
1246vendor_match(const signed char *vendor, const char *vendor_string)
1247{
1248    /* vendor is not necessarily NUL-terminated. */
1249    int i, len;
1250    len = strlen(vendor_string);
1251    if(memcmp(vendor, vendor_string, len) != 0)
1252        return 0;
1253    for(i = len; i < 4; i++)
1254        if(vendor[i] != ' ' && vendor[i] != '\0')
1255            return 0;
1256    return 1;
1257}
1258
1259static const char*
1260vendor_foundry(const signed char *vendor)
1261{
1262    int i;
1263    for(i = 0; i < countof(vendor_foundries); i++)
1264        if(vendor_match(vendor, vendor_foundries[i][0]))
1265            return vendor_foundries[i][1];
1266    return NULL;
1267}
1268
1269static int
1270readEncodings(ListPtr encodings, char *dirname)
1271{
1272    char *fullname;
1273    DIR *dirp;
1274    struct dirent *file;
1275    char **names, **name;
1276
1277    if(strlen(dirname) > 1 && dirname[strlen(dirname) - 1] == '/')
1278        dirname[strlen(dirname) - 1] = '\0';
1279
1280    dirp = opendir(dirname);
1281    if(dirp == NULL) {
1282        perror("opendir");
1283        return -1;
1284    }
1285
1286    while((file = readdir(dirp)) != NULL) {
1287        fullname = dsprintf("%s/%s", dirname, file->d_name);
1288        if(fullname == NULL) {
1289            fprintf(stderr, "Couldn't allocate fullname\n");
1290            closedir(dirp);
1291            return -1;
1292        }
1293
1294        names = FontEncIdentify(fullname);
1295        if(!names)
1296            continue;
1297
1298        for(name = names; *name; name++) {
1299            if(fullname[0] != '/' && !relative) {
1300                char *n;
1301                n = dsprintf("%s%s", encodingPrefix, fullname);
1302                if(n == NULL) {
1303                    fprintf(stderr, "Couldn't allocate name\n");
1304                    closedir(dirp);
1305                    return -1;
1306                }
1307                encodingsToDo = listConsF(encodingsToDo, "%s %s", *name, n);
1308                free(n);
1309            } else {
1310                encodingsToDo =
1311                    listConsF(encodingsToDo, "%s %s", *name, fullname);
1312            }
1313            if(encodingsToDo == NULL) {
1314                fprintf(stderr, "Couldn't allocate encodings\n");
1315                closedir(dirp);
1316                return -1;
1317            }
1318        }
1319        free(names);            /* only the spine */
1320    }
1321    closedir(dirp);
1322    return 0;
1323}
1324