several fixes
[swftools.git] / lib / gfxfont.c
1 /* swffont.c
2
3    Functions for loading external fonts.
4
5    Extension module for the rfxswf library.
6    Part of the swftools package.
7
8    Copyright (c) 2003, 2004, 2005 Matthias Kramm
9  
10    This program is free software; you can redistribute it and/or modify
11    it under the terms of the GNU General Public License as published by
12    the Free Software Foundation; either version 2 of the License, or
13    (at your option) any later version.
14
15    This program is distributed in the hope that it will be useful,
16    but WITHOUT ANY WARRANTY; without even the implied warranty of
17    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18    GNU General Public License for more details.
19
20    You should have received a copy of the GNU General Public License
21    along with this program; if not, write to the Free Software
22    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA */
23
24 #include "gfxdevice.h"
25 #include "gfxtools.h"
26
27 static int loadfont_scale = 64;
28 static int skip_unused = 0;
29 static int full_unicode = 1;
30
31 #ifdef HAVE_FREETYPE
32
33 #ifdef HAVE_FT2BUILD_H
34 #include <ft2build.h>
35 #include FT_FREETYPE_H
36 #include FT_GLYPH_H
37 #include FT_SIZES_H
38 #include FT_SFNT_NAMES_H
39 #include FT_TRUETYPE_IDS_H
40 #include FT_OUTLINE_H
41 #else
42 #include <freetype/freetype.h>
43 #include <freetype/ftglyph.h>
44 #include <freetype/ftsizes.h>
45 #include <freetype/ftsnames.h>
46 #include <freetype/ttnameid.h>
47 #include <freetype/ftoutln.h>
48 #endif
49
50 /* Setting subpixels to 64 also means that the "point size" of the
51    font outlines will be 64. So the font, when rendered at original
52    size (i.e., the swf fontsize is 1024) will have the same size as
53    if it was rendered at 64pt */
54
55 #define FT_SCALE 1
56 #define FT_SUBPIXELS 64
57
58 static int ft_move_to(FT_Vector* _to, void* user) 
59 {
60     gfxdrawer_t* draw = (gfxdrawer_t*)user;
61     double x = _to->x*FT_SCALE/(float)FT_SUBPIXELS;
62     double y = -_to->y*FT_SCALE/(float)FT_SUBPIXELS;
63     draw->moveTo(draw, x,y);
64     return 0;
65 }
66 static int ft_line_to(FT_Vector* _to, void* user) 
67 {
68     gfxdrawer_t* draw = (gfxdrawer_t*)user;
69     double x = _to->x*FT_SCALE/(float)FT_SUBPIXELS;
70     double y = -_to->y*FT_SCALE/(float)FT_SUBPIXELS;
71     draw->lineTo(draw, x,y);
72     return 0;
73 }
74 static int ft_cubic_to(FT_Vector* _c1, FT_Vector* _c2, FT_Vector* _to, void* user)
75 {
76     gfxdrawer_t* draw = (gfxdrawer_t*)user;
77     double tox = _to->x*FT_SCALE/(float)FT_SUBPIXELS;
78     double toy = -_to->y*FT_SCALE/(float)FT_SUBPIXELS;
79     double c1x = _c1->x*FT_SCALE/(float)FT_SUBPIXELS;
80     double c1y = -_c1->y*FT_SCALE/(float)FT_SUBPIXELS;
81     double c2x = _c2->x*FT_SCALE/(float)FT_SUBPIXELS;
82     double c2y = -_c2->y*FT_SCALE/(float)FT_SUBPIXELS;
83     gfxdraw_cubicTo(draw, c1x, c1y, c2x, c2y, tox, toy);
84     return 0;
85 }
86 static int ft_conic_to(FT_Vector* _c, FT_Vector* _to, void* user) 
87 {
88     gfxdrawer_t* draw = (gfxdrawer_t*)user;
89     double tox = _to->x*FT_SCALE/(float)FT_SUBPIXELS;
90     double toy = -_to->y*FT_SCALE/(float)FT_SUBPIXELS;
91     double cx = _c->x*FT_SCALE/(float)FT_SUBPIXELS;
92     double cy = -_c->y*FT_SCALE/(float)FT_SUBPIXELS;
93     gfxdraw_conicTo(draw, cx,cy, tox,toy);
94     return 0;
95 }
96 static FT_Outline_Funcs outline_functions =
97 {
98   ft_move_to,
99   ft_line_to,
100   ft_conic_to,
101   ft_cubic_to,
102   0,0
103 };
104
105 static FT_Library ftlibrary = 0;
106
107 static gfxglyph_t cloneGlyph(gfxglyph_t*src)
108 {
109     gfxglyph_t dest;
110     memset(&dest, 0, sizeof(dest));
111     if(src->name)
112         dest.name = strdup(src->name);
113     dest.advance = src->advance;
114     dest.unicode = src->unicode;
115     dest.line = gfxline_clone(src->line);
116     return dest;
117 }
118
119 static void glyph_clear(gfxglyph_t*g)
120 {
121     gfxline_t*line;
122     if(g->name) {
123         free(g->name); g->name = 0;
124     }
125     gfxline_free(g->line);g->line = 0;
126 }
127
128 void gfxfont_free(gfxfont_t*font)
129 {
130     int t;
131     for(t=0;t<font->num_glyphs;t++) {
132         glyph_clear(&font->glyphs[t]);
133     }
134     if(font->glyphs) {
135         free(font->glyphs);font->glyphs = 0;
136     }
137     font->num_glyphs = 0;
138     if(font->unicode2glyph) {
139         free(font->unicode2glyph);font->unicode2glyph = 0;
140     }
141     free(font);
142 }
143
144 gfxfont_t* gfxfont_load(char*filename)
145 {
146     FT_Face face;
147     FT_Error error;
148     const char* name = 0;
149     FT_ULong charcode;
150     FT_UInt gindex;
151     gfxfont_t* font;
152     int t;
153     int*glyph2glyph = 0;
154     int*glyph2unicode = 0;
155     FT_Size size;
156     int max_unicode = 0;
157     int charmap = -1;
158     int isunicode = 1;
159    
160     if(ftlibrary == 0) {
161         if(FT_Init_FreeType(&ftlibrary)) {
162             fprintf(stderr, "Couldn't init freetype library!\n");
163             exit(1);
164         }
165     }
166     error = FT_New_Face(ftlibrary, filename, 0, &face);
167     FT_Set_Pixel_Sizes (face, 16*loadfont_scale, 16*loadfont_scale);
168
169     if(error) {
170         fprintf(stderr, "Couldn't load file %s- not a TTF file?\n", filename);
171         return 0;
172     }
173     if(face->num_glyphs <= 0) {
174         fprintf(stderr, "File %s contains %d glyphs\n", face->num_glyphs);
175         return 0;
176     }
177
178     font = rfx_calloc(sizeof(gfxfont_t));
179     //font->style =  ((face->style_flags&FT_STYLE_FLAG_ITALIC)?FONT_STYLE_ITALIC:0) |((face->style_flags&FT_STYLE_FLAG_BOLD)?FONT_STYLE_BOLD:0);
180     //font->ascent = abs(face->ascender)*FT_SCALE*loadfont_scale*20/FT_SUBPIXELS/2; //face->bbox.xMin;
181     //font->descent = abs(face->descender)*FT_SCALE*loadfont_scale*20/FT_SUBPIXELS/2; //face->bbox.xMax;
182     //font->leading = font->layout->ascent + font->layout->descent;
183     //font->encoding = FONT_ENCODING_UNICODE;
184     font->max_unicode = 0;
185     
186     font->glyphs = rfx_calloc(face->num_glyphs*sizeof(gfxglyph_t));
187     glyph2unicode = rfx_calloc(face->num_glyphs*sizeof(int));
188     glyph2glyph = rfx_calloc(face->num_glyphs*sizeof(int));
189
190     if(FT_HAS_GLYPH_NAMES(face)) {
191         //font->glyphnames = rfx_calloc(face->num_glyphs*sizeof(char*));
192     }
193
194     /*name = FT_Get_Postscript_Name(face);
195     if(name && *name)
196         font->name = strdup(name);*/
197
198     while(1) 
199     {
200         charcode = FT_Get_First_Char(face, &gindex);
201         while(gindex != 0)
202         {
203             if(gindex >= 0 && gindex<face->num_glyphs) {
204                 if(!glyph2unicode[gindex]) {
205                     glyph2unicode[gindex] = charcode;
206                     if(charcode + 1 > font->max_unicode) {
207                         font->max_unicode = charcode + 1;
208                     }
209                 }
210             }
211             charcode = FT_Get_Next_Char(face, charcode, &gindex);
212         }
213
214         /* if we didn't find a single encoding character, try
215            the font's charmaps instead. That usually means that
216            the encoding is no longer unicode. 
217            TODO: find a way to convert the encoding to unicode
218          */
219         if(font->max_unicode == 0 && charmap < face->num_charmaps - 1) {
220             charmap++;
221             FT_Set_Charmap(face, face->charmaps[charmap]);
222             //font->encoding = 0;//anything but unicode FIXME
223             isunicode = 0;
224         } else 
225             break;
226     }
227
228     /* TODO: if isunicode is 1, we now need to permutate the character
229              order so that each character is at it's encoding position */
230
231     if(full_unicode)
232         font->max_unicode = 65535;
233     
234     font->unicode2glyph = rfx_calloc(font->max_unicode*sizeof(int));
235     
236     for(t=0;t<font->max_unicode;t++) {
237         int g = FT_Get_Char_Index(face, t);
238         if(!g || g>=face->num_glyphs)
239             g = -1;
240         font->unicode2glyph[t] = g;
241         if(g>=0) {
242             max_unicode = t+1;
243             if(!glyph2unicode[g]) {
244                 glyph2unicode[g] = t;
245             }
246         }
247     }
248     font->max_unicode = max_unicode;
249
250     font->num_glyphs = 0;
251
252     for(t=0; t < face->num_glyphs; t++) {
253         FT_Glyph glyph;
254         FT_BBox bbox;
255         FT_Matrix matrix;
256         char name[128];
257         gfxdrawer_t draw;
258         int ret;
259         char hasname = 0;
260         name[0]=0;
261         if(FT_HAS_GLYPH_NAMES(face)) {
262             error = FT_Get_Glyph_Name(face, t, name, 127);
263             if(!error && name[0] && !strstr(name, "notdef")) {
264                 font->glyphs[font->num_glyphs].name = strdup(name);
265                 hasname = 1;
266             }
267         }
268         if(!glyph2unicode[t] && !hasname && skip_unused) {
269             continue;
270         }
271         error = FT_Load_Glyph(face, t, FT_LOAD_NO_BITMAP);
272         if(error) {
273             fprintf(stderr, "Couldn't load glyph %d, error:%d\n", t, error);
274             continue;
275         }
276         error = FT_Get_Glyph(face->glyph, &glyph);
277         if(error) {
278             fprintf(stderr, "Couldn't get glyph %d, error:%d\n", t, error);
279             continue;
280         }
281
282         FT_Glyph_Get_CBox(glyph, ft_glyph_bbox_unscaled, &bbox);
283         bbox.yMin = -bbox.yMin;
284         bbox.yMax = -bbox.yMax;
285         if(bbox.xMax < bbox.xMin) {
286             // swap
287             bbox.xMax ^= bbox.xMin;
288             bbox.xMin ^= bbox.xMax;
289             bbox.xMax ^= bbox.xMin;
290         }
291         if(bbox.yMax < bbox.yMin) {
292             // swap
293             bbox.yMax ^= bbox.yMin;
294             bbox.yMin ^= bbox.yMax;
295             bbox.yMax ^= bbox.yMin;
296         }
297
298         gfxdrawer_target_gfxline(&draw);
299
300         //error = FT_Outline_Decompose(&face->glyph->outline, &outline_functions, &draw);
301         error = FT_Outline_Decompose(&face->glyph->outline, &outline_functions, &draw);
302         
303         if(error) {
304             fprintf(stderr, "Couldn't decompose glyph %d\n", t);
305             gfxline_free((gfxline_t*)draw.result(&draw));
306             continue;
307         }
308
309 #if 0
310         if(bbox.xMin > 0) {
311             font->glyph[font->num_glyphs].advance = (bbox.xMax*20*FT_SCALE)/FT_SUBPIXELS;
312         } else {
313             font->glyph[font->num_glyphs].advance = ((bbox.xMax - bbox.xMin)*20*FT_SCALE)/FT_SUBPIXELS;
314         }
315 #else
316         font->glyphs[font->num_glyphs].advance = glyph->advance.x*20/65536;
317 #endif
318         
319         font->glyphs[font->num_glyphs].line = (gfxline_t*)draw.result(&draw);
320         
321         /*font->glyphs[font->num_glyphs].bbox.xmin = (bbox.xMin*FT_SCALE*20)/FT_SUBPIXELS;
322         font->glyphs[font->num_glyphs].bbox.ymin = (bbox.yMin*FT_SCALE*20)/FT_SUBPIXELS;
323         font->glyphs[font->num_glyphs].bbox.xmax = (bbox.xMax*FT_SCALE*20)/FT_SUBPIXELS;
324         font->glyphs[font->num_glyphs].bbox.ymax = (bbox.yMax*FT_SCALE*20)/FT_SUBPIXELS;*/
325
326         FT_Done_Glyph(glyph);
327         font->glyphs[font->num_glyphs].unicode = glyph2unicode[t];
328         glyph2glyph[t] = font->num_glyphs;
329         font->num_glyphs++;
330     }
331     /* notice: if skip_unused is true, font->glyph2unicode, font->glyphnames and font->layout->bounds will 
332                have more memory allocated than just font->num_glyphs, but only the first font->numchars 
333                are used/valid */
334
335     for(t=0;t<font->max_unicode;t++) {
336         if(font->unicode2glyph[t]>=0) {
337             font->unicode2glyph[t] = glyph2glyph[font->unicode2glyph[t]];
338         }
339     }
340     rfx_free(glyph2glyph);
341     rfx_free(glyph2unicode);
342
343     FT_Done_Face(face);
344     FT_Done_FreeType(ftlibrary);ftlibrary=0;
345   
346     if(!isunicode && font->num_glyphs>0) {
347         /* if the encoding isn't unicode, remap the font
348            so that the encoding equals the char position, and
349            remove the unicode table */
350         int t;
351         gfxglyph_t*newglyphs = rfx_calloc(font->max_unicode*sizeof(gfxglyph_t));
352
353         for(t=0;t<max_unicode;t++) {
354             int c = font->unicode2glyph[t];
355             if(c>=font->num_glyphs || c<0)
356                 c = 0;
357             newglyphs[t] = cloneGlyph(&font->glyphs[c]);
358             newglyphs[t].unicode = -1;
359         }
360         for(t=0;t<font->num_glyphs;t++) {
361             glyph_clear(&font->glyphs[t]);
362         }
363         free(font->glyphs);
364         font->glyphs = newglyphs;
365         font->num_glyphs = font->max_unicode;
366
367         free(font->unicode2glyph);font->unicode2glyph = 0;
368         font->max_unicode = 0;
369     }
370
371     if(font->unicode2glyph) {
372         int t;
373         int bad = 0;
374         /* check whether the Unicode indices look o.k.
375            If they don't, disable the unicode lookup by setting
376            the unicode map to -1 everywhere */
377         for(t=0;t<font->num_glyphs;t++) {
378             int c = font->glyphs[t].unicode;
379             gfxline_t* line = font->glyphs[t].line;
380             if(c && c < 32 && (line && line->next && line->next->next)) {
381                 // the character maps into the unicode control character range
382                 // between 0001-001f. Yet it is not empty. Treat the one
383                 // mapping as broken, and look how many of those we find.
384                 bad ++;
385             }
386         }
387         if(bad>5) {
388             free(font->unicode2glyph);font->unicode2glyph = 0;
389             font->max_unicode = 0;
390             for(t=0;t<font->num_glyphs;t++) {
391                 font->glyphs[t].unicode = -1;
392             }
393         }
394     }
395
396     return font;
397 }
398 #else
399
400 gfxfont_t* gfxfont_load(char*filename)
401 {
402     fprintf(stderr, "No freetype support compiled in! Not able to load %s\n", filename);
403 }
404
405 #endif
406