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