blob: d83d2beaf79c438c446ad49ecda943300cd0fae2 [file] [log] [blame]
Huw Davies00acb5f2004-08-17 22:33:14 +00001/*
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
Jonathan Ernst360a3f92006-05-18 14:49:52 +020018 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
Huw Davies00acb5f2004-08-17 22:33:14 +000019 */
20
21#include "config.h"
Alexandre Julliard49b7fdc2005-08-03 21:25:10 +000022#include "wine/port.h"
Huw Davies00acb5f2004-08-17 22:33:14 +000023
24#include <assert.h>
25#include <ctype.h>
Alexandre Julliardf5c49b22008-03-22 20:11:25 +010026#include <errno.h>
Alexandre Julliard4ca7a062006-05-27 13:24:36 +020027#include <signal.h>
Huw Davies00acb5f2004-08-17 22:33:14 +000028#include <stdio.h>
Gerald Pfeifer51c9f122004-08-22 02:01:21 +000029#include <stdlib.h>
Alexandre Julliardf2734a12008-03-22 21:00:56 +010030#ifdef HAVE_GETOPT_H
31# include <getopt.h>
32#endif
Huw Davies00acb5f2004-08-17 22:33:14 +000033
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
Huw Davies130a0e42006-03-21 17:08:56 +000042#include FT_TRUETYPE_TAGS_H
Aric Stewart8be27f62007-12-10 16:24:10 +090043#ifdef HAVE_FREETYPE_INTERNAL_SFNT_H
44#include <freetype/internal/sfnt.h>
45#endif
Huw Davies00acb5f2004-08-17 22:33:14 +000046
47#include "wine/unicode.h"
48#include "wingdi.h"
49
Dmitry Timoshkov069d1e82005-05-20 09:41:37 +000050#include "pshpack1.h"
Huw Davies00acb5f2004-08-17 22:33:14 +000051
52typedef struct
53{
Alexandre Julliard1f220db2009-08-29 21:06:19 +020054 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;
88
89typedef struct
90{
Huw Davies00acb5f2004-08-17 22:33:14 +000091 WORD dfVersion;
92 DWORD dfSize;
93 char dfCopyright[60];
Alexandre Julliarda145b302008-03-22 14:56:16 +010094 FONTINFO16 fi;
Huw Davies00acb5f2004-08-17 22:33:14 +000095} FNT_HEADER;
96
Alexandre Julliard1f220db2009-08-29 21:06:19 +020097typedef struct
98{
99 WORD offset;
100 WORD length;
101 WORD flags;
102 WORD id;
103 WORD handle;
104 WORD usage;
105} NE_NAMEINFO;
106
107typedef struct
108{
109 WORD type_id;
110 WORD count;
111 DWORD resloader;
112} NE_TYPEINFO;
113
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
124
125#define NE_OSFLAGS_WINDOWS 0x02
126
127#define NE_RSCTYPE_FONTDIR 0x8007
128#define NE_RSCTYPE_FONT 0x8008
129#define NE_RSCTYPE_SCALABLE_FONTPATH 0x80cc
130
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
144
Huw Davies00acb5f2004-08-17 22:33:14 +0000145typedef struct {
146 WORD width;
147 DWORD offset;
148} CHAR_TABLE_ENTRY;
149
Huw Davies130a0e42006-03-21 17:08:56 +0000150typedef struct {
151 DWORD version;
152 ULONG numSizes;
153} eblcHeader_t;
154
155typedef 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;
169
170typedef 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;
184
Aric Stewart8be27f62007-12-10 16:24:10 +0900185typedef struct
186{
187 FT_Int major;
188 FT_Int minor;
189 FT_Int patch;
190} FT_Version_t;
191static FT_Version_t FT_Version;
192
Huw Davies130a0e42006-03-21 17:08:56 +0000193#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
Dmitry Timoshkov069d1e82005-05-20 09:41:37 +0000197#include "poppack.h"
Huw Davies00acb5f2004-08-17 22:33:14 +0000198
Alexandre Julliarda145b302008-03-22 14:56:16 +0100199struct fontinfo
200{
201 FNT_HEADER hdr;
202 CHAR_TABLE_ENTRY dfCharTable[258];
203 BYTE *data;
204};
205
Alexandre Julliardf5c49b22008-03-22 20:11:25 +0100206static const BYTE MZ_hdr[] =
207{
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
216};
217
Alexandre Julliardf2734a12008-03-22 21:00:56 +0100218static char *option_output;
219static int option_defchar = ' ';
220static int option_dpi = 96;
221static int option_fnt_mode = 0;
222static int option_quiet = 0;
223
Alexandre Julliardef4440a2006-03-21 16:18:45 +0100224static const char *output_name;
225
Alexandre Julliarda145b302008-03-22 14:56:16 +0100226static FT_Library ft_library;
227
Stefan Huehnere5462382005-06-15 18:10:39 +0000228static void usage(char **argv)
Huw Davies00acb5f2004-08-17 22:33:14 +0000229{
Alexandre Julliardf2734a12008-03-22 21:00:56 +0100230 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" );
Huw Davies00acb5f2004-08-17 22:33:14 +0000238}
239
Vincent Béron2e5da5f2005-12-07 12:44:17 +0100240#ifndef __GNUC__
241#define __attribute__(X)
242#endif
243
Alexandre Julliardef4440a2006-03-21 16:18:45 +0100244/* atexit handler to cleanup files */
245static void cleanup(void)
246{
247 if (output_name) unlink( output_name );
248}
249
Alexandre Julliard4ca7a062006-05-27 13:24:36 +0200250static void exit_on_signal( int sig )
251{
252 exit(1); /* this will call the atexit functions */
253}
254
Vincent Béron2e5da5f2005-12-07 12:44:17 +0100255static void error(const char *s, ...) __attribute__((format (printf, 1, 2)));
256
257static void error(const char *s, ...)
258{
259 va_list ap;
260 va_start(ap, s);
261 fprintf(stderr, "Error: ");
262 vfprintf(stderr, s, ap);
Vincent Béron2e5da5f2005-12-07 12:44:17 +0100263 va_end(ap);
264 exit(1);
265}
266
Alexandre Julliarda145b302008-03-22 14:56:16 +0100267static const char *get_face_name( const struct fontinfo *info )
268{
269 return (const char *)info->data + info->hdr.fi.dfFace - info->hdr.fi.dfBitsOffset;
270}
271
Stefan Huehnere5462382005-06-15 18:10:39 +0000272static int lookup_charset(int enc)
Huw Davies00acb5f2004-08-17 22:33:14 +0000273{
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;
320 }
321 fprintf(stderr, "Unknown encoding %d - using OEM_CHARSET\n", enc);
322
323 return OEM_CHARSET;
324}
325
Huw Davies26dbcc02006-04-07 10:28:30 +0100326static int get_char(const union cptable *cptable, int enc, int index)
327{
328 /* Korean has the Won sign in place of '\\' */
329 if(enc == 949 && index == '\\')
330 return 0x20a9;
331
332 return cptable->sbcs.cp2uni[index];
333}
334
Aric Stewart8be27f62007-12-10 16:24:10 +0900335/* from gdi32/freetype.c */
336static FT_Error load_sfnt_table(FT_Face ft_face, FT_ULong table, FT_Long offset, FT_Byte *buf, FT_ULong *len)
337{
338
339 FT_Error err;
340
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)
348 {
349 /* 2.0.x */
350 sfnt = *(SFNT_Interface**)((char*)tt_face + 528);
351 }
352 else
353 {
354 /* A field was added in the middle of the structure in 2.1.x */
355 sfnt = *(SFNT_Interface**)((char*)tt_face + 532);
356 }
357 err = sfnt->load_any(tt_face, table, offset, buf, len);
358#else
359 err = FT_Err_Unimplemented_Feature;
360#endif
361 return err;
362}
363
Alexandre Julliarda145b302008-03-22 14:56:16 +0100364static struct fontinfo *fill_fontinfo( const char *face_name, int ppem, int enc, int dpi,
365 unsigned char def_char, int avg_width )
Huw Davies00acb5f2004-08-17 22:33:14 +0000366{
Alexandre Julliarda145b302008-03-22 14:56:16 +0100367 FT_Face face;
Gerald Pfeifer01511752010-04-24 12:32:44 +0200368 int ascent = 0, il, el, width_bytes = 0, space_size, max_width = 0;
Huw Davies00acb5f2004-08-17 22:33:14 +0000369 BYTE left_byte, right_byte, byte;
370 DWORD start;
Huw Davies00acb5f2004-08-17 22:33:14 +0000371 int i, x, y, x_off, x_end, first_char;
Mike McCormack723ee0a2005-07-05 14:26:54 +0000372 FT_UInt gi;
Huw Davies00acb5f2004-08-17 22:33:14 +0000373 int num_names;
374 const union cptable *cptable;
375 FT_SfntName sfntname;
Huw Davies00acb5f2004-08-17 22:33:14 +0000376 TT_OS2 *os2;
Huw Davies130a0e42006-03-21 17:08:56 +0000377 FT_ULong needed;
378 eblcHeader_t *eblc;
379 bitmapSizeTable_t *size_table;
380 int num_sizes;
Alexandre Julliarda145b302008-03-22 14:56:16 +0100381 struct fontinfo *info;
382 size_t data_pos;
383
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 );
Huw Davies130a0e42006-03-21 17:08:56 +0000386
Huw Davies00acb5f2004-08-17 22:33:14 +0000387 cptable = wine_cp_get_table(enc);
Vincent Béron2e5da5f2005-12-07 12:44:17 +0100388 if(!cptable)
389 error("Can't find codepage %d\n", enc);
Huw Davies84ae4d82005-08-19 09:57:47 +0000390
Huw Davies00acb5f2004-08-17 22:33:14 +0000391 if(cptable->info.char_size != 1) {
Huw Davies84ae4d82005-08-19 09:57:47 +0000392 /* for double byte charsets we actually want to use cp1252 */
393 cptable = wine_cp_get_table(1252);
Vincent Béron2e5da5f2005-12-07 12:44:17 +0100394 if(!cptable)
395 error("Can't find codepage 1252\n");
Huw Davies00acb5f2004-08-17 22:33:14 +0000396 }
397
Alexandre Julliarda145b302008-03-22 14:56:16 +0100398 assert( face->size->metrics.y_ppem == ppem );
Huw Davies130a0e42006-03-21 17:08:56 +0000399
400 needed = 0;
Aric Stewart8be27f62007-12-10 16:24:10 +0900401 if (load_sfnt_table(face, TTAG_EBLC, 0, NULL, &needed))
402 fprintf(stderr,"Can't find EBLC table\n");
403 else
Huw Davies130a0e42006-03-21 17:08:56 +0000404 {
Aric Stewart8be27f62007-12-10 16:24:10 +0900405 eblc = malloc(needed);
406 load_sfnt_table(face, TTAG_EBLC, 0, (FT_Byte *)eblc, &needed);
Huw Davies130a0e42006-03-21 17:08:56 +0000407
Aric Stewart8be27f62007-12-10 16:24:10 +0900408 num_sizes = GET_BE_DWORD(&eblc->numSizes);
409
410 size_table = (bitmapSizeTable_t *)(eblc + 1);
411 for(i = 0; i < num_sizes; i++)
412 {
413 if(size_table->hori.ascender - size_table->hori.descender == ppem)
414 {
415 ascent = size_table->hori.ascender;
Aric Stewart8be27f62007-12-10 16:24:10 +0900416 break;
417 }
418 size_table++;
419 }
420
421 free(eblc);
422 }
Huw Davies35bca772006-04-12 12:14:27 +0100423
Huw Davies130a0e42006-03-21 17:08:56 +0000424 /* 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)
429 {
430 if(FT_Load_Char(face, 0xc5, FT_LOAD_DEFAULT))
431 error("Can't find Aring\n");
432 ascent = face->glyph->metrics.horiBearingY >> 6;
Huw Davies130a0e42006-03-21 17:08:56 +0000433 }
434
Alexandre Julliarda145b302008-03-22 14:56:16 +0100435 start = sizeof(FNT_HEADER);
Huw Davies00acb5f2004-08-17 22:33:14 +0000436
Vincent Béron2e5da5f2005-12-07 12:44:17 +0100437 if(FT_Load_Char(face, 'M', FT_LOAD_DEFAULT))
438 error("Can't find M\n");
Huw Davies00acb5f2004-08-17 22:33:14 +0000439 il = ascent - (face->glyph->metrics.height >> 6);
440
Aric Stewart8be27f62007-12-10 16:24:10 +0900441 /* 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)
Huw Davies00acb5f2004-08-17 22:33:14 +0000443 il = 0;
Aric Stewarta8e62522007-12-26 08:15:06 -0600444 /* Japanese system fonts have an external leading (not small font) */
445 if (enc == 932 && ppem > 11)
Aric Stewart8be27f62007-12-10 16:24:10 +0900446 el = 2;
447 else
448 el = 0;
Huw Davies00acb5f2004-08-17 22:33:14 +0000449
Huw Davies00acb5f2004-08-17 22:33:14 +0000450 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 */
Dmitry Timoshkov069d1e82005-05-20 09:41:37 +0000452 first_char = 32; /* FT_Get_Next_Char for some reason returns too high
453 number in this case */
Huw Davies00acb5f2004-08-17 22:33:14 +0000454
Alexandre Julliarda145b302008-03-22 14:56:16 +0100455 info = calloc( 1, sizeof(*info) );
Dmitry Timoshkov069d1e82005-05-20 09:41:37 +0000456
Alexandre Julliarda145b302008-03-22 14:56:16 +0100457 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);
Huw Davies00acb5f2004-08-17 22:33:14 +0000460
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);
Peter Berg Larsen6e3bcb52005-04-18 10:30:55 +0000464 if(sfntname.platform_id == 1 && sfntname.encoding_id == 0 &&
465 sfntname.language_id == 0 && sfntname.name_id == 0) {
Alexandre Julliarda145b302008-03-22 14:56:16 +0100466 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;
Alexandre Julliard240d4ee2005-04-25 15:51:45 +0000469 }
Huw Davies00acb5f2004-08-17 22:33:14 +0000470 }
471
472 os2 = FT_Get_Sfnt_Table(face, ft_sfnt_os2);
473 for(i = first_char; i < 0x100; i++) {
Huw Davies26dbcc02006-04-07 10:28:30 +0100474 int c = get_char(cptable, enc, i);
475 gi = FT_Get_Char_Index(face, c);
Alexandre Julliardf2734a12008-03-22 21:00:56 +0100476 if(gi == 0 && !option_quiet)
Alexandre Julliarda145b302008-03-22 14:56:16 +0100477 fprintf(stderr, "warning: %s %u: missing glyph for char %04x\n",
478 face->family_name, ppem, cptable->sbcs.cp2uni[i]);
Huw Davies26dbcc02006-04-07 10:28:30 +0100479 if(FT_Load_Char(face, c, FT_LOAD_DEFAULT)) {
Huw Davies00acb5f2004-08-17 22:33:14 +0000480 fprintf(stderr, "error loading char %d - bad news!\n", i);
481 continue;
482 }
Alexandre Julliarda145b302008-03-22 14:56:16 +0100483 info->dfCharTable[i].width = face->glyph->metrics.horiAdvance >> 6;
484 info->dfCharTable[i].offset = start + (width_bytes * ppem);
Huw Davies00acb5f2004-08-17 22:33:14 +0000485 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;
488 }
489 /* space */
490 space_size = (ppem + 3) / 4;
Alexandre Julliarda145b302008-03-22 14:56:16 +0100491 info->dfCharTable[i].width = space_size;
492 info->dfCharTable[i].offset = start + (width_bytes * ppem);
Huw Davies00acb5f2004-08-17 22:33:14 +0000493 width_bytes += (space_size + 7) >> 3;
494 /* sentinel */
Alexandre Julliarda145b302008-03-22 14:56:16 +0100495 info->dfCharTable[++i].width = 0;
496 info->dfCharTable[i].offset = start + (width_bytes * ppem);
Huw Davies00acb5f2004-08-17 22:33:14 +0000497
Alexandre Julliarda145b302008-03-22 14:56:16 +0100498 info->hdr.fi.dfType = 0;
Dmitry Timoshkov65dbecb2009-05-11 20:56:02 +0900499 info->hdr.fi.dfPoints = ((ppem - il - el) * 72 + dpi/2) / dpi;
Alexandre Julliarda145b302008-03-22 14:56:16 +0100500 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;
Huw Davies00acb5f2004-08-17 22:33:14 +0000513 switch(os2->panose[PAN_FAMILYTYPE_INDEX]) {
514 case PAN_FAMILY_SCRIPT:
Alexandre Julliarda145b302008-03-22 14:56:16 +0100515 info->hdr.fi.dfPitchAndFamily |= FF_SCRIPT;
Huw Davies00acb5f2004-08-17 22:33:14 +0000516 break;
517 case PAN_FAMILY_DECORATIVE:
518 case PAN_FAMILY_PICTORIAL:
Alexandre Julliarda145b302008-03-22 14:56:16 +0100519 info->hdr.fi.dfPitchAndFamily |= FF_DECORATIVE;
Huw Davies00acb5f2004-08-17 22:33:14 +0000520 break;
521 case PAN_FAMILY_TEXT_DISPLAY:
Alexandre Julliarda145b302008-03-22 14:56:16 +0100522 if(info->hdr.fi.dfPitchAndFamily == 0) /* fixed */
523 info->hdr.fi.dfPitchAndFamily = FF_MODERN;
Huw Davies00acb5f2004-08-17 22:33:14 +0000524 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:
Alexandre Julliarda145b302008-03-22 14:56:16 +0100529 info->hdr.fi.dfPitchAndFamily |= FF_SWISS;
Huw Davies00acb5f2004-08-17 22:33:14 +0000530 break;
531 default:
Alexandre Julliarda145b302008-03-22 14:56:16 +0100532 info->hdr.fi.dfPitchAndFamily |= FF_ROMAN;
Huw Davies00acb5f2004-08-17 22:33:14 +0000533 }
534 }
535 break;
536 default:
Alexandre Julliarda145b302008-03-22 14:56:16 +0100537 info->hdr.fi.dfPitchAndFamily |= FF_DONTCARE;
Huw Davies00acb5f2004-08-17 22:33:14 +0000538 }
539
Alexandre Julliarda145b302008-03-22 14:56:16 +0100540 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;
Huw Davies00acb5f2004-08-17 22:33:14 +0000545
Alexandre Julliarda145b302008-03-22 14:56:16 +0100546 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 */
Huw Davies00acb5f2004-08-17 22:33:14 +0000550
Alexandre Julliarda145b302008-03-22 14:56:16 +0100551 info->hdr.dfVersion = 0x300;
552 info->hdr.dfSize = start + info->hdr.fi.dfWidthBytes * ppem + strlen(face->family_name) + 1;
553
554 info->data = calloc( info->hdr.dfSize - start, 1 );
555 data_pos = 0;
Huw Davies00acb5f2004-08-17 22:33:14 +0000556
557 for(i = first_char; i < 0x100; i++) {
Huw Davies26dbcc02006-04-07 10:28:30 +0100558 int c = get_char(cptable, enc, i);
559 if(FT_Load_Char(face, c, FT_LOAD_DEFAULT)) {
Huw Davies00acb5f2004-08-17 22:33:14 +0000560 continue;
561 }
Alexandre Julliarda145b302008-03-22 14:56:16 +0100562 assert(info->dfCharTable[i].width == face->glyph->metrics.horiAdvance >> 6);
Huw Davies00acb5f2004-08-17 22:33:14 +0000563
Alexandre Julliarda145b302008-03-22 14:56:16 +0100564 for(x = 0; x < ((info->dfCharTable[i].width + 7) / 8); x++) {
Huw Davies00acb5f2004-08-17 22:33:14 +0000565 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) {
Alexandre Julliarda145b302008-03-22 14:56:16 +0100568 info->data[data_pos++] = 0;
Huw Davies00acb5f2004-08-17 22:33:14 +0000569 continue;
570 }
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) {
Alexandre Julliarda145b302008-03-22 14:56:16 +0100574 info->data[data_pos++] = 0;
Huw Davies00acb5f2004-08-17 22:33:14 +0000575 continue;
576 }
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];
581
Frédéric Delanoy701aabb2011-07-28 21:51:20 +0200582 /* On the last non-trivial output byte (x == x_end) have we got one or two input bytes */
Huw Davies00acb5f2004-08-17 22:33:14 +0000583 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];
587
588 byte = (left_byte << (8 - (face->glyph->bitmap_left & 7))) & 0xff;
589 byte |= ((right_byte >> (face->glyph->bitmap_left & 7)) & 0xff);
Alexandre Julliarda145b302008-03-22 14:56:16 +0100590 info->data[data_pos++] = byte;
Huw Davies00acb5f2004-08-17 22:33:14 +0000591 }
592 }
593 }
Alexandre Julliarda145b302008-03-22 14:56:16 +0100594 data_pos += ((space_size + 7) / 8) * ppem;
595 if (width_bytes & 1) data_pos += ppem;
Huw Davies00acb5f2004-08-17 22:33:14 +0000596
Alexandre Julliarda145b302008-03-22 14:56:16 +0100597 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 );
Huw Davies00acb5f2004-08-17 22:33:14 +0000600
Alexandre Julliarda145b302008-03-22 14:56:16 +0100601 FT_Done_Face( face );
602 return info;
Huw Davies00acb5f2004-08-17 22:33:14 +0000603}
604
Alexandre Julliarda145b302008-03-22 14:56:16 +0100605static void write_fontinfo( const struct fontinfo *info, FILE *fp )
606{
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 );
611}
Huw Davies00acb5f2004-08-17 22:33:14 +0000612
Alexandre Julliardf2734a12008-03-22 21:00:56 +0100613/* parse options from the argv array and remove all the recognized ones */
614static char **parse_options( int argc, char **argv )
615{
616 int optc;
617
618 while ((optc = getopt( argc, argv, "d:ho:qr:s" )) != -1)
619 {
620 switch(optc)
621 {
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);
643 }
644 }
645 return &argv[optind];
646}
647
Huw Davies00acb5f2004-08-17 22:33:14 +0000648int main(int argc, char **argv)
649{
Alexandre Julliardf5c49b22008-03-22 20:11:25 +0100650 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;
Alexandre Julliardf2734a12008-03-22 21:00:56 +0100663 char *input_file;
664 char **args;
Alexandre Julliarda145b302008-03-22 14:56:16 +0100665
Alexandre Julliardf2734a12008-03-22 21:00:56 +0100666 args = parse_options( argc, argv );
667
668 input_file = *args++;
669 if (!input_file || !*args)
670 {
Huw Davies00acb5f2004-08-17 22:33:14 +0000671 usage(argv);
Alexandre Julliardf5c49b22008-03-22 20:11:25 +0100672 exit(1);
Huw Davies00acb5f2004-08-17 22:33:14 +0000673 }
674
Alexandre Julliarda145b302008-03-22 14:56:16 +0100675 if(FT_Init_FreeType(&ft_library))
Vincent Béron2e5da5f2005-12-07 12:44:17 +0100676 error("ft init failure\n");
Huw Davies00acb5f2004-08-17 22:33:14 +0000677
Aric Stewart8be27f62007-12-10 16:24:10 +0900678 FT_Version.major=FT_Version.minor=FT_Version.patch=-1;
Alexandre Julliarda145b302008-03-22 14:56:16 +0100679 FT_Library_Version(ft_library,&FT_Version.major,&FT_Version.minor,&FT_Version.patch);
Aric Stewart8be27f62007-12-10 16:24:10 +0900680
Alexandre Julliardf2734a12008-03-22 21:00:56 +0100681 num_files = 0;
682 while (args[num_files]) num_files++;
683
684 if (option_fnt_mode && num_files > 1)
685 error( "can only specify one font in .fnt mode\n" );
686
Alexandre Julliardf5c49b22008-03-22 20:11:25 +0100687 info = malloc( num_files * sizeof(*info) );
688 for (i = 0; i < num_files; i++)
Huw Davies00acb5f2004-08-17 22:33:14 +0000689 {
Alexandre Julliardf2734a12008-03-22 21:00:56 +0100690 int ppem, enc, avg_width;
Alexandre Julliardf5c49b22008-03-22 20:11:25 +0100691 const char *name;
692
Alexandre Julliardf2734a12008-03-22 21:00:56 +0100693 if (sscanf( args[i], "%d,%d,%d", &ppem, &enc, &avg_width ) != 3)
Alexandre Julliardf5c49b22008-03-22 20:11:25 +0100694 {
695 usage(argv);
696 exit(1);
697 }
Alexandre Julliardf2734a12008-03-22 21:00:56 +0100698 if (!(info[i] = fill_fontinfo( input_file, ppem, enc, option_dpi, option_defchar, avg_width )))
699 exit(1);
Alexandre Julliardf5c49b22008-03-22 20:11:25 +0100700
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 );
710 }
Huw Davies00acb5f2004-08-17 22:33:14 +0000711 }
712
Alexandre Julliardf2734a12008-03-22 21:00:56 +0100713 if (option_dpi <= 108)
Alexandre Julliardf5c49b22008-03-22 20:11:25 +0100714 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;
718
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);
729
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;
746
747 fontdir_off = (non_resident_name_off + non_resident_name_len + 15) & ~0xf;
748 font_off = (fontdir_off + fontdir_len + 15) & ~0x0f;
Huw Davies00acb5f2004-08-17 22:33:14 +0000749
Alexandre Julliardef4440a2006-03-21 16:18:45 +0100750 atexit( cleanup );
Alexandre Julliard4ca7a062006-05-27 13:24:36 +0200751 signal( SIGTERM, exit_on_signal );
752 signal( SIGINT, exit_on_signal );
753#ifdef SIGHUP
754 signal( SIGHUP, exit_on_signal );
755#endif
756
Alexandre Julliardf2734a12008-03-22 21:00:56 +0100757 if (!option_output) /* build a default output name */
Alexandre Julliarda145b302008-03-22 14:56:16 +0100758 {
Alexandre Julliardf2734a12008-03-22 21:00:56 +0100759 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" );
Alexandre Julliardf5c49b22008-03-22 20:11:25 +0100767 }
768
Alexandre Julliardf2734a12008-03-22 21:00:56 +0100769 if (!(ofp = fopen(option_output, "wb")))
Alexandre Julliardf5c49b22008-03-22 20:11:25 +0100770 {
Alexandre Julliardf2734a12008-03-22 21:00:56 +0100771 perror( option_output );
Alexandre Julliarda145b302008-03-22 14:56:16 +0100772 exit(1);
773 }
Alexandre Julliardf2734a12008-03-22 21:00:56 +0100774 output_name = option_output;
775 if (option_fnt_mode)
776 {
777 write_fontinfo( info[0], ofp );
778 goto done;
779 }
Alexandre Julliardf5c49b22008-03-22 20:11:25 +0100780
781 fwrite(MZ_hdr, sizeof(MZ_hdr), 1, ofp);
782 fwrite(&NE_hdr, sizeof(NE_hdr), 1, ofp);
783
784 align = 4;
785 fwrite(&align, sizeof(align), 1, ofp);
786
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);
791
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);
799
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);
804
805 for(res = first_res | 0x8000, i = 0; i < num_files; i++, res++) {
806 int len = (info[i]->hdr.dfSize + 15) & ~0xf;
807
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);
815
816 font_off += len;
817 }
818
819 /* empty type info */
820 memset(&rc_type, 0, sizeof(rc_type));
821 fwrite(&rc_type, sizeof(rc_type), 1, ofp);
822
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);
827
828 fputc(0x00, ofp); fputc(0x00, ofp);
829 fputc(0x00, ofp);
830 fputc(0x00, ofp); fputc(0x00, ofp);
831
832 fputc(strlen(non_resident_name), ofp);
833 fwrite(non_resident_name, strlen(non_resident_name), 1, ofp);
834 fputc(0x00, ofp); /* terminator */
835
836 /* empty ne_modtab and ne_imptab */
837 fputc(0x00, ofp);
838 fputc(0x00, ofp);
839
840 pad = ftell(ofp) & 0xf;
841 if(pad != 0)
842 pad = 0x10 - pad;
843 for(i = 0; i < pad; i++)
844 fputc(0x00, ofp);
845
846 /* FONTDIR resource */
847 fwrite(&num_files, sizeof(num_files), 1, ofp);
848
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);
855 }
856
857 pad = ftell(ofp) & 0xf;
858 if(pad != 0)
859 pad = 0x10 - pad;
860 for(i = 0; i < pad; i++)
861 fputc(0x00, ofp);
862
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);
870 }
Alexandre Julliardf2734a12008-03-22 21:00:56 +0100871done:
Alexandre Julliardf5c49b22008-03-22 20:11:25 +0100872 fclose(ofp);
Alexandre Julliardef4440a2006-03-21 16:18:45 +0100873 output_name = NULL;
Huw Davies00acb5f2004-08-17 22:33:14 +0000874 exit(0);
875}
876
877#else /* HAVE_FREETYPE */
878
879int main(int argc, char **argv)
880{
Francois Gougetf4c05242005-11-10 11:39:07 +0000881 fprintf( stderr, "%s needs to be built with FreeType support\n", argv[0] );
Huw Davies00acb5f2004-08-17 22:33:14 +0000882 exit(1);
883}
884
885#endif /* HAVE_FREETYPE */