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