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