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