cmd: Add support for usebackq (for /f).
[wine/multimedia.git] / tools / sfnt2fnt.c
blob07c82b18939f8a0caa4a21587e750a57dc077767
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
44 #include "wine/unicode.h"
45 #include "wingdi.h"
46 #include "basetsd.h"
48 #include "pshpack1.h"
50 typedef struct
52 INT16 dfType;
53 INT16 dfPoints;
54 INT16 dfVertRes;
55 INT16 dfHorizRes;
56 INT16 dfAscent;
57 INT16 dfInternalLeading;
58 INT16 dfExternalLeading;
59 BYTE dfItalic;
60 BYTE dfUnderline;
61 BYTE dfStrikeOut;
62 INT16 dfWeight;
63 BYTE dfCharSet;
64 INT16 dfPixWidth;
65 INT16 dfPixHeight;
66 BYTE dfPitchAndFamily;
67 INT16 dfAvgWidth;
68 INT16 dfMaxWidth;
69 BYTE dfFirstChar;
70 BYTE dfLastChar;
71 BYTE dfDefaultChar;
72 BYTE dfBreakChar;
73 INT16 dfWidthBytes;
74 LONG dfDevice;
75 LONG dfFace;
76 LONG dfBitsPointer;
77 LONG dfBitsOffset;
78 BYTE dfReserved;
79 LONG dfFlags;
80 INT16 dfAspace;
81 INT16 dfBspace;
82 INT16 dfCspace;
83 LONG dfColorPointer;
84 LONG dfReserved1[4];
85 } FONTINFO16;
87 typedef struct
89 WORD dfVersion;
90 DWORD dfSize;
91 char dfCopyright[60];
92 FONTINFO16 fi;
93 } FNT_HEADER;
95 typedef struct
97 WORD offset;
98 WORD length;
99 WORD flags;
100 WORD id;
101 WORD handle;
102 WORD usage;
103 } NE_NAMEINFO;
105 typedef struct
107 WORD type_id;
108 WORD count;
109 DWORD resloader;
110 } NE_TYPEINFO;
112 #define NE_FFLAGS_SINGLEDATA 0x0001
113 #define NE_FFLAGS_MULTIPLEDATA 0x0002
114 #define NE_FFLAGS_WIN32 0x0010
115 #define NE_FFLAGS_FRAMEBUF 0x0100
116 #define NE_FFLAGS_CONSOLE 0x0200
117 #define NE_FFLAGS_GUI 0x0300
118 #define NE_FFLAGS_SELFLOAD 0x0800
119 #define NE_FFLAGS_LINKERROR 0x2000
120 #define NE_FFLAGS_CALLWEP 0x4000
121 #define NE_FFLAGS_LIBMODULE 0x8000
123 #define NE_OSFLAGS_WINDOWS 0x02
125 #define NE_RSCTYPE_FONTDIR 0x8007
126 #define NE_RSCTYPE_FONT 0x8008
127 #define NE_RSCTYPE_SCALABLE_FONTPATH 0x80cc
129 #define NE_SEGFLAGS_DATA 0x0001
130 #define NE_SEGFLAGS_ALLOCATED 0x0002
131 #define NE_SEGFLAGS_LOADED 0x0004
132 #define NE_SEGFLAGS_ITERATED 0x0008
133 #define NE_SEGFLAGS_MOVEABLE 0x0010
134 #define NE_SEGFLAGS_SHAREABLE 0x0020
135 #define NE_SEGFLAGS_PRELOAD 0x0040
136 #define NE_SEGFLAGS_EXECUTEONLY 0x0080
137 #define NE_SEGFLAGS_READONLY 0x0080
138 #define NE_SEGFLAGS_RELOC_DATA 0x0100
139 #define NE_SEGFLAGS_SELFLOAD 0x0800
140 #define NE_SEGFLAGS_DISCARDABLE 0x1000
141 #define NE_SEGFLAGS_32BIT 0x2000
143 typedef struct {
144 WORD width;
145 DWORD offset;
146 } CHAR_TABLE_ENTRY;
148 typedef struct {
149 DWORD version;
150 ULONG numSizes;
151 } eblcHeader_t;
153 typedef struct {
154 CHAR ascender;
155 CHAR descender;
156 BYTE widthMax;
157 CHAR caretSlopeNumerator;
158 CHAR caretSlopeDenominator;
159 CHAR caretOffset;
160 CHAR minOriginSB;
161 CHAR minAdvanceSB;
162 CHAR maxBeforeBL;
163 CHAR maxAfterBL;
164 CHAR pad1;
165 CHAR pad2;
166 } sbitLineMetrics_t;
168 typedef struct {
169 ULONG indexSubTableArrayOffset;
170 ULONG indexTableSize;
171 ULONG numberOfIndexSubTables;
172 ULONG colorRef;
173 sbitLineMetrics_t hori;
174 sbitLineMetrics_t vert;
175 USHORT startGlyphIndex;
176 USHORT endGlyphIndex;
177 BYTE ppemX;
178 BYTE ppemY;
179 BYTE bitDepth;
180 CHAR flags;
181 } bitmapSizeTable_t;
183 typedef struct
185 FT_Int major;
186 FT_Int minor;
187 FT_Int patch;
188 } FT_Version_t;
189 static FT_Version_t FT_Version;
191 #include "poppack.h"
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]) ))
196 #ifdef WORDS_BIGENDIAN
197 static WORD byteswap_word(WORD x)
199 return ( ( (x & 0xff) << 8) |
200 ( (x & 0xff00) >> 8) );
202 static DWORD byteswap_dword(DWORD x)
204 return ( ( (x & 0xff) << 24) |
205 ( (x & 0xff00) << 8) |
206 ( (x & 0xff0000) >> 8) |
207 ( (x & 0xff000000) >> 24) );
209 # define PUT_LE_WORD(x) byteswap_word(x)
210 # define PUT_LE_DWORD(x) byteswap_dword(x)
211 #else
212 # define PUT_LE_WORD(x) (x)
213 # define PUT_LE_DWORD(x) (x)
214 #endif
216 struct fontinfo
218 FNT_HEADER hdr;
219 CHAR_TABLE_ENTRY dfCharTable[258];
220 BYTE *data;
223 static const BYTE MZ_hdr[] =
225 'M', 'Z', 0x0d, 0x01, 0x01, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00,
226 0xb8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
227 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
228 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00,
229 0x0e, 0x1f, 0xba, 0x0e, 0x00, 0xb4, 0x09, 0xcd, 0x21, 0xb8, 0x01, 0x4c, 0xcd, 0x21, 'T', 'h',
230 'i', 's', ' ', 'P', 'r', 'o', 'g', 'r', 'a', 'm', ' ', 'c', 'a', 'n', 'n', 'o',
231 't', ' ', 'b', 'e', ' ', 'r', 'u', 'n', ' ', 'i', 'n', ' ', 'D', 'O', 'S', ' ',
232 'm', 'o', 'd', 'e', 0x0d, 0x0a, 0x24, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
235 static char *option_output;
236 static int option_defchar = ' ';
237 static int option_dpi = 96;
238 static int option_fnt_mode = 0;
239 static int option_quiet = 0;
241 static const char *output_name;
243 static FT_Library ft_library;
245 static void usage(char **argv)
247 fprintf(stderr, "%s [options] input.ttf ppem,enc,avg_width ...\n", argv[0]);
248 fprintf(stderr, "Options:\n");
249 fprintf(stderr, " -h Display help\n" );
250 fprintf(stderr, " -d char Set the font default char\n" );
251 fprintf(stderr, " -o file Set output file name\n" );
252 fprintf(stderr, " -q Quiet mode\n" );
253 fprintf(stderr, " -r dpi Set resolution in DPI (default: 96)\n" );
254 fprintf(stderr, " -s Single .fnt file mode\n" );
257 #ifndef __GNUC__
258 #define __attribute__(X)
259 #endif
261 /* atexit handler to cleanup files */
262 static void cleanup(void)
264 if (output_name) unlink( output_name );
267 static void exit_on_signal( int sig )
269 exit(1); /* this will call the atexit functions */
272 static void error(const char *s, ...) __attribute__((format (printf, 1, 2)));
274 static void error(const char *s, ...)
276 va_list ap;
277 va_start(ap, s);
278 fprintf(stderr, "Error: ");
279 vfprintf(stderr, s, ap);
280 va_end(ap);
281 exit(1);
284 static const char *get_face_name( const struct fontinfo *info )
286 return (const char *)info->data + info->hdr.fi.dfFace - info->hdr.fi.dfBitsOffset;
289 static int lookup_charset(int enc)
291 /* FIXME: make winelib app and use TranslateCharsetInfo */
292 switch(enc) {
293 case 1250:
294 return EE_CHARSET;
295 case 1251:
296 return RUSSIAN_CHARSET;
297 case 1252:
298 return ANSI_CHARSET;
299 case 1253:
300 return GREEK_CHARSET;
301 case 1254:
302 return TURKISH_CHARSET;
303 case 1255:
304 return HEBREW_CHARSET;
305 case 1256:
306 return ARABIC_CHARSET;
307 case 1257:
308 return BALTIC_CHARSET;
309 case 1258:
310 return VIETNAMESE_CHARSET;
311 case 437:
312 case 737:
313 case 775:
314 case 850:
315 case 852:
316 case 855:
317 case 857:
318 case 860:
319 case 861:
320 case 862:
321 case 863:
322 case 864:
323 case 865:
324 case 866:
325 case 869:
326 return OEM_CHARSET;
327 case 874:
328 return THAI_CHARSET;
329 case 932:
330 return SHIFTJIS_CHARSET;
331 case 936:
332 return GB2312_CHARSET;
333 case 949:
334 return HANGUL_CHARSET;
335 case 950:
336 return CHINESEBIG5_CHARSET;
338 fprintf(stderr, "Unknown encoding %d - using OEM_CHARSET\n", enc);
340 return OEM_CHARSET;
343 static int get_char(const union cptable *cptable, int enc, int index)
345 /* Korean has the Won sign in place of '\\' */
346 if(enc == 949 && index == '\\')
347 return 0x20a9;
349 return cptable->sbcs.cp2uni[index];
352 static struct fontinfo *fill_fontinfo( const char *face_name, int ppem, int enc, int dpi,
353 unsigned char def_char, int avg_width )
355 FT_Face face;
356 int ascent = 0, il, el, width_bytes = 0, space_size, max_width = 0;
357 BYTE left_byte, right_byte, byte;
358 DWORD start;
359 int i, x, y, x_off, x_end, first_char;
360 FT_UInt gi;
361 int num_names;
362 const union cptable *cptable;
363 FT_SfntName sfntname;
364 TT_OS2 *os2;
365 FT_ULong needed;
366 eblcHeader_t *eblc;
367 bitmapSizeTable_t *size_table;
368 int num_sizes;
369 struct fontinfo *info;
370 size_t data_pos;
372 if (FT_New_Face(ft_library, face_name, 0, &face)) error( "Cannot open face %s\n", face_name );
373 if (FT_Set_Pixel_Sizes(face, ppem, ppem)) error( "cannot set face size to %u\n", ppem );
375 cptable = wine_cp_get_table(enc);
376 if(!cptable)
377 error("Can't find codepage %d\n", enc);
379 if(cptable->info.char_size != 1) {
380 /* for double byte charsets we actually want to use cp1252 */
381 cptable = wine_cp_get_table(1252);
382 if(!cptable)
383 error("Can't find codepage 1252\n");
386 assert( face->size->metrics.y_ppem == ppem );
388 needed = 0;
389 if (FT_Load_Sfnt_Table(face, TTAG_EBLC, 0, NULL, &needed))
390 fprintf(stderr,"Can't find EBLC table\n");
391 else
393 eblc = malloc(needed);
394 FT_Load_Sfnt_Table(face, TTAG_EBLC, 0, (FT_Byte *)eblc, &needed);
396 num_sizes = GET_BE_DWORD(&eblc->numSizes);
398 size_table = (bitmapSizeTable_t *)(eblc + 1);
399 for(i = 0; i < num_sizes; i++)
401 if( (signed char)size_table->hori.ascender - (signed char)size_table->hori.descender == ppem)
403 ascent = size_table->hori.ascender;
404 break;
406 size_table++;
409 free(eblc);
412 /* Versions of fontforge prior to early 2006 have incorrect
413 ascender values in the eblc table, so we won't find the
414 correct bitmapSizeTable. In this case use the height of
415 the Aring glyph instead. */
416 if(ascent == 0)
418 if(FT_Load_Char(face, 0xc5, FT_LOAD_DEFAULT))
419 error("Can't find Aring\n");
420 ascent = face->glyph->metrics.horiBearingY >> 6;
423 start = sizeof(FNT_HEADER);
425 if(FT_Load_Char(face, 'M', FT_LOAD_DEFAULT))
426 error("Can't find M\n");
427 il = ascent - (face->glyph->metrics.height >> 6);
429 /* Hack: Courier has no internal leading, nor do any Chinese or Japanese fonts */
430 if(!strcmp(face->family_name, "Courier") || enc == 936 || enc == 950 || enc == 932)
431 il = 0;
432 /* Japanese system fonts have an external leading (not small font) */
433 if (enc == 932 && ppem > 11)
434 el = 2;
435 else
436 el = 0;
438 first_char = FT_Get_First_Char(face, &gi);
439 if(first_char == 0xd) /* fontforge's first glyph is 0xd, we'll catch this and skip it */
440 first_char = 32; /* FT_Get_Next_Char for some reason returns too high
441 number in this case */
443 info = calloc( 1, sizeof(*info) );
445 info->hdr.fi.dfFirstChar = first_char;
446 info->hdr.fi.dfLastChar = 0xff;
447 start += ((unsigned char)info->hdr.fi.dfLastChar - (unsigned char)info->hdr.fi.dfFirstChar + 3 ) * sizeof(*info->dfCharTable);
449 num_names = FT_Get_Sfnt_Name_Count(face);
450 for(i = 0; i <num_names; i++) {
451 FT_Get_Sfnt_Name(face, i, &sfntname);
452 if(sfntname.platform_id == 1 && sfntname.encoding_id == 0 &&
453 sfntname.language_id == 0 && sfntname.name_id == 0) {
454 size_t len = min( sfntname.string_len, sizeof(info->hdr.dfCopyright)-1 );
455 memcpy(info->hdr.dfCopyright, sfntname.string, len);
456 info->hdr.dfCopyright[len] = 0;
460 os2 = FT_Get_Sfnt_Table(face, ft_sfnt_os2);
461 for(i = first_char; i < 0x100; i++) {
462 int c = get_char(cptable, enc, i);
463 gi = FT_Get_Char_Index(face, c);
464 if(gi == 0 && !option_quiet)
465 fprintf(stderr, "warning: %s %u: missing glyph for char %04x\n",
466 face->family_name, ppem, cptable->sbcs.cp2uni[i]);
467 if(FT_Load_Char(face, c, FT_LOAD_DEFAULT)) {
468 fprintf(stderr, "error loading char %d - bad news!\n", i);
469 continue;
471 info->dfCharTable[i].width = face->glyph->metrics.horiAdvance >> 6;
472 info->dfCharTable[i].offset = start + (width_bytes * ppem);
473 width_bytes += ((face->glyph->metrics.horiAdvance >> 6) + 7) >> 3;
474 if(max_width < (face->glyph->metrics.horiAdvance >> 6))
475 max_width = face->glyph->metrics.horiAdvance >> 6;
477 /* space */
478 space_size = (ppem + 3) / 4;
479 info->dfCharTable[i].width = space_size;
480 info->dfCharTable[i].offset = start + (width_bytes * ppem);
481 width_bytes += (space_size + 7) >> 3;
482 /* sentinel */
483 info->dfCharTable[++i].width = 0;
484 info->dfCharTable[i].offset = start + (width_bytes * ppem);
486 info->hdr.fi.dfType = 0;
487 info->hdr.fi.dfPoints = ((ppem - il - el) * 72 + dpi/2) / dpi;
488 info->hdr.fi.dfVertRes = dpi;
489 info->hdr.fi.dfHorizRes = dpi;
490 info->hdr.fi.dfAscent = ascent;
491 info->hdr.fi.dfInternalLeading = il;
492 info->hdr.fi.dfExternalLeading = el;
493 info->hdr.fi.dfItalic = (face->style_flags & FT_STYLE_FLAG_ITALIC) ? 1 : 0;
494 info->hdr.fi.dfUnderline = 0;
495 info->hdr.fi.dfStrikeOut = 0;
496 info->hdr.fi.dfWeight = os2->usWeightClass;
497 info->hdr.fi.dfCharSet = lookup_charset(enc);
498 info->hdr.fi.dfPixWidth = (face->face_flags & FT_FACE_FLAG_FIXED_WIDTH) ? avg_width : 0;
499 info->hdr.fi.dfPixHeight = ppem;
500 info->hdr.fi.dfPitchAndFamily = FT_IS_FIXED_WIDTH(face) ? 0 : TMPF_FIXED_PITCH;
501 switch(os2->panose[PAN_FAMILYTYPE_INDEX]) {
502 case PAN_FAMILY_SCRIPT:
503 info->hdr.fi.dfPitchAndFamily |= FF_SCRIPT;
504 break;
505 case PAN_FAMILY_DECORATIVE:
506 case PAN_FAMILY_PICTORIAL:
507 info->hdr.fi.dfPitchAndFamily |= FF_DECORATIVE;
508 break;
509 case PAN_FAMILY_TEXT_DISPLAY:
510 if(info->hdr.fi.dfPitchAndFamily == 0) /* fixed */
511 info->hdr.fi.dfPitchAndFamily = FF_MODERN;
512 else {
513 switch(os2->panose[PAN_SERIFSTYLE_INDEX]) {
514 case PAN_SERIF_NORMAL_SANS:
515 case PAN_SERIF_OBTUSE_SANS:
516 case PAN_SERIF_PERP_SANS:
517 info->hdr.fi.dfPitchAndFamily |= FF_SWISS;
518 break;
519 default:
520 info->hdr.fi.dfPitchAndFamily |= FF_ROMAN;
523 break;
524 default:
525 info->hdr.fi.dfPitchAndFamily |= FF_DONTCARE;
528 info->hdr.fi.dfAvgWidth = avg_width;
529 info->hdr.fi.dfMaxWidth = max_width;
530 info->hdr.fi.dfDefaultChar = def_char - info->hdr.fi.dfFirstChar;
531 info->hdr.fi.dfBreakChar = ' ' - info->hdr.fi.dfFirstChar;
532 info->hdr.fi.dfWidthBytes = (width_bytes + 1) & ~1;
534 info->hdr.fi.dfFace = start + info->hdr.fi.dfWidthBytes * ppem;
535 info->hdr.fi.dfBitsOffset = start;
536 info->hdr.fi.dfFlags = 0x10; /* DFF_1COLOR */
537 info->hdr.fi.dfFlags |= FT_IS_FIXED_WIDTH(face) ? 1 : 2; /* DFF_FIXED : DFF_PROPORTIONAL */
539 info->hdr.dfVersion = 0x300;
540 info->hdr.dfSize = start + info->hdr.fi.dfWidthBytes * ppem + strlen(face->family_name) + 1;
542 info->data = calloc( info->hdr.dfSize - start, 1 );
543 data_pos = 0;
545 for(i = first_char; i < 0x100; i++) {
546 int c = get_char(cptable, enc, i);
547 if(FT_Load_Char(face, c, FT_LOAD_DEFAULT)) {
548 continue;
550 assert(info->dfCharTable[i].width == face->glyph->metrics.horiAdvance >> 6);
552 for(x = 0; x < ((info->dfCharTable[i].width + 7) / 8); x++) {
553 for(y = 0; y < ppem; y++) {
554 if(y < ascent - face->glyph->bitmap_top ||
555 y >= face->glyph->bitmap.rows + ascent - face->glyph->bitmap_top) {
556 info->data[data_pos++] = 0;
557 continue;
559 x_off = face->glyph->bitmap_left / 8;
560 x_end = (face->glyph->bitmap_left + face->glyph->bitmap.width - 1) / 8;
561 if(x < x_off || x > x_end) {
562 info->data[data_pos++] = 0;
563 continue;
565 if(x == x_off)
566 left_byte = 0;
567 else
568 left_byte = face->glyph->bitmap.buffer[(y - (ascent - face->glyph->bitmap_top)) * face->glyph->bitmap.pitch + x - x_off - 1];
570 /* On the last non-trivial output byte (x == x_end) have we got one or two input bytes */
571 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)))
572 right_byte = 0;
573 else
574 right_byte = face->glyph->bitmap.buffer[(y - (ascent - face->glyph->bitmap_top)) * face->glyph->bitmap.pitch + x - x_off];
576 byte = (left_byte << (8 - (face->glyph->bitmap_left & 7))) & 0xff;
577 byte |= ((right_byte >> (face->glyph->bitmap_left & 7)) & 0xff);
578 info->data[data_pos++] = byte;
582 data_pos += ((space_size + 7) / 8) * ppem;
583 if (width_bytes & 1) data_pos += ppem;
585 memcpy( info->data + data_pos, face->family_name, strlen( face->family_name ));
586 data_pos += strlen( face->family_name ) + 1;
587 assert( start + data_pos == info->hdr.dfSize );
589 FT_Done_Face( face );
590 return info;
593 static void adjust_fontinfo( FONTINFO16 * fi )
595 fi->dfType = PUT_LE_WORD(fi->dfType);
596 fi->dfPoints = PUT_LE_WORD(fi->dfPoints);
597 fi->dfVertRes = PUT_LE_WORD(fi->dfVertRes);
598 fi->dfHorizRes = PUT_LE_WORD(fi->dfHorizRes);
599 fi->dfAscent = PUT_LE_WORD(fi->dfAscent);
600 fi->dfInternalLeading = PUT_LE_WORD(fi->dfInternalLeading);
601 fi->dfExternalLeading = PUT_LE_WORD(fi->dfExternalLeading);
602 fi->dfWeight = PUT_LE_WORD(fi->dfWeight);
603 fi->dfPixWidth = PUT_LE_WORD(fi->dfPixWidth);
604 fi->dfPixHeight = PUT_LE_WORD(fi->dfPixHeight);
605 fi->dfAvgWidth = PUT_LE_WORD(fi->dfAvgWidth);
606 fi->dfMaxWidth = PUT_LE_WORD(fi->dfMaxWidth);
607 fi->dfWidthBytes = PUT_LE_WORD(fi->dfWidthBytes);
608 fi->dfAspace = PUT_LE_WORD(fi->dfAspace);
609 fi->dfBspace = PUT_LE_WORD(fi->dfBspace);
610 fi->dfCspace = PUT_LE_WORD(fi->dfCspace);
611 fi->dfDevice = PUT_LE_DWORD(fi->dfDevice);
612 fi->dfFace = PUT_LE_DWORD(fi->dfFace);
613 fi->dfBitsPointer = PUT_LE_DWORD(fi->dfBitsPointer);
614 fi->dfBitsOffset = PUT_LE_DWORD(fi->dfBitsOffset);
615 fi->dfFlags = PUT_LE_DWORD(fi->dfFlags);
616 fi->dfColorPointer = PUT_LE_DWORD(fi->dfColorPointer);
619 static void write_fontinfo( const struct fontinfo *info, FILE *fp )
621 FNT_HEADER tmp_hdr;
622 int num_chars, i;
623 CHAR_TABLE_ENTRY tmp_chartable[258];
624 memcpy(&tmp_hdr, &info->hdr, sizeof(info->hdr));
625 tmp_hdr.dfVersion = PUT_LE_WORD(tmp_hdr.dfVersion);
626 tmp_hdr.dfSize = PUT_LE_DWORD(tmp_hdr.dfSize);
627 adjust_fontinfo(&(tmp_hdr.fi));
628 fwrite( &tmp_hdr, sizeof(info->hdr), 1, fp );
629 num_chars = ((unsigned char)info->hdr.fi.dfLastChar - (unsigned char)info->hdr.fi.dfFirstChar) + 3;
631 memcpy(&tmp_chartable, info->dfCharTable + info->hdr.fi.dfFirstChar, num_chars * sizeof(CHAR_TABLE_ENTRY));
632 for (i=0; i < num_chars; ++i) {
633 tmp_chartable[i].width = PUT_LE_WORD(tmp_chartable[i].width);
634 tmp_chartable[i].offset = PUT_LE_DWORD(tmp_chartable[i].offset);
636 fwrite( tmp_chartable, sizeof(CHAR_TABLE_ENTRY), num_chars, fp );
637 fwrite( info->data, info->hdr.dfSize - info->hdr.fi.dfBitsOffset, 1, fp );
640 /* parse options from the argv array and remove all the recognized ones */
641 static char **parse_options( int argc, char **argv )
643 int optc;
645 while ((optc = getopt( argc, argv, "d:ho:qr:s" )) != -1)
647 switch(optc)
649 case 'd':
650 option_defchar = atoi( optarg );
651 break;
652 case 'o':
653 option_output = strdup( optarg );
654 break;
655 case 'q':
656 option_quiet = 1;
657 break;
658 case 'r':
659 option_dpi = atoi( optarg );
660 break;
661 case 's':
662 option_fnt_mode = 1;
663 break;
664 case 'h':
665 usage(argv);
666 exit(0);
667 case '?':
668 usage(argv);
669 exit(1);
672 return &argv[optind];
675 int main(int argc, char **argv)
677 int i, j;
678 FILE *ofp;
679 short align, num_files;
680 int resource_table_len, non_resident_name_len, resident_name_len;
681 unsigned short resource_table_off, resident_name_off, module_ref_off, non_resident_name_off, fontdir_off, font_off;
682 char resident_name[200];
683 int fontdir_len = 2;
684 char non_resident_name[200];
685 unsigned short first_res = 0x0050, pad, res;
686 IMAGE_OS2_HEADER NE_hdr;
687 NE_TYPEINFO rc_type;
688 NE_NAMEINFO rc_name;
689 struct fontinfo **info;
690 char *input_file;
691 char **args;
692 short tmp16;
694 args = parse_options( argc, argv );
696 input_file = *args++;
697 if (!input_file || !*args)
699 usage(argv);
700 exit(1);
703 if(FT_Init_FreeType(&ft_library))
704 error("ft init failure\n");
706 FT_Version.major=FT_Version.minor=FT_Version.patch=-1;
707 FT_Library_Version(ft_library,&FT_Version.major,&FT_Version.minor,&FT_Version.patch);
709 num_files = 0;
710 while (args[num_files]) num_files++;
712 if (option_fnt_mode && num_files > 1)
713 error( "can only specify one font in .fnt mode\n" );
715 info = malloc( num_files * sizeof(*info) );
716 for (i = 0; i < num_files; i++)
718 int ppem, enc, avg_width;
719 const char *name;
721 if (sscanf( args[i], "%d,%d,%d", &ppem, &enc, &avg_width ) != 3)
723 usage(argv);
724 exit(1);
726 if (!(info[i] = fill_fontinfo( input_file, ppem, enc, option_dpi, option_defchar, avg_width )))
727 exit(1);
729 name = get_face_name( info[i] );
730 fontdir_len += 0x74 + strlen(name) + 1;
731 if(i == 0) {
732 sprintf(non_resident_name, "FONTRES 100,%d,%d : %s %d",
733 info[i]->hdr.fi.dfVertRes, info[i]->hdr.fi.dfHorizRes,
734 name, info[i]->hdr.fi.dfPoints );
735 strcpy(resident_name, name);
736 } else {
737 sprintf(non_resident_name + strlen(non_resident_name), ",%d", info[i]->hdr.fi.dfPoints );
741 if (option_dpi <= 108)
742 strcat(non_resident_name, " (VGA res)");
743 else
744 strcat(non_resident_name, " (8514 res)");
745 non_resident_name_len = strlen(non_resident_name) + 4;
747 /* shift count + fontdir entry + num_files of font + nul type + \007FONTDIR */
748 resource_table_len = sizeof(align) + sizeof("FONTDIR") +
749 sizeof(NE_TYPEINFO) + sizeof(NE_NAMEINFO) +
750 sizeof(NE_TYPEINFO) + sizeof(NE_NAMEINFO) * num_files +
751 sizeof(NE_TYPEINFO);
752 resource_table_off = sizeof(NE_hdr);
753 resident_name_off = resource_table_off + resource_table_len;
754 resident_name_len = strlen(resident_name) + 4;
755 module_ref_off = resident_name_off + resident_name_len;
756 non_resident_name_off = sizeof(MZ_hdr) + module_ref_off + sizeof(align);
758 memset(&NE_hdr, 0, sizeof(NE_hdr));
759 NE_hdr.ne_magic = PUT_LE_WORD(0x454e);
760 NE_hdr.ne_ver = 5;
761 NE_hdr.ne_rev = 1;
762 NE_hdr.ne_flags = PUT_LE_WORD(NE_FFLAGS_LIBMODULE | NE_FFLAGS_GUI);
763 NE_hdr.ne_cbnrestab = PUT_LE_WORD(non_resident_name_len);
764 NE_hdr.ne_segtab = PUT_LE_WORD(sizeof(NE_hdr));
765 NE_hdr.ne_rsrctab = PUT_LE_WORD(sizeof(NE_hdr));
766 NE_hdr.ne_restab = PUT_LE_WORD(resident_name_off);
767 NE_hdr.ne_modtab = PUT_LE_WORD(module_ref_off);
768 NE_hdr.ne_imptab = PUT_LE_WORD(module_ref_off);
769 NE_hdr.ne_enttab = NE_hdr.ne_modtab;
770 NE_hdr.ne_nrestab = PUT_LE_DWORD(non_resident_name_off);
771 NE_hdr.ne_align = PUT_LE_WORD(4);
772 NE_hdr.ne_exetyp = NE_OSFLAGS_WINDOWS;
773 NE_hdr.ne_expver = PUT_LE_WORD(0x400);
775 fontdir_off = (non_resident_name_off + non_resident_name_len + 15) & ~0xf;
776 font_off = (fontdir_off + fontdir_len + 15) & ~0x0f;
778 atexit( cleanup );
779 signal( SIGTERM, exit_on_signal );
780 signal( SIGINT, exit_on_signal );
781 #ifdef SIGHUP
782 signal( SIGHUP, exit_on_signal );
783 #endif
785 if (!option_output) /* build a default output name */
787 char *p = strrchr( input_file, '/' );
788 if (p) p++;
789 else p = input_file;
790 option_output = malloc( strlen(p) + sizeof(".fon") );
791 strcpy( option_output, p );
792 p = strrchr( option_output, '.' );
793 if (!p) p = option_output + strlen(option_output);
794 strcpy( p, option_fnt_mode ? ".fnt" : ".fon" );
797 if (!(ofp = fopen(option_output, "wb")))
799 perror( option_output );
800 exit(1);
802 output_name = option_output;
803 if (option_fnt_mode)
805 write_fontinfo( info[0], ofp );
806 goto done;
809 fwrite(MZ_hdr, sizeof(MZ_hdr), 1, ofp);
810 fwrite(&NE_hdr, sizeof(NE_hdr), 1, ofp);
812 align = PUT_LE_WORD(4);
813 fwrite(&align, sizeof(align), 1, ofp);
815 rc_type.type_id = PUT_LE_WORD(NE_RSCTYPE_FONTDIR);
816 rc_type.count = PUT_LE_WORD(1);
817 rc_type.resloader = 0;
818 fwrite(&rc_type, sizeof(rc_type), 1, ofp);
820 rc_name.offset = PUT_LE_WORD(fontdir_off >> 4);
821 rc_name.length = PUT_LE_WORD((fontdir_len + 15) >> 4);
822 rc_name.flags = PUT_LE_WORD(NE_SEGFLAGS_MOVEABLE | NE_SEGFLAGS_PRELOAD);
823 rc_name.id = PUT_LE_WORD(resident_name_off - sizeof("FONTDIR") - sizeof(NE_hdr));
824 rc_name.handle = 0;
825 rc_name.usage = 0;
826 fwrite(&rc_name, sizeof(rc_name), 1, ofp);
828 rc_type.type_id = PUT_LE_WORD(NE_RSCTYPE_FONT);
829 rc_type.count = PUT_LE_WORD(num_files);
830 rc_type.resloader = 0;
831 fwrite(&rc_type, sizeof(rc_type), 1, ofp);
833 for(res = first_res | 0x8000, i = 0; i < num_files; i++, res++) {
834 int len = (info[i]->hdr.dfSize + 15) & ~0xf;
836 rc_name.offset = PUT_LE_WORD(font_off >> 4);
837 rc_name.length = PUT_LE_WORD(len >> 4);
838 rc_name.flags = PUT_LE_WORD(NE_SEGFLAGS_MOVEABLE | NE_SEGFLAGS_SHAREABLE | NE_SEGFLAGS_DISCARDABLE);
839 rc_name.id = PUT_LE_WORD(res);
840 rc_name.handle = 0;
841 rc_name.usage = 0;
842 fwrite(&rc_name, sizeof(rc_name), 1, ofp);
844 font_off += len;
847 /* empty type info */
848 memset(&rc_type, 0, sizeof(rc_type));
849 fwrite(&rc_type, sizeof(rc_type), 1, ofp);
851 fputc(strlen("FONTDIR"), ofp);
852 fwrite("FONTDIR", strlen("FONTDIR"), 1, ofp);
853 fputc(strlen(resident_name), ofp);
854 fwrite(resident_name, strlen(resident_name), 1, ofp);
856 fputc(0x00, ofp); fputc(0x00, ofp);
857 fputc(0x00, ofp);
858 fputc(0x00, ofp); fputc(0x00, ofp);
860 fputc(strlen(non_resident_name), ofp);
861 fwrite(non_resident_name, strlen(non_resident_name), 1, ofp);
862 fputc(0x00, ofp); /* terminator */
864 /* empty ne_modtab and ne_imptab */
865 fputc(0x00, ofp);
866 fputc(0x00, ofp);
868 pad = ftell(ofp) & 0xf;
869 if(pad != 0)
870 pad = 0x10 - pad;
871 for(i = 0; i < pad; i++)
872 fputc(0x00, ofp);
874 /* FONTDIR resource */
875 tmp16 = PUT_LE_WORD(num_files);
876 fwrite(&tmp16, sizeof(tmp16), 1, ofp);
878 for(res = first_res, i = 0; i < num_files; i++, res++) {
879 FNT_HEADER tmp_hdr;
880 int sz;
881 const char *name = get_face_name( info[i] );
882 tmp16 = PUT_LE_WORD(res);
883 fwrite(&tmp16, sizeof(tmp16), 1, ofp);
884 sz = FIELD_OFFSET(FNT_HEADER,fi.dfBitsOffset);
885 memcpy(&tmp_hdr, &info[i]->hdr, sz);
886 tmp_hdr.dfVersion = PUT_LE_WORD(tmp_hdr.dfVersion);
887 tmp_hdr.dfSize = PUT_LE_DWORD(tmp_hdr.dfSize);
888 adjust_fontinfo(&(tmp_hdr.fi));
889 fwrite(&tmp_hdr, FIELD_OFFSET(FNT_HEADER,fi.dfBitsOffset), 1, ofp);
890 fputc(0x00, ofp);
891 fwrite(name, strlen(name) + 1, 1, ofp);
894 pad = ftell(ofp) & 0xf;
895 if(pad != 0)
896 pad = 0x10 - pad;
897 for(i = 0; i < pad; i++)
898 fputc(0x00, ofp);
900 for(res = first_res, i = 0; i < num_files; i++, res++) {
901 write_fontinfo( info[i], ofp );
902 pad = info[i]->hdr.dfSize & 0xf;
903 if(pad != 0)
904 pad = 0x10 - pad;
905 for(j = 0; j < pad; j++)
906 fputc(0x00, ofp);
908 done:
909 fclose(ofp);
910 output_name = NULL;
911 exit(0);
914 #else /* HAVE_FREETYPE */
916 int main(int argc, char **argv)
918 fprintf( stderr, "%s needs to be built with FreeType support\n", argv[0] );
919 exit(1);
922 #endif /* HAVE_FREETYPE */