winedump: Cast-qual warnings fix.
[wine] / tools / sfnt2fnt.c
1 /*
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
18  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
19  */
20
21 #include "config.h"
22 #include "wine/port.h"
23
24 #include <assert.h>
25 #include <ctype.h>
26 #include <signal.h>
27 #include <stdio.h>
28 #include <stdlib.h>
29
30 #ifdef HAVE_FREETYPE
31
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
39
40 #include "wine/unicode.h"
41 #include "wine/wingdi16.h"
42 #include "wingdi.h"
43
44 #include "pshpack1.h"
45
46 typedef struct
47 {
48     WORD dfVersion;
49     DWORD dfSize;
50     char dfCopyright[60];
51 } FNT_HEADER;
52
53 typedef struct {
54     WORD width;
55     DWORD offset;
56 } CHAR_TABLE_ENTRY;
57
58 typedef struct {
59     DWORD version;
60     ULONG numSizes;
61 } eblcHeader_t;
62
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;
77
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;
92
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]) ))
96
97 #include "poppack.h"
98
99 static const char *output_name;
100
101 static void usage(char **argv)
102 {
103     fprintf(stderr, "%s foo.ttf ppem enc dpi def_char avg_width\n", argv[0]);
104     return;
105 }
106
107 #ifndef __GNUC__
108 #define __attribute__(X)
109 #endif
110
111 /* atexit handler to cleanup files */
112 static void cleanup(void)
113 {
114     if (output_name) unlink( output_name );
115 }
116
117 static void exit_on_signal( int sig )
118 {
119     exit(1);  /* this will call the atexit functions */
120 }
121
122 static void error(const char *s, ...) __attribute__((format (printf, 1, 2)));
123
124 static void error(const char *s, ...)
125 {
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);
132 }
133
134 static int lookup_charset(int enc)
135 {
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;
182     }
183     fprintf(stderr, "Unknown encoding %d - using OEM_CHARSET\n", enc);
184
185     return OEM_CHARSET;
186 }
187
188 static int get_char(const union cptable *cptable, int enc, int index)
189 {
190     /* Korean has the Won sign in place of '\\' */
191     if(enc == 949 && index == '\\')
192         return 0x20a9;
193
194     return cptable->sbcs.cp2uni[index];
195 }
196
197 static void fill_fontinfo(FT_Face face, int enc, FILE *fp, int dpi, unsigned char def_char, int avg_width)
198 {
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;
211
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
218
219     cptable = wine_cp_get_table(enc);
220     if(!cptable)
221         error("Can't find codepage %d\n", enc);
222
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");
228     }
229
230     ppem = face->size->metrics.y_ppem;
231
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");
236
237     eblc = malloc(needed);
238     FT_Load_Sfnt_Table(face, TTAG_EBLC, 0, (FT_Byte *)eblc, &needed);
239
240     num_sizes = GET_BE_DWORD(&eblc->numSizes);
241
242     size_table = (bitmapSizeTable_t *)(eblc + 1);
243     for(i = 0; i < num_sizes; i++)
244     {
245         if(size_table->hori.ascender - size_table->hori.descender == ppem)
246         {
247             ascent = size_table->hori.ascender;
248             descent = -size_table->hori.descender;
249             break;
250         }
251         size_table++;
252     }
253
254     free(eblc);
255 #endif
256
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) 
262     {
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;
267     }
268
269     start = sizeof(FNT_HEADER) + sizeof(FONTINFO16);
270
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);
274
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;
278
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 */
283
284     dfCharTable = malloc((255 + 3) * sizeof(*dfCharTable));
285     memset(dfCharTable, 0, (255 + 3) * sizeof(*dfCharTable));
286
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);
291
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;
300         }
301     }
302
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;
312         }
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;
318     }
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);
327
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;
364             }
365         }
366         break;
367     default:
368         fi.dfPitchAndFamily |= FF_DONTCARE;
369     }
370
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;
376
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 */
381
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);
387
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;
392         }
393         assert(dfCharTable[i].width == face->glyph->metrics.horiAdvance >> 6);
394
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;
401                 }
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;
407                 }
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];
412
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];
418
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);
422             }
423         }
424     }
425     for(x = 0; x < (space_size + 7) / 8; x++) {
426         for(y = 0; y < ppem; y++)
427             fputc('\0', fp);
428     }
429
430     if(width_bytes & 1) {
431         for(y = 0; y < ppem; y++)
432             fputc('\0', fp);
433     }
434     fprintf(fp, "%s", face->family_name);
435     fputc('\0', fp);
436
437 }
438
439
440 int main(int argc, char **argv)
441 {
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);
454     }
455
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]);
461
462     if(FT_Init_FreeType(&lib))
463         error("ft init failure\n");
464
465     if(FT_New_Face(lib, argv[1], 0, &face)) {
466         fprintf(stderr, "Can't open face\n");
467         usage(argv);
468         exit(1);
469     }
470
471     if(FT_Set_Pixel_Sizes(face, ppem, ppem)) {
472         fprintf(stderr, "Can't set size\n");
473         usage(argv);
474         exit(1);
475     }
476
477     strcpy(name, face->family_name);
478     /* FIXME: should add a -o option instead */
479     for(cp = name; *cp; cp++)
480     {
481         if(*cp == ' ') *cp = '_';
482         else if (*cp >= 'A' && *cp <= 'Z') *cp += 'a' - 'A';
483     }
484
485     sprintf(output, "%s-%d-%d-%d.fnt", name, enc, dpi, ppem);
486
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
493
494     fp = fopen(output, "w");
495     output_name = output;
496
497     fill_fontinfo(face, enc, fp, dpi, def_char, avg_width);
498     fclose(fp);
499     output_name = NULL;
500     exit(0);
501 }
502
503 #else /* HAVE_FREETYPE */
504
505 int main(int argc, char **argv)
506 {
507     fprintf( stderr, "%s needs to be built with FreeType support\n", argv[0] );
508     exit(1);
509 }
510
511 #endif /* HAVE_FREETYPE */