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