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