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