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
22 #include "wine/port.h"
32 #ifdef HAVE_FT2BUILD_H
35 #include FT_FREETYPE_H
36 #include FT_SFNT_NAMES_H
37 #include FT_TRUETYPE_TABLES_H
38 #include FT_TRUETYPE_TAGS_H
39 #ifdef HAVE_FREETYPE_INTERNAL_SFNT_H
40 #include <freetype/internal/sfnt.h>
43 #include "wine/unicode.h"
44 #include "wine/wingdi16.h"
70 CHAR caretSlopeNumerator
;
71 CHAR caretSlopeDenominator
;
82 ULONG indexSubTableArrayOffset
;
84 ULONG numberOfIndexSubTables
;
86 sbitLineMetrics_t hori
;
87 sbitLineMetrics_t vert
;
88 USHORT startGlyphIndex
;
102 static FT_Version_t FT_Version
;
104 #define GET_BE_WORD(ptr) MAKEWORD( ((BYTE *)(ptr))[1], ((BYTE *)(ptr))[0] )
105 #define GET_BE_DWORD(ptr) ((DWORD)MAKELONG( GET_BE_WORD(&((WORD *)(ptr))[1]), \
106 GET_BE_WORD(&((WORD *)(ptr))[0]) ))
110 static const char *output_name
;
112 static void usage(char **argv
)
114 fprintf(stderr
, "%s foo.ttf ppem enc dpi def_char avg_width\n", argv
[0]);
119 #define __attribute__(X)
122 /* atexit handler to cleanup files */
123 static void cleanup(void)
125 if (output_name
) unlink( output_name
);
128 static void exit_on_signal( int sig
)
130 exit(1); /* this will call the atexit functions */
133 static void error(const char *s
, ...) __attribute__((format (printf
, 1, 2)));
135 static void error(const char *s
, ...)
139 fprintf(stderr
, "Error: ");
140 vfprintf(stderr
, s
, ap
);
145 static int lookup_charset(int enc
)
147 /* FIXME: make winelib app and use TranslateCharsetInfo */
152 return RUSSIAN_CHARSET
;
156 return GREEK_CHARSET
;
158 return TURKISH_CHARSET
;
160 return HEBREW_CHARSET
;
162 return ARABIC_CHARSET
;
164 return BALTIC_CHARSET
;
166 return VIETNAMESE_CHARSET
;
186 return SHIFTJIS_CHARSET
;
188 return GB2312_CHARSET
;
190 return HANGUL_CHARSET
;
192 return CHINESEBIG5_CHARSET
;
194 fprintf(stderr
, "Unknown encoding %d - using OEM_CHARSET\n", enc
);
199 static int get_char(const union cptable
*cptable
, int enc
, int index
)
201 /* Korean has the Won sign in place of '\\' */
202 if(enc
== 949 && index
== '\\')
205 return cptable
->sbcs
.cp2uni
[index
];
208 /* from gdi32/freetype.c */
209 static FT_Error
load_sfnt_table(FT_Face ft_face
, FT_ULong table
, FT_Long offset
, FT_Byte
*buf
, FT_ULong
*len
)
214 /* If the FT_Load_Sfnt_Table function is there we'll use it */
215 #ifdef HAVE_FT_LOAD_SFNT_TABLE
216 err
= FT_Load_Sfnt_Table(ft_face
, table
, offset
, buf
, len
);
217 #elif defined(HAVE_FREETYPE_INTERNAL_SFNT_H)
218 TT_Face tt_face
= (TT_Face
) ft_face
;
219 SFNT_Interface
*sfnt
;
220 if (FT_Version
.major
==2 && FT_Version
.minor
==0)
223 sfnt
= *(SFNT_Interface
**)((char*)tt_face
+ 528);
227 /* A field was added in the middle of the structure in 2.1.x */
228 sfnt
= *(SFNT_Interface
**)((char*)tt_face
+ 532);
230 err
= sfnt
->load_any(tt_face
, table
, offset
, buf
, len
);
232 err
= FT_Err_Unimplemented_Feature
;
237 static void fill_fontinfo(FT_Face face
, int enc
, FILE *fp
, int dpi
, unsigned char def_char
, int avg_width
)
239 int ascent
= 0, il
, el
, ppem
, descent
= 0, width_bytes
= 0, space_size
, max_width
= 0;
242 BYTE left_byte
, right_byte
, byte
;
244 CHAR_TABLE_ENTRY
*dfCharTable
;
245 int i
, x
, y
, x_off
, x_end
, first_char
;
248 const union cptable
*cptable
;
249 FT_SfntName sfntname
;
253 bitmapSizeTable_t
*size_table
;
256 cptable
= wine_cp_get_table(enc
);
258 error("Can't find codepage %d\n", enc
);
260 if(cptable
->info
.char_size
!= 1) {
261 /* for double byte charsets we actually want to use cp1252 */
262 cptable
= wine_cp_get_table(1252);
264 error("Can't find codepage 1252\n");
267 ppem
= face
->size
->metrics
.y_ppem
;
270 if (load_sfnt_table(face
, TTAG_EBLC
, 0, NULL
, &needed
))
271 fprintf(stderr
,"Can't find EBLC table\n");
274 eblc
= malloc(needed
);
275 load_sfnt_table(face
, TTAG_EBLC
, 0, (FT_Byte
*)eblc
, &needed
);
277 num_sizes
= GET_BE_DWORD(&eblc
->numSizes
);
279 size_table
= (bitmapSizeTable_t
*)(eblc
+ 1);
280 for(i
= 0; i
< num_sizes
; i
++)
282 if(size_table
->hori
.ascender
- size_table
->hori
.descender
== ppem
)
284 ascent
= size_table
->hori
.ascender
;
285 descent
= -size_table
->hori
.descender
;
294 /* Versions of fontforge prior to early 2006 have incorrect
295 ascender values in the eblc table, so we won't find the
296 correct bitmapSizeTable. In this case use the height of
297 the Aring glyph instead. */
300 if(FT_Load_Char(face
, 0xc5, FT_LOAD_DEFAULT
))
301 error("Can't find Aring\n");
302 ascent
= face
->glyph
->metrics
.horiBearingY
>> 6;
303 descent
= ppem
- ascent
;
306 start
= sizeof(FNT_HEADER
) + sizeof(FONTINFO16
);
308 if(FT_Load_Char(face
, 'M', FT_LOAD_DEFAULT
))
309 error("Can't find M\n");
310 il
= ascent
- (face
->glyph
->metrics
.height
>> 6);
312 /* Hack: Courier has no internal leading, nor do any Chinese or Japanese fonts */
313 if(!strcmp(face
->family_name
, "Courier") || enc
== 936 || enc
== 950 || enc
== 932)
315 /* Japanese system fonts have an external leading (not small font) */
316 if (enc
== 932 && ppem
> 11)
321 first_char
= FT_Get_First_Char(face
, &gi
);
322 if(first_char
== 0xd) /* fontforge's first glyph is 0xd, we'll catch this and skip it */
323 first_char
= 32; /* FT_Get_Next_Char for some reason returns too high
324 number in this case */
326 dfCharTable
= malloc((255 + 3) * sizeof(*dfCharTable
));
327 memset(dfCharTable
, 0, (255 + 3) * sizeof(*dfCharTable
));
329 memset(&fi
, 0, sizeof(fi
));
330 fi
.dfFirstChar
= first_char
;
331 fi
.dfLastChar
= 0xff;
332 start
+= ((unsigned char)fi
.dfLastChar
- (unsigned char)fi
.dfFirstChar
+ 3 ) * sizeof(*dfCharTable
);
334 num_names
= FT_Get_Sfnt_Name_Count(face
);
335 for(i
= 0; i
<num_names
; i
++) {
336 FT_Get_Sfnt_Name(face
, i
, &sfntname
);
337 if(sfntname
.platform_id
== 1 && sfntname
.encoding_id
== 0 &&
338 sfntname
.language_id
== 0 && sfntname
.name_id
== 0) {
339 size_t len
= min( sfntname
.string_len
, sizeof(hdr
.dfCopyright
)-1 );
340 memcpy(hdr
.dfCopyright
, sfntname
.string
, len
);
341 hdr
.dfCopyright
[len
] = 0;
345 os2
= FT_Get_Sfnt_Table(face
, ft_sfnt_os2
);
346 for(i
= first_char
; i
< 0x100; i
++) {
347 int c
= get_char(cptable
, enc
, i
);
348 gi
= FT_Get_Char_Index(face
, c
);
350 fprintf(stderr
, "Missing glyph for char %04x\n", cptable
->sbcs
.cp2uni
[i
]);
351 if(FT_Load_Char(face
, c
, FT_LOAD_DEFAULT
)) {
352 fprintf(stderr
, "error loading char %d - bad news!\n", i
);
355 dfCharTable
[i
].width
= face
->glyph
->metrics
.horiAdvance
>> 6;
356 dfCharTable
[i
].offset
= start
+ (width_bytes
* ppem
);
357 width_bytes
+= ((face
->glyph
->metrics
.horiAdvance
>> 6) + 7) >> 3;
358 if(max_width
< (face
->glyph
->metrics
.horiAdvance
>> 6))
359 max_width
= face
->glyph
->metrics
.horiAdvance
>> 6;
362 space_size
= (ppem
+ 3) / 4;
363 dfCharTable
[i
].width
= space_size
;
364 dfCharTable
[i
].offset
= start
+ (width_bytes
* ppem
);
365 width_bytes
+= (space_size
+ 7) >> 3;
367 dfCharTable
[++i
].width
= 0;
368 dfCharTable
[i
].offset
= start
+ (width_bytes
* ppem
);
371 fi
.dfPoints
= ((ppem
- il
) * 72 + dpi
/2) / dpi
;
374 fi
.dfAscent
= ascent
;
375 fi
.dfInternalLeading
= il
;
376 fi
.dfExternalLeading
= el
;
377 fi
.dfItalic
= (face
->style_flags
& FT_STYLE_FLAG_ITALIC
) ? 1 : 0;
380 fi
.dfWeight
= os2
->usWeightClass
;
381 fi
.dfCharSet
= lookup_charset(enc
);
382 fi
.dfPixWidth
= (face
->face_flags
& FT_FACE_FLAG_FIXED_WIDTH
) ?
384 fi
.dfPixHeight
= ppem
;
385 fi
.dfPitchAndFamily
= FT_IS_FIXED_WIDTH(face
) ? 0 : TMPF_FIXED_PITCH
;
386 switch(os2
->panose
[PAN_FAMILYTYPE_INDEX
]) {
387 case PAN_FAMILY_SCRIPT
:
388 fi
.dfPitchAndFamily
|= FF_SCRIPT
;
390 case PAN_FAMILY_DECORATIVE
:
391 case PAN_FAMILY_PICTORIAL
:
392 fi
.dfPitchAndFamily
|= FF_DECORATIVE
;
394 case PAN_FAMILY_TEXT_DISPLAY
:
395 if(fi
.dfPitchAndFamily
== 0) /* fixed */
396 fi
.dfPitchAndFamily
= FF_MODERN
;
398 switch(os2
->panose
[PAN_SERIFSTYLE_INDEX
]) {
399 case PAN_SERIF_NORMAL_SANS
:
400 case PAN_SERIF_OBTUSE_SANS
:
401 case PAN_SERIF_PERP_SANS
:
402 fi
.dfPitchAndFamily
|= FF_SWISS
;
405 fi
.dfPitchAndFamily
|= FF_ROMAN
;
410 fi
.dfPitchAndFamily
|= FF_DONTCARE
;
413 fi
.dfAvgWidth
= avg_width
;
414 fi
.dfMaxWidth
= max_width
;
415 fi
.dfDefaultChar
= def_char
- fi
.dfFirstChar
;
416 fi
.dfBreakChar
= ' ' - fi
.dfFirstChar
;
417 fi
.dfWidthBytes
= (width_bytes
+ 1) & ~1;
419 fi
.dfFace
= start
+ fi
.dfWidthBytes
* ppem
;
420 fi
.dfBitsOffset
= start
;
421 fi
.dfFlags
= 0x10; /* DFF_1COLOR */
422 fi
.dfFlags
|= FT_IS_FIXED_WIDTH(face
) ? 1 : 2; /* DFF_FIXED : DFF_PROPORTIONAL */
424 hdr
.dfVersion
= 0x300;
425 hdr
.dfSize
= start
+ fi
.dfWidthBytes
* ppem
+ strlen(face
->family_name
) + 1;
426 fwrite(&hdr
, sizeof(hdr
), 1, fp
);
427 fwrite(&fi
, sizeof(fi
), 1, fp
);
428 fwrite(dfCharTable
+ fi
.dfFirstChar
, sizeof(*dfCharTable
), ((unsigned char)fi
.dfLastChar
- (unsigned char)fi
.dfFirstChar
) + 3, fp
);
430 for(i
= first_char
; i
< 0x100; i
++) {
431 int c
= get_char(cptable
, enc
, i
);
432 if(FT_Load_Char(face
, c
, FT_LOAD_DEFAULT
)) {
435 assert(dfCharTable
[i
].width
== face
->glyph
->metrics
.horiAdvance
>> 6);
437 for(x
= 0; x
< ((dfCharTable
[i
].width
+ 7) / 8); x
++) {
438 for(y
= 0; y
< ppem
; y
++) {
439 if(y
< ascent
- face
->glyph
->bitmap_top
||
440 y
>= face
->glyph
->bitmap
.rows
+ ascent
- face
->glyph
->bitmap_top
) {
444 x_off
= face
->glyph
->bitmap_left
/ 8;
445 x_end
= (face
->glyph
->bitmap_left
+ face
->glyph
->bitmap
.width
- 1) / 8;
446 if(x
< x_off
|| x
> x_end
) {
453 left_byte
= face
->glyph
->bitmap
.buffer
[(y
- (ascent
- face
->glyph
->bitmap_top
)) * face
->glyph
->bitmap
.pitch
+ x
- x_off
- 1];
455 /* On the last non-trival output byte (x == x_end) have we got one or two input bytes */
456 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)))
459 right_byte
= face
->glyph
->bitmap
.buffer
[(y
- (ascent
- face
->glyph
->bitmap_top
)) * face
->glyph
->bitmap
.pitch
+ x
- x_off
];
461 byte
= (left_byte
<< (8 - (face
->glyph
->bitmap_left
& 7))) & 0xff;
462 byte
|= ((right_byte
>> (face
->glyph
->bitmap_left
& 7)) & 0xff);
467 for(x
= 0; x
< (space_size
+ 7) / 8; x
++) {
468 for(y
= 0; y
< ppem
; y
++)
472 if(width_bytes
& 1) {
473 for(y
= 0; y
< ppem
; y
++)
476 fprintf(fp
, "%s", face
->family_name
);
482 int main(int argc
, char **argv
)
488 unsigned int def_char
;
498 ppem
= atoi(argv
[2]);
501 def_char
= atoi(argv
[5]);
502 avg_width
= atoi(argv
[6]);
504 if(FT_Init_FreeType(&lib
))
505 error("ft init failure\n");
507 FT_Version
.major
=FT_Version
.minor
=FT_Version
.patch
=-1;
508 FT_Library_Version(lib
,&FT_Version
.major
,&FT_Version
.minor
,&FT_Version
.patch
);
510 if(FT_New_Face(lib
, argv
[1], 0, &face
)) {
511 fprintf(stderr
, "Can't open face\n");
516 if(FT_Set_Pixel_Sizes(face
, ppem
, ppem
)) {
517 fprintf(stderr
, "Can't set size\n");
522 strcpy(name
, face
->family_name
);
523 /* FIXME: should add a -o option instead */
524 for(cp
= name
; *cp
; cp
++)
526 if(*cp
== ' ') *cp
= '_';
527 else if (*cp
>= 'A' && *cp
<= 'Z') *cp
+= 'a' - 'A';
530 sprintf(output
, "%s-%d-%d-%d.fnt", name
, enc
, dpi
, ppem
);
533 signal( SIGTERM
, exit_on_signal
);
534 signal( SIGINT
, exit_on_signal
);
536 signal( SIGHUP
, exit_on_signal
);
539 fp
= fopen(output
, "w");
540 output_name
= output
;
542 fill_fontinfo(face
, enc
, fp
, dpi
, def_char
, avg_width
);
548 #else /* HAVE_FREETYPE */
550 int main(int argc
, char **argv
)
552 fprintf( stderr
, "%s needs to be built with FreeType support\n", argv
[0] );
556 #endif /* HAVE_FREETYPE */