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