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