Release 1.4.1.
[wine] / tools / sfnt2fnt.c
1 /*
2  * sfnttofnt.  Bitmap only ttf to Window fnt file converter
3  *
4  * Copyright 2004 Huw Davies
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2.1 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this library; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
19  */
20
21 #include "config.h"
22 #include "wine/port.h"
23
24 #include <assert.h>
25 #include <ctype.h>
26 #include <errno.h>
27 #include <signal.h>
28 #include <stdio.h>
29 #include <stdlib.h>
30 #ifdef HAVE_GETOPT_H
31 # include <getopt.h>
32 #endif
33
34 #ifdef HAVE_FREETYPE
35
36 #ifdef HAVE_FT2BUILD_H
37 #include <ft2build.h>
38 #endif
39 #include FT_FREETYPE_H
40 #include FT_SFNT_NAMES_H
41 #include FT_TRUETYPE_TABLES_H
42 #include FT_TRUETYPE_TAGS_H
43
44 #include "wine/unicode.h"
45 #include "wingdi.h"
46
47 #include "pshpack1.h"
48
49 typedef struct
50 {
51     INT16 dfType;
52     INT16 dfPoints;
53     INT16 dfVertRes;
54     INT16 dfHorizRes;
55     INT16 dfAscent;
56     INT16 dfInternalLeading;
57     INT16 dfExternalLeading;
58     BYTE  dfItalic;
59     BYTE  dfUnderline;
60     BYTE  dfStrikeOut;
61     INT16 dfWeight;
62     BYTE  dfCharSet;
63     INT16 dfPixWidth;
64     INT16 dfPixHeight;
65     BYTE  dfPitchAndFamily;
66     INT16 dfAvgWidth;
67     INT16 dfMaxWidth;
68     BYTE  dfFirstChar;
69     BYTE  dfLastChar;
70     BYTE  dfDefaultChar;
71     BYTE  dfBreakChar;
72     INT16 dfWidthBytes;
73     LONG  dfDevice;
74     LONG  dfFace;
75     LONG  dfBitsPointer;
76     LONG  dfBitsOffset;
77     BYTE  dfReserved;
78     LONG  dfFlags;
79     INT16 dfAspace;
80     INT16 dfBspace;
81     INT16 dfCspace;
82     LONG  dfColorPointer;
83     LONG  dfReserved1[4];
84 } FONTINFO16;
85
86 typedef struct
87 {
88     WORD dfVersion;
89     DWORD dfSize;
90     char dfCopyright[60];
91     FONTINFO16 fi;
92 } FNT_HEADER;
93
94 typedef struct
95 {
96     WORD  offset;
97     WORD  length;
98     WORD  flags;
99     WORD  id;
100     WORD  handle;
101     WORD  usage;
102 } NE_NAMEINFO;
103
104 typedef struct
105 {
106     WORD  type_id;
107     WORD  count;
108     DWORD resloader;
109 } NE_TYPEINFO;
110
111 #define NE_FFLAGS_SINGLEDATA    0x0001
112 #define NE_FFLAGS_MULTIPLEDATA  0x0002
113 #define NE_FFLAGS_WIN32         0x0010
114 #define NE_FFLAGS_FRAMEBUF      0x0100
115 #define NE_FFLAGS_CONSOLE       0x0200
116 #define NE_FFLAGS_GUI           0x0300
117 #define NE_FFLAGS_SELFLOAD      0x0800
118 #define NE_FFLAGS_LINKERROR     0x2000
119 #define NE_FFLAGS_CALLWEP       0x4000
120 #define NE_FFLAGS_LIBMODULE     0x8000
121
122 #define NE_OSFLAGS_WINDOWS      0x02
123
124 #define NE_RSCTYPE_FONTDIR            0x8007
125 #define NE_RSCTYPE_FONT               0x8008
126 #define NE_RSCTYPE_SCALABLE_FONTPATH  0x80cc
127
128 #define NE_SEGFLAGS_DATA        0x0001
129 #define NE_SEGFLAGS_ALLOCATED   0x0002
130 #define NE_SEGFLAGS_LOADED      0x0004
131 #define NE_SEGFLAGS_ITERATED    0x0008
132 #define NE_SEGFLAGS_MOVEABLE    0x0010
133 #define NE_SEGFLAGS_SHAREABLE   0x0020
134 #define NE_SEGFLAGS_PRELOAD     0x0040
135 #define NE_SEGFLAGS_EXECUTEONLY 0x0080
136 #define NE_SEGFLAGS_READONLY    0x0080
137 #define NE_SEGFLAGS_RELOC_DATA  0x0100
138 #define NE_SEGFLAGS_SELFLOAD    0x0800
139 #define NE_SEGFLAGS_DISCARDABLE 0x1000
140 #define NE_SEGFLAGS_32BIT       0x2000
141
142 typedef struct {
143     WORD width;
144     DWORD offset;
145 } CHAR_TABLE_ENTRY;
146
147 typedef struct {
148     DWORD version;
149     ULONG numSizes;
150 } eblcHeader_t;
151
152 typedef struct {
153     CHAR ascender;
154     CHAR descender;
155     BYTE widthMax;
156     CHAR caretSlopeNumerator;
157     CHAR caretSlopeDenominator;
158     CHAR caretOffset;
159     CHAR minOriginSB;
160     CHAR minAdvanceSB;
161     CHAR maxBeforeBL;
162     CHAR maxAfterBL;
163     CHAR pad1;
164     CHAR pad2;
165 } sbitLineMetrics_t;
166
167 typedef struct {
168     ULONG indexSubTableArrayOffset;
169     ULONG indexTableSize;
170     ULONG numberOfIndexSubTables;
171     ULONG colorRef;
172     sbitLineMetrics_t hori;
173     sbitLineMetrics_t vert;
174     USHORT startGlyphIndex;
175     USHORT endGlyphIndex;
176     BYTE ppemX;
177     BYTE ppemY;
178     BYTE bitDepth;
179     CHAR flags;
180 } bitmapSizeTable_t;
181
182 typedef struct
183 {
184     FT_Int major;
185     FT_Int minor;
186     FT_Int patch;
187 } FT_Version_t;
188 static FT_Version_t FT_Version;
189
190 #define GET_BE_WORD(ptr)  MAKEWORD( ((BYTE *)(ptr))[1], ((BYTE *)(ptr))[0] )
191 #define GET_BE_DWORD(ptr) ((DWORD)MAKELONG( GET_BE_WORD(&((WORD *)(ptr))[1]), \
192                                             GET_BE_WORD(&((WORD *)(ptr))[0]) ))
193
194 #include "poppack.h"
195
196 struct fontinfo
197 {
198     FNT_HEADER hdr;
199     CHAR_TABLE_ENTRY dfCharTable[258];
200     BYTE *data;
201 };
202
203 static const BYTE MZ_hdr[] =
204 {
205     'M',  'Z',  0x0d, 0x01, 0x01, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00,
206     0xb8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
207     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
208     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00,
209     0x0e, 0x1f, 0xba, 0x0e, 0x00, 0xb4, 0x09, 0xcd, 0x21, 0xb8, 0x01, 0x4c, 0xcd, 0x21, 'T',  'h',
210     'i',  's',  ' ',  'P',  'r',  'o',  'g',  'r',  'a',  'm',  ' ',  'c',  'a',  'n',  'n',  'o',
211     't',  ' ',  'b',  'e',  ' ',  'r',  'u',  'n',  ' ',  'i',  'n',  ' ',  'D',  'O',  'S',  ' ',
212     'm',  'o',  'd',  'e',  0x0d, 0x0a, 0x24, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
213 };
214
215 static char *option_output;
216 static int option_defchar = ' ';
217 static int option_dpi = 96;
218 static int option_fnt_mode = 0;
219 static int option_quiet = 0;
220
221 static const char *output_name;
222
223 static FT_Library ft_library;
224
225 static void usage(char **argv)
226 {
227     fprintf(stderr, "%s [options] input.ttf ppem,enc,avg_width ...\n", argv[0]);
228     fprintf(stderr, "Options:\n");
229     fprintf(stderr, "  -h       Display help\n" );
230     fprintf(stderr, "  -d char  Set the font default char\n" );
231     fprintf(stderr, "  -o file  Set output file name\n" );
232     fprintf(stderr, "  -q       Quiet mode\n" );
233     fprintf(stderr, "  -r dpi   Set resolution in DPI (default: 96)\n" );
234     fprintf(stderr, "  -s       Single .fnt file mode\n" );
235 }
236
237 #ifndef __GNUC__
238 #define __attribute__(X)
239 #endif
240
241 /* atexit handler to cleanup files */
242 static void cleanup(void)
243 {
244     if (output_name) unlink( output_name );
245 }
246
247 static void exit_on_signal( int sig )
248 {
249     exit(1);  /* this will call the atexit functions */
250 }
251
252 static void error(const char *s, ...) __attribute__((format (printf, 1, 2)));
253
254 static void error(const char *s, ...)
255 {
256     va_list ap;
257     va_start(ap, s);
258     fprintf(stderr, "Error: ");
259     vfprintf(stderr, s, ap);
260     va_end(ap);
261     exit(1);
262 }
263
264 static const char *get_face_name( const struct fontinfo *info )
265 {
266     return (const char *)info->data + info->hdr.fi.dfFace - info->hdr.fi.dfBitsOffset;
267 }
268
269 static int lookup_charset(int enc)
270 {
271     /* FIXME: make winelib app and use TranslateCharsetInfo */
272     switch(enc) {
273     case 1250:
274         return EE_CHARSET;
275     case 1251:
276         return RUSSIAN_CHARSET;
277     case 1252:
278         return ANSI_CHARSET;
279     case 1253:
280         return GREEK_CHARSET;
281     case 1254:
282         return TURKISH_CHARSET;
283     case 1255:
284         return HEBREW_CHARSET;
285     case 1256:
286         return ARABIC_CHARSET;
287     case 1257:
288         return BALTIC_CHARSET;
289     case 1258:
290         return VIETNAMESE_CHARSET;
291     case 437:
292     case 737:
293     case 775:
294     case 850:
295     case 852:
296     case 855:
297     case 857:
298     case 860:
299     case 861:
300     case 862:
301     case 863:
302     case 864:
303     case 865:
304     case 866:
305     case 869:
306         return OEM_CHARSET;
307     case 874:
308         return THAI_CHARSET;
309     case 932:
310         return SHIFTJIS_CHARSET;
311     case 936:
312         return GB2312_CHARSET;
313     case 949:
314         return HANGUL_CHARSET;
315     case 950:
316         return CHINESEBIG5_CHARSET;
317     }
318     fprintf(stderr, "Unknown encoding %d - using OEM_CHARSET\n", enc);
319
320     return OEM_CHARSET;
321 }
322
323 static int get_char(const union cptable *cptable, int enc, int index)
324 {
325     /* Korean has the Won sign in place of '\\' */
326     if(enc == 949 && index == '\\')
327         return 0x20a9;
328
329     return cptable->sbcs.cp2uni[index];
330 }
331
332 static struct fontinfo *fill_fontinfo( const char *face_name, int ppem, int enc, int dpi,
333                                        unsigned char def_char, int avg_width )
334 {
335     FT_Face face;
336     int ascent = 0, il, el, width_bytes = 0, space_size, max_width = 0;
337     BYTE left_byte, right_byte, byte;
338     DWORD start;
339     int i, x, y, x_off, x_end, first_char;
340     FT_UInt gi;
341     int num_names;
342     const union cptable *cptable;
343     FT_SfntName sfntname;
344     TT_OS2 *os2;
345     FT_ULong needed;
346     eblcHeader_t *eblc;
347     bitmapSizeTable_t *size_table;
348     int num_sizes;
349     struct fontinfo *info;
350     size_t data_pos;
351
352     if (FT_New_Face(ft_library, face_name, 0, &face)) error( "Cannot open face %s\n", face_name );
353     if (FT_Set_Pixel_Sizes(face, ppem, ppem)) error( "cannot set face size to %u\n", ppem );
354
355     cptable = wine_cp_get_table(enc);
356     if(!cptable)
357         error("Can't find codepage %d\n", enc);
358
359     if(cptable->info.char_size != 1) {
360         /* for double byte charsets we actually want to use cp1252 */
361         cptable = wine_cp_get_table(1252);
362         if(!cptable)
363             error("Can't find codepage 1252\n");
364     }
365
366     assert( face->size->metrics.y_ppem == ppem );
367
368     needed = 0;
369     if (FT_Load_Sfnt_Table(face, TTAG_EBLC, 0, NULL, &needed))
370         fprintf(stderr,"Can't find EBLC table\n");
371     else
372     {
373         eblc = malloc(needed);
374         FT_Load_Sfnt_Table(face, TTAG_EBLC, 0, (FT_Byte *)eblc, &needed);
375
376         num_sizes = GET_BE_DWORD(&eblc->numSizes);
377
378         size_table = (bitmapSizeTable_t *)(eblc + 1);
379         for(i = 0; i < num_sizes; i++)
380         {
381             if(size_table->hori.ascender - size_table->hori.descender == ppem)
382             {
383                 ascent = size_table->hori.ascender;
384                 break;
385             }
386             size_table++;
387         }
388
389         free(eblc);
390     }
391
392     /* Versions of fontforge prior to early 2006 have incorrect
393        ascender values in the eblc table, so we won't find the 
394        correct bitmapSizeTable.  In this case use the height of
395        the Aring glyph instead. */
396     if(ascent == 0) 
397     {
398         if(FT_Load_Char(face, 0xc5, FT_LOAD_DEFAULT))
399             error("Can't find Aring\n");
400         ascent = face->glyph->metrics.horiBearingY >> 6;
401     }
402
403     start = sizeof(FNT_HEADER);
404
405     if(FT_Load_Char(face, 'M', FT_LOAD_DEFAULT))
406         error("Can't find M\n");
407     il = ascent - (face->glyph->metrics.height >> 6);
408
409     /* Hack: Courier has no internal leading, nor do any Chinese or Japanese fonts */
410     if(!strcmp(face->family_name, "Courier") || enc == 936 || enc == 950 || enc == 932)
411         il = 0;
412     /* Japanese system fonts have an external leading (not small font) */
413     if (enc == 932 && ppem > 11)
414         el = 2;
415     else
416         el = 0;
417
418     first_char = FT_Get_First_Char(face, &gi);
419     if(first_char == 0xd) /* fontforge's first glyph is 0xd, we'll catch this and skip it */
420         first_char = 32; /* FT_Get_Next_Char for some reason returns too high
421                             number in this case */
422
423     info = calloc( 1, sizeof(*info) );
424
425     info->hdr.fi.dfFirstChar = first_char;
426     info->hdr.fi.dfLastChar = 0xff;
427     start += ((unsigned char)info->hdr.fi.dfLastChar - (unsigned char)info->hdr.fi.dfFirstChar + 3 ) * sizeof(*info->dfCharTable);
428
429     num_names = FT_Get_Sfnt_Name_Count(face);
430     for(i = 0; i <num_names; i++) {
431         FT_Get_Sfnt_Name(face, i, &sfntname);
432         if(sfntname.platform_id == 1 && sfntname.encoding_id == 0 &&
433            sfntname.language_id == 0 && sfntname.name_id == 0) {
434             size_t len = min( sfntname.string_len, sizeof(info->hdr.dfCopyright)-1 );
435             memcpy(info->hdr.dfCopyright, sfntname.string, len);
436             info->hdr.dfCopyright[len] = 0;
437         }
438     }
439
440     os2 = FT_Get_Sfnt_Table(face, ft_sfnt_os2);
441     for(i = first_char; i < 0x100; i++) {
442         int c = get_char(cptable, enc, i);
443         gi = FT_Get_Char_Index(face, c);
444         if(gi == 0 && !option_quiet)
445             fprintf(stderr, "warning: %s %u: missing glyph for char %04x\n",
446                     face->family_name, ppem, cptable->sbcs.cp2uni[i]);
447         if(FT_Load_Char(face, c, FT_LOAD_DEFAULT)) {
448             fprintf(stderr, "error loading char %d - bad news!\n", i);
449             continue;
450         }
451         info->dfCharTable[i].width = face->glyph->metrics.horiAdvance >> 6;
452         info->dfCharTable[i].offset = start + (width_bytes * ppem);
453         width_bytes += ((face->glyph->metrics.horiAdvance >> 6) + 7) >> 3;
454         if(max_width < (face->glyph->metrics.horiAdvance >> 6))
455             max_width = face->glyph->metrics.horiAdvance >> 6;
456     }
457     /* space */
458     space_size = (ppem + 3) / 4;
459     info->dfCharTable[i].width = space_size;
460     info->dfCharTable[i].offset = start + (width_bytes * ppem);
461     width_bytes += (space_size + 7) >> 3;
462     /* sentinel */
463     info->dfCharTable[++i].width = 0;
464     info->dfCharTable[i].offset = start + (width_bytes * ppem);
465
466     info->hdr.fi.dfType = 0;
467     info->hdr.fi.dfPoints = ((ppem - il - el) * 72 + dpi/2) / dpi;
468     info->hdr.fi.dfVertRes = dpi;
469     info->hdr.fi.dfHorizRes = dpi;
470     info->hdr.fi.dfAscent = ascent;
471     info->hdr.fi.dfInternalLeading = il;
472     info->hdr.fi.dfExternalLeading = el;
473     info->hdr.fi.dfItalic = (face->style_flags & FT_STYLE_FLAG_ITALIC) ? 1 : 0;
474     info->hdr.fi.dfUnderline = 0;
475     info->hdr.fi.dfStrikeOut = 0;
476     info->hdr.fi.dfWeight = os2->usWeightClass;
477     info->hdr.fi.dfCharSet = lookup_charset(enc);
478     info->hdr.fi.dfPixWidth = (face->face_flags & FT_FACE_FLAG_FIXED_WIDTH) ? avg_width : 0;
479     info->hdr.fi.dfPixHeight = ppem;
480     info->hdr.fi.dfPitchAndFamily = FT_IS_FIXED_WIDTH(face) ? 0 : TMPF_FIXED_PITCH;
481     switch(os2->panose[PAN_FAMILYTYPE_INDEX]) {
482     case PAN_FAMILY_SCRIPT:
483         info->hdr.fi.dfPitchAndFamily |= FF_SCRIPT;
484         break;
485     case PAN_FAMILY_DECORATIVE:
486     case PAN_FAMILY_PICTORIAL:
487         info->hdr.fi.dfPitchAndFamily |= FF_DECORATIVE;
488         break;
489     case PAN_FAMILY_TEXT_DISPLAY:
490         if(info->hdr.fi.dfPitchAndFamily == 0) /* fixed */
491             info->hdr.fi.dfPitchAndFamily = FF_MODERN;
492         else {
493             switch(os2->panose[PAN_SERIFSTYLE_INDEX]) {
494             case PAN_SERIF_NORMAL_SANS:
495             case PAN_SERIF_OBTUSE_SANS:
496             case PAN_SERIF_PERP_SANS:
497                 info->hdr.fi.dfPitchAndFamily |= FF_SWISS;
498                 break;
499             default:
500                 info->hdr.fi.dfPitchAndFamily |= FF_ROMAN;
501             }
502         }
503         break;
504     default:
505         info->hdr.fi.dfPitchAndFamily |= FF_DONTCARE;
506     }
507
508     info->hdr.fi.dfAvgWidth = avg_width;
509     info->hdr.fi.dfMaxWidth = max_width;
510     info->hdr.fi.dfDefaultChar = def_char - info->hdr.fi.dfFirstChar;
511     info->hdr.fi.dfBreakChar = ' ' - info->hdr.fi.dfFirstChar;
512     info->hdr.fi.dfWidthBytes = (width_bytes + 1) & ~1;
513
514     info->hdr.fi.dfFace = start + info->hdr.fi.dfWidthBytes * ppem;
515     info->hdr.fi.dfBitsOffset = start;
516     info->hdr.fi.dfFlags = 0x10; /* DFF_1COLOR */
517     info->hdr.fi.dfFlags |= FT_IS_FIXED_WIDTH(face) ? 1 : 2; /* DFF_FIXED : DFF_PROPORTIONAL */
518
519     info->hdr.dfVersion = 0x300;
520     info->hdr.dfSize = start + info->hdr.fi.dfWidthBytes * ppem + strlen(face->family_name) + 1;
521
522     info->data = calloc( info->hdr.dfSize - start, 1 );
523     data_pos = 0;
524
525     for(i = first_char; i < 0x100; i++) {
526         int c = get_char(cptable, enc, i);
527         if(FT_Load_Char(face, c, FT_LOAD_DEFAULT)) {
528             continue;
529         }
530         assert(info->dfCharTable[i].width == face->glyph->metrics.horiAdvance >> 6);
531
532         for(x = 0; x < ((info->dfCharTable[i].width + 7) / 8); x++) {
533             for(y = 0; y < ppem; y++) {
534                 if(y < ascent - face->glyph->bitmap_top ||
535                    y >=  face->glyph->bitmap.rows + ascent - face->glyph->bitmap_top) {
536                     info->data[data_pos++] = 0;
537                     continue;
538                 }
539                 x_off = face->glyph->bitmap_left / 8;
540                 x_end = (face->glyph->bitmap_left + face->glyph->bitmap.width - 1) / 8;
541                 if(x < x_off || x > x_end) {
542                     info->data[data_pos++] = 0;
543                     continue;
544                 }
545                 if(x == x_off)
546                     left_byte = 0;
547                 else
548                     left_byte = face->glyph->bitmap.buffer[(y - (ascent - face->glyph->bitmap_top)) * face->glyph->bitmap.pitch + x - x_off - 1];
549
550                 /* On the last non-trivial output byte (x == x_end) have we got one or two input bytes */
551                 if(x == x_end && (face->glyph->bitmap_left % 8 != 0) && ((face->glyph->bitmap.width % 8 == 0) || (x != (((face->glyph->bitmap.width) & ~0x7) + face->glyph->bitmap_left) / 8)))
552                     right_byte = 0;
553                 else
554                     right_byte = face->glyph->bitmap.buffer[(y - (ascent - face->glyph->bitmap_top)) * face->glyph->bitmap.pitch + x - x_off];
555
556                 byte = (left_byte << (8 - (face->glyph->bitmap_left & 7))) & 0xff;
557                 byte |= ((right_byte >> (face->glyph->bitmap_left & 7)) & 0xff);
558                 info->data[data_pos++] = byte;
559             }
560         }
561     }
562     data_pos += ((space_size + 7) / 8) * ppem;
563     if (width_bytes & 1) data_pos += ppem;
564
565     memcpy( info->data + data_pos, face->family_name, strlen( face->family_name ));
566     data_pos += strlen( face->family_name ) + 1;
567     assert( start + data_pos == info->hdr.dfSize );
568
569     FT_Done_Face( face );
570     return info;
571 }
572
573 static void write_fontinfo( const struct fontinfo *info, FILE *fp )
574 {
575     fwrite( &info->hdr, sizeof(info->hdr), 1, fp );
576     fwrite( info->dfCharTable + info->hdr.fi.dfFirstChar, sizeof(*info->dfCharTable),
577             ((unsigned char)info->hdr.fi.dfLastChar - (unsigned char)info->hdr.fi.dfFirstChar) + 3, fp );
578     fwrite( info->data, info->hdr.dfSize - info->hdr.fi.dfBitsOffset, 1, fp );
579 }
580
581 /* parse options from the argv array and remove all the recognized ones */
582 static char **parse_options( int argc, char **argv )
583 {
584     int optc;
585
586     while ((optc = getopt( argc, argv, "d:ho:qr:s" )) != -1)
587     {
588         switch(optc)
589         {
590         case 'd':
591             option_defchar = atoi( optarg );
592             break;
593         case 'o':
594             option_output = strdup( optarg );
595             break;
596         case 'q':
597             option_quiet = 1;
598             break;
599         case 'r':
600             option_dpi = atoi( optarg );
601             break;
602         case 's':
603             option_fnt_mode = 1;
604             break;
605         case 'h':
606             usage(argv);
607             exit(0);
608         case '?':
609             usage(argv);
610             exit(1);
611         }
612     }
613     return &argv[optind];
614 }
615
616 int main(int argc, char **argv)
617 {
618     int i, j;
619     FILE *ofp;
620     short align, num_files;
621     int resource_table_len, non_resident_name_len, resident_name_len;
622     unsigned short resource_table_off, resident_name_off, module_ref_off, non_resident_name_off, fontdir_off, font_off;
623     char resident_name[200];
624     int fontdir_len = 2;
625     char non_resident_name[200];
626     unsigned short first_res = 0x0050, pad, res;
627     IMAGE_OS2_HEADER NE_hdr;
628     NE_TYPEINFO rc_type;
629     NE_NAMEINFO rc_name;
630     struct fontinfo **info;
631     char *input_file;
632     char **args;
633
634     args = parse_options( argc, argv );
635
636     input_file = *args++;
637     if (!input_file || !*args)
638     {
639         usage(argv);
640         exit(1);
641     }
642
643     if(FT_Init_FreeType(&ft_library))
644         error("ft init failure\n");
645
646     FT_Version.major=FT_Version.minor=FT_Version.patch=-1;
647     FT_Library_Version(ft_library,&FT_Version.major,&FT_Version.minor,&FT_Version.patch);
648
649     num_files = 0;
650     while (args[num_files]) num_files++;
651
652     if (option_fnt_mode && num_files > 1)
653         error( "can only specify one font in .fnt mode\n" );
654
655     info = malloc( num_files * sizeof(*info) );
656     for (i = 0; i < num_files; i++)
657     {
658         int ppem, enc, avg_width;
659         const char *name;
660
661         if (sscanf( args[i], "%d,%d,%d", &ppem, &enc, &avg_width ) != 3)
662         {
663             usage(argv);
664             exit(1);
665         }
666         if (!(info[i] = fill_fontinfo( input_file, ppem, enc, option_dpi, option_defchar, avg_width )))
667             exit(1);
668
669         name = get_face_name( info[i] );
670         fontdir_len += 0x74 + strlen(name) + 1;
671         if(i == 0) {
672             sprintf(non_resident_name, "FONTRES 100,%d,%d : %s %d",
673                     info[i]->hdr.fi.dfVertRes, info[i]->hdr.fi.dfHorizRes,
674                     name, info[i]->hdr.fi.dfPoints );
675             strcpy(resident_name, name);
676         } else {
677             sprintf(non_resident_name + strlen(non_resident_name), ",%d", info[i]->hdr.fi.dfPoints );
678         }
679     }
680
681     if (option_dpi <= 108)
682         strcat(non_resident_name, " (VGA res)");
683     else
684         strcat(non_resident_name, " (8514 res)");
685     non_resident_name_len = strlen(non_resident_name) + 4;
686
687     /* shift count + fontdir entry + num_files of font + nul type + \007FONTDIR */
688     resource_table_len = sizeof(align) + sizeof("FONTDIR") +
689                          sizeof(NE_TYPEINFO) + sizeof(NE_NAMEINFO) +
690                          sizeof(NE_TYPEINFO) + sizeof(NE_NAMEINFO) * num_files +
691                          sizeof(NE_TYPEINFO);
692     resource_table_off = sizeof(NE_hdr);
693     resident_name_off = resource_table_off + resource_table_len;
694     resident_name_len = strlen(resident_name) + 4;
695     module_ref_off = resident_name_off + resident_name_len;
696     non_resident_name_off = sizeof(MZ_hdr) + module_ref_off + sizeof(align);
697
698     memset(&NE_hdr, 0, sizeof(NE_hdr));
699     NE_hdr.ne_magic = 0x454e;
700     NE_hdr.ne_ver = 5;
701     NE_hdr.ne_rev = 1;
702     NE_hdr.ne_flags = NE_FFLAGS_LIBMODULE | NE_FFLAGS_GUI;
703     NE_hdr.ne_cbnrestab = non_resident_name_len;
704     NE_hdr.ne_segtab = sizeof(NE_hdr);
705     NE_hdr.ne_rsrctab = sizeof(NE_hdr);
706     NE_hdr.ne_restab = resident_name_off;
707     NE_hdr.ne_modtab = module_ref_off;
708     NE_hdr.ne_imptab = module_ref_off;
709     NE_hdr.ne_enttab = NE_hdr.ne_modtab;
710     NE_hdr.ne_nrestab = non_resident_name_off;
711     NE_hdr.ne_align = 4;
712     NE_hdr.ne_exetyp = NE_OSFLAGS_WINDOWS;
713     NE_hdr.ne_expver = 0x400;
714
715     fontdir_off = (non_resident_name_off + non_resident_name_len + 15) & ~0xf;
716     font_off = (fontdir_off + fontdir_len + 15) & ~0x0f;
717
718     atexit( cleanup );
719     signal( SIGTERM, exit_on_signal );
720     signal( SIGINT, exit_on_signal );
721 #ifdef SIGHUP
722     signal( SIGHUP, exit_on_signal );
723 #endif
724
725     if (!option_output)  /* build a default output name */
726     {
727         char *p = strrchr( input_file, '/' );
728         if (p) p++;
729         else p = input_file;
730         option_output = malloc( strlen(p) + sizeof(".fon") );
731         strcpy( option_output, p );
732         p = strrchr( option_output, '.' );
733         if (!p) p = option_output + strlen(option_output);
734         strcpy( p, option_fnt_mode ? ".fnt" : ".fon" );
735     }
736
737     if (!(ofp = fopen(option_output, "wb")))
738     {
739         perror( option_output );
740         exit(1);
741     }
742     output_name = option_output;
743     if (option_fnt_mode)
744     {
745         write_fontinfo( info[0], ofp );
746         goto done;
747     }
748
749     fwrite(MZ_hdr, sizeof(MZ_hdr), 1, ofp);
750     fwrite(&NE_hdr, sizeof(NE_hdr), 1, ofp);
751
752     align = 4;
753     fwrite(&align, sizeof(align), 1, ofp);
754
755     rc_type.type_id = NE_RSCTYPE_FONTDIR;
756     rc_type.count = 1;
757     rc_type.resloader = 0;
758     fwrite(&rc_type, sizeof(rc_type), 1, ofp);
759
760     rc_name.offset = fontdir_off >> 4;
761     rc_name.length = (fontdir_len + 15) >> 4;
762     rc_name.flags = NE_SEGFLAGS_MOVEABLE | NE_SEGFLAGS_PRELOAD;
763     rc_name.id = resident_name_off - sizeof("FONTDIR") - NE_hdr.ne_rsrctab;
764     rc_name.handle = 0;
765     rc_name.usage = 0;
766     fwrite(&rc_name, sizeof(rc_name), 1, ofp);
767
768     rc_type.type_id = NE_RSCTYPE_FONT;
769     rc_type.count = num_files;
770     rc_type.resloader = 0;
771     fwrite(&rc_type, sizeof(rc_type), 1, ofp);
772
773     for(res = first_res | 0x8000, i = 0; i < num_files; i++, res++) {
774         int len = (info[i]->hdr.dfSize + 15) & ~0xf;
775
776         rc_name.offset = font_off >> 4;
777         rc_name.length = len >> 4;
778         rc_name.flags = NE_SEGFLAGS_MOVEABLE | NE_SEGFLAGS_SHAREABLE | NE_SEGFLAGS_DISCARDABLE;
779         rc_name.id = res;
780         rc_name.handle = 0;
781         rc_name.usage = 0;
782         fwrite(&rc_name, sizeof(rc_name), 1, ofp);
783
784         font_off += len;
785     }
786
787     /* empty type info */
788     memset(&rc_type, 0, sizeof(rc_type));
789     fwrite(&rc_type, sizeof(rc_type), 1, ofp);
790
791     fputc(strlen("FONTDIR"), ofp);
792     fwrite("FONTDIR", strlen("FONTDIR"), 1, ofp);
793     fputc(strlen(resident_name), ofp);
794     fwrite(resident_name, strlen(resident_name), 1, ofp);
795
796     fputc(0x00, ofp);    fputc(0x00, ofp);
797     fputc(0x00, ofp);
798     fputc(0x00, ofp);    fputc(0x00, ofp);
799
800     fputc(strlen(non_resident_name), ofp);
801     fwrite(non_resident_name, strlen(non_resident_name), 1, ofp);
802     fputc(0x00, ofp); /* terminator */
803
804     /* empty ne_modtab and ne_imptab */
805     fputc(0x00, ofp);
806     fputc(0x00, ofp);
807
808     pad = ftell(ofp) & 0xf;
809     if(pad != 0)
810         pad = 0x10 - pad;
811     for(i = 0; i < pad; i++)
812         fputc(0x00, ofp);
813
814     /* FONTDIR resource */
815     fwrite(&num_files, sizeof(num_files), 1, ofp);
816
817     for(res = first_res, i = 0; i < num_files; i++, res++) {
818         const char *name = get_face_name( info[i] );
819         fwrite(&res, sizeof(res), 1, ofp);
820         fwrite(&info[i]->hdr, FIELD_OFFSET(FNT_HEADER,fi.dfBitsOffset), 1, ofp);
821         fputc(0x00, ofp);
822         fwrite(name, strlen(name) + 1, 1, ofp);
823     }
824
825     pad = ftell(ofp) & 0xf;
826     if(pad != 0)
827         pad = 0x10 - pad;
828     for(i = 0; i < pad; i++)
829         fputc(0x00, ofp);
830
831     for(res = first_res, i = 0; i < num_files; i++, res++) {
832         write_fontinfo( info[i], ofp );
833         pad = info[i]->hdr.dfSize & 0xf;
834         if(pad != 0)
835             pad = 0x10 - pad;
836         for(j = 0; j < pad; j++)
837             fputc(0x00, ofp);
838     }
839 done:
840     fclose(ofp);
841     output_name = NULL;
842     exit(0);
843 }
844
845 #else /* HAVE_FREETYPE */
846
847 int main(int argc, char **argv)
848 {
849     fprintf( stderr, "%s needs to be built with FreeType support\n", argv[0] );
850     exit(1);
851 }
852
853 #endif /* HAVE_FREETYPE */