msi: Constify some variables.
[wine/multimedia.git] / tools / sfnt2fnt.c
blob955f61a21454a1f7c3f0955ebeef7141edb9ea46
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 <signal.h>
27 #include <stdio.h>
28 #include <stdlib.h>
30 #ifdef HAVE_FREETYPE
32 #ifdef HAVE_FT2BUILD_H
33 #include <ft2build.h>
34 #endif
35 #include FT_FREETYPE_H
36 #include FT_SFNT_NAMES_H
37 #include FT_TRUETYPE_TABLES_H
38 #include FT_TRUETYPE_TAGS_H
40 #include "wine/unicode.h"
41 #include "wine/wingdi16.h"
42 #include "wingdi.h"
44 #include "pshpack1.h"
46 typedef struct
48 WORD dfVersion;
49 DWORD dfSize;
50 char dfCopyright[60];
51 } FNT_HEADER;
53 typedef struct {
54 WORD width;
55 DWORD offset;
56 } CHAR_TABLE_ENTRY;
58 typedef struct {
59 DWORD version;
60 ULONG numSizes;
61 } eblcHeader_t;
63 typedef struct {
64 CHAR ascender;
65 CHAR descender;
66 BYTE widthMax;
67 CHAR caretSlopeNumerator;
68 CHAR caretSlopeDenominator;
69 CHAR caretOffset;
70 CHAR minOriginSB;
71 CHAR minAdvanceSB;
72 CHAR maxBeforeBL;
73 CHAR maxAfterBL;
74 CHAR pad1;
75 CHAR pad2;
76 } sbitLineMetrics_t;
78 typedef struct {
79 ULONG indexSubTableArrayOffset;
80 ULONG indexTableSize;
81 ULONG numberOfIndexSubTables;
82 ULONG colorRef;
83 sbitLineMetrics_t hori;
84 sbitLineMetrics_t vert;
85 USHORT startGlyphIndex;
86 USHORT endGlyphIndex;
87 BYTE ppemX;
88 BYTE ppemY;
89 BYTE bitDepth;
90 CHAR flags;
91 } bitmapSizeTable_t;
93 #define GET_BE_WORD(ptr) MAKEWORD( ((BYTE *)(ptr))[1], ((BYTE *)(ptr))[0] )
94 #define GET_BE_DWORD(ptr) ((DWORD)MAKELONG( GET_BE_WORD(&((WORD *)(ptr))[1]), \
95 GET_BE_WORD(&((WORD *)(ptr))[0]) ))
97 #include "poppack.h"
99 static const char *output_name;
101 static void usage(char **argv)
103 fprintf(stderr, "%s foo.ttf ppem enc dpi def_char avg_width\n", argv[0]);
104 return;
107 #ifndef __GNUC__
108 #define __attribute__(X)
109 #endif
111 /* atexit handler to cleanup files */
112 static void cleanup(void)
114 if (output_name) unlink( output_name );
117 static void exit_on_signal( int sig )
119 exit(1); /* this will call the atexit functions */
122 static void error(const char *s, ...) __attribute__((format (printf, 1, 2)));
124 static void error(const char *s, ...)
126 va_list ap;
127 va_start(ap, s);
128 fprintf(stderr, "Error: ");
129 vfprintf(stderr, s, ap);
130 va_end(ap);
131 exit(1);
134 static int lookup_charset(int enc)
136 /* FIXME: make winelib app and use TranslateCharsetInfo */
137 switch(enc) {
138 case 1250:
139 return EE_CHARSET;
140 case 1251:
141 return RUSSIAN_CHARSET;
142 case 1252:
143 return ANSI_CHARSET;
144 case 1253:
145 return GREEK_CHARSET;
146 case 1254:
147 return TURKISH_CHARSET;
148 case 1255:
149 return HEBREW_CHARSET;
150 case 1256:
151 return ARABIC_CHARSET;
152 case 1257:
153 return BALTIC_CHARSET;
154 case 1258:
155 return VIETNAMESE_CHARSET;
156 case 437:
157 case 737:
158 case 775:
159 case 850:
160 case 852:
161 case 855:
162 case 857:
163 case 860:
164 case 861:
165 case 862:
166 case 863:
167 case 864:
168 case 865:
169 case 866:
170 case 869:
171 return OEM_CHARSET;
172 case 874:
173 return THAI_CHARSET;
174 case 932:
175 return SHIFTJIS_CHARSET;
176 case 936:
177 return GB2312_CHARSET;
178 case 949:
179 return HANGUL_CHARSET;
180 case 950:
181 return CHINESEBIG5_CHARSET;
183 fprintf(stderr, "Unknown encoding %d - using OEM_CHARSET\n", enc);
185 return OEM_CHARSET;
188 static int get_char(const union cptable *cptable, int enc, int index)
190 /* Korean has the Won sign in place of '\\' */
191 if(enc == 949 && index == '\\')
192 return 0x20a9;
194 return cptable->sbcs.cp2uni[index];
197 static void fill_fontinfo(FT_Face face, int enc, FILE *fp, int dpi, unsigned char def_char, int avg_width)
199 int ascent = 0, il, ppem, descent = 0, width_bytes = 0, space_size, max_width = 0;
200 FNT_HEADER hdr;
201 FONTINFO16 fi;
202 BYTE left_byte, right_byte, byte;
203 DWORD start;
204 CHAR_TABLE_ENTRY *dfCharTable;
205 int i, x, y, x_off, x_end, first_char;
206 FT_UInt gi;
207 int num_names;
208 const union cptable *cptable;
209 FT_SfntName sfntname;
210 TT_OS2 *os2;
212 #ifdef HAVE_FT_LOAD_SFNT_TABLE
213 FT_ULong needed;
214 eblcHeader_t *eblc;
215 bitmapSizeTable_t *size_table;
216 int num_sizes;
217 #endif
219 cptable = wine_cp_get_table(enc);
220 if(!cptable)
221 error("Can't find codepage %d\n", enc);
223 if(cptable->info.char_size != 1) {
224 /* for double byte charsets we actually want to use cp1252 */
225 cptable = wine_cp_get_table(1252);
226 if(!cptable)
227 error("Can't find codepage 1252\n");
230 ppem = face->size->metrics.y_ppem;
232 #ifdef HAVE_FT_LOAD_SFNT_TABLE
233 needed = 0;
234 if(FT_Load_Sfnt_Table(face, TTAG_EBLC, 0, NULL, &needed))
235 error("Can't find EBLC table\n");
237 eblc = malloc(needed);
238 FT_Load_Sfnt_Table(face, TTAG_EBLC, 0, (FT_Byte *)eblc, &needed);
240 num_sizes = GET_BE_DWORD(&eblc->numSizes);
242 size_table = (bitmapSizeTable_t *)(eblc + 1);
243 for(i = 0; i < num_sizes; i++)
245 if(size_table->hori.ascender - size_table->hori.descender == ppem)
247 ascent = size_table->hori.ascender;
248 descent = -size_table->hori.descender;
249 break;
251 size_table++;
254 free(eblc);
255 #endif
257 /* Versions of fontforge prior to early 2006 have incorrect
258 ascender values in the eblc table, so we won't find the
259 correct bitmapSizeTable. In this case use the height of
260 the Aring glyph instead. */
261 if(ascent == 0)
263 if(FT_Load_Char(face, 0xc5, FT_LOAD_DEFAULT))
264 error("Can't find Aring\n");
265 ascent = face->glyph->metrics.horiBearingY >> 6;
266 descent = ppem - ascent;
269 start = sizeof(FNT_HEADER) + sizeof(FONTINFO16);
271 if(FT_Load_Char(face, 'M', FT_LOAD_DEFAULT))
272 error("Can't find M\n");
273 il = ascent - (face->glyph->metrics.height >> 6);
275 /* Hack: Courier has no internal leading, nor do any Chinese fonts */
276 if(!strcmp(face->family_name, "Courier") || enc == 936 || enc == 950)
277 il = 0;
279 first_char = FT_Get_First_Char(face, &gi);
280 if(first_char == 0xd) /* fontforge's first glyph is 0xd, we'll catch this and skip it */
281 first_char = 32; /* FT_Get_Next_Char for some reason returns too high
282 number in this case */
284 dfCharTable = malloc((255 + 3) * sizeof(*dfCharTable));
285 memset(dfCharTable, 0, (255 + 3) * sizeof(*dfCharTable));
287 memset(&fi, 0, sizeof(fi));
288 fi.dfFirstChar = first_char;
289 fi.dfLastChar = 0xff;
290 start += ((unsigned char)fi.dfLastChar - (unsigned char)fi.dfFirstChar + 3 ) * sizeof(*dfCharTable);
292 num_names = FT_Get_Sfnt_Name_Count(face);
293 for(i = 0; i <num_names; i++) {
294 FT_Get_Sfnt_Name(face, i, &sfntname);
295 if(sfntname.platform_id == 1 && sfntname.encoding_id == 0 &&
296 sfntname.language_id == 0 && sfntname.name_id == 0) {
297 size_t len = min( sfntname.string_len, sizeof(hdr.dfCopyright)-1 );
298 memcpy(hdr.dfCopyright, sfntname.string, len);
299 hdr.dfCopyright[len] = 0;
303 os2 = FT_Get_Sfnt_Table(face, ft_sfnt_os2);
304 for(i = first_char; i < 0x100; i++) {
305 int c = get_char(cptable, enc, i);
306 gi = FT_Get_Char_Index(face, c);
307 if(gi == 0)
308 fprintf(stderr, "Missing glyph for char %04x\n", cptable->sbcs.cp2uni[i]);
309 if(FT_Load_Char(face, c, FT_LOAD_DEFAULT)) {
310 fprintf(stderr, "error loading char %d - bad news!\n", i);
311 continue;
313 dfCharTable[i].width = face->glyph->metrics.horiAdvance >> 6;
314 dfCharTable[i].offset = start + (width_bytes * ppem);
315 width_bytes += ((face->glyph->metrics.horiAdvance >> 6) + 7) >> 3;
316 if(max_width < (face->glyph->metrics.horiAdvance >> 6))
317 max_width = face->glyph->metrics.horiAdvance >> 6;
319 /* space */
320 space_size = (ppem + 3) / 4;
321 dfCharTable[i].width = space_size;
322 dfCharTable[i].offset = start + (width_bytes * ppem);
323 width_bytes += (space_size + 7) >> 3;
324 /* sentinel */
325 dfCharTable[++i].width = 0;
326 dfCharTable[i].offset = start + (width_bytes * ppem);
328 fi.dfType = 0;
329 fi.dfPoints = ((ppem - il) * 72 + dpi/2) / dpi;
330 fi.dfVertRes = dpi;
331 fi.dfHorizRes = dpi;
332 fi.dfAscent = ascent;
333 fi.dfInternalLeading = il;
334 fi.dfExternalLeading = 0;
335 fi.dfItalic = (face->style_flags & FT_STYLE_FLAG_ITALIC) ? 1 : 0;
336 fi.dfUnderline = 0;
337 fi.dfStrikeOut = 0;
338 fi.dfWeight = os2->usWeightClass;
339 fi.dfCharSet = lookup_charset(enc);
340 fi.dfPixWidth = (face->face_flags & FT_FACE_FLAG_FIXED_WIDTH) ?
341 avg_width : 0;
342 fi.dfPixHeight = ppem;
343 fi.dfPitchAndFamily = FT_IS_FIXED_WIDTH(face) ? 0 : TMPF_FIXED_PITCH;
344 switch(os2->panose[PAN_FAMILYTYPE_INDEX]) {
345 case PAN_FAMILY_SCRIPT:
346 fi.dfPitchAndFamily |= FF_SCRIPT;
347 break;
348 case PAN_FAMILY_DECORATIVE:
349 case PAN_FAMILY_PICTORIAL:
350 fi.dfPitchAndFamily |= FF_DECORATIVE;
351 break;
352 case PAN_FAMILY_TEXT_DISPLAY:
353 if(fi.dfPitchAndFamily == 0) /* fixed */
354 fi.dfPitchAndFamily = FF_MODERN;
355 else {
356 switch(os2->panose[PAN_SERIFSTYLE_INDEX]) {
357 case PAN_SERIF_NORMAL_SANS:
358 case PAN_SERIF_OBTUSE_SANS:
359 case PAN_SERIF_PERP_SANS:
360 fi.dfPitchAndFamily |= FF_SWISS;
361 break;
362 default:
363 fi.dfPitchAndFamily |= FF_ROMAN;
366 break;
367 default:
368 fi.dfPitchAndFamily |= FF_DONTCARE;
371 fi.dfAvgWidth = avg_width;
372 fi.dfMaxWidth = max_width;
373 fi.dfDefaultChar = def_char - fi.dfFirstChar;
374 fi.dfBreakChar = ' ' - fi.dfFirstChar;
375 fi.dfWidthBytes = (width_bytes + 1) & ~1;
377 fi.dfFace = start + fi.dfWidthBytes * ppem;
378 fi.dfBitsOffset = start;
379 fi.dfFlags = 0x10; /* DFF_1COLOR */
380 fi.dfFlags |= FT_IS_FIXED_WIDTH(face) ? 1 : 2; /* DFF_FIXED : DFF_PROPORTIONAL */
382 hdr.dfVersion = 0x300;
383 hdr.dfSize = start + fi.dfWidthBytes * ppem + strlen(face->family_name) + 1;
384 fwrite(&hdr, sizeof(hdr), 1, fp);
385 fwrite(&fi, sizeof(fi), 1, fp);
386 fwrite(dfCharTable + fi.dfFirstChar, sizeof(*dfCharTable), ((unsigned char)fi.dfLastChar - (unsigned char)fi.dfFirstChar) + 3, fp);
388 for(i = first_char; i < 0x100; i++) {
389 int c = get_char(cptable, enc, i);
390 if(FT_Load_Char(face, c, FT_LOAD_DEFAULT)) {
391 continue;
393 assert(dfCharTable[i].width == face->glyph->metrics.horiAdvance >> 6);
395 for(x = 0; x < ((dfCharTable[i].width + 7) / 8); x++) {
396 for(y = 0; y < ppem; y++) {
397 if(y < ascent - face->glyph->bitmap_top ||
398 y >= face->glyph->bitmap.rows + ascent - face->glyph->bitmap_top) {
399 fputc('\0', fp);
400 continue;
402 x_off = face->glyph->bitmap_left / 8;
403 x_end = (face->glyph->bitmap_left + face->glyph->bitmap.width - 1) / 8;
404 if(x < x_off || x > x_end) {
405 fputc('\0', fp);
406 continue;
408 if(x == x_off)
409 left_byte = 0;
410 else
411 left_byte = face->glyph->bitmap.buffer[(y - (ascent - face->glyph->bitmap_top)) * face->glyph->bitmap.pitch + x - x_off - 1];
413 /* On the last non-trival output byte (x == x_end) have we got one or two input bytes */
414 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)))
415 right_byte = 0;
416 else
417 right_byte = face->glyph->bitmap.buffer[(y - (ascent - face->glyph->bitmap_top)) * face->glyph->bitmap.pitch + x - x_off];
419 byte = (left_byte << (8 - (face->glyph->bitmap_left & 7))) & 0xff;
420 byte |= ((right_byte >> (face->glyph->bitmap_left & 7)) & 0xff);
421 fputc(byte, fp);
425 for(x = 0; x < (space_size + 7) / 8; x++) {
426 for(y = 0; y < ppem; y++)
427 fputc('\0', fp);
430 if(width_bytes & 1) {
431 for(y = 0; y < ppem; y++)
432 fputc('\0', fp);
434 fprintf(fp, "%s", face->family_name);
435 fputc('\0', fp);
440 int main(int argc, char **argv)
442 int ppem, enc;
443 FT_Face face;
444 FT_Library lib;
445 int dpi, avg_width;
446 unsigned int def_char;
447 FILE *fp;
448 char output[256];
449 char name[256];
450 char *cp;
451 if(argc != 7) {
452 usage(argv);
453 exit(0);
456 ppem = atoi(argv[2]);
457 enc = atoi(argv[3]);
458 dpi = atoi(argv[4]);
459 def_char = atoi(argv[5]);
460 avg_width = atoi(argv[6]);
462 if(FT_Init_FreeType(&lib))
463 error("ft init failure\n");
465 if(FT_New_Face(lib, argv[1], 0, &face)) {
466 fprintf(stderr, "Can't open face\n");
467 usage(argv);
468 exit(1);
471 if(FT_Set_Pixel_Sizes(face, ppem, ppem)) {
472 fprintf(stderr, "Can't set size\n");
473 usage(argv);
474 exit(1);
477 strcpy(name, face->family_name);
478 /* FIXME: should add a -o option instead */
479 for(cp = name; *cp; cp++)
481 if(*cp == ' ') *cp = '_';
482 else if (*cp >= 'A' && *cp <= 'Z') *cp += 'a' - 'A';
485 sprintf(output, "%s-%d-%d-%d.fnt", name, enc, dpi, ppem);
487 atexit( cleanup );
488 signal( SIGTERM, exit_on_signal );
489 signal( SIGINT, exit_on_signal );
490 #ifdef SIGHUP
491 signal( SIGHUP, exit_on_signal );
492 #endif
494 fp = fopen(output, "w");
495 output_name = output;
497 fill_fontinfo(face, enc, fp, dpi, def_char, avg_width);
498 fclose(fp);
499 output_name = NULL;
500 exit(0);
503 #else /* HAVE_FREETYPE */
505 int main(int argc, char **argv)
507 fprintf( stderr, "%s needs to be built with FreeType support\n", argv[0] );
508 exit(1);
511 #endif /* HAVE_FREETYPE */