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