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