initial revision
[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 gfxline_t * clonePath(gfxline_t*line)
108 {
109     gfxline_t*dest = 0;
110     gfxline_t*pos = 0;
111     while(line) {
112         gfxline_t*n = rfx_calloc(sizeof(gfxline_t));
113         *n = *line;
114         n->next = 0;
115         if(!pos) {
116             dest = pos = n;
117         } else {
118             pos->next = n;
119             pos = n;
120         }
121         line = line->next;
122     }
123     return dest;
124 }
125
126 static gfxglyph_t cloneGlyph(gfxglyph_t*src)
127 {
128     gfxglyph_t dest;
129     memset(&dest, 0, sizeof(dest));
130     if(src->name)
131         dest.name = strdup(src->name);
132     dest.advance = src->advance;
133     dest.unicode = src->unicode;
134     dest.line = clonePath(src->line);
135     return dest;
136 }
137
138 static void glyph_clear(gfxglyph_t*g)
139 {
140     gfxline_t*line;
141     if(g->name) {
142         free(g->name); g->name = 0;
143     }
144     gfxline_free(g->line);
145 }
146
147 gfxfont_t* gfxfont_load(char*filename)
148 {
149     FT_Face face;
150     FT_Error error;
151     const char* name = 0;
152     FT_ULong charcode;
153     FT_UInt gindex;
154     gfxfont_t* font;
155     int t;
156     int*glyph2glyph = 0;
157     int*glyph2unicode = 0;
158     FT_Size size;
159     int max_unicode = 0;
160     int charmap = -1;
161     int isunicode = 1;
162    
163     if(ftlibrary == 0) {
164         if(FT_Init_FreeType(&ftlibrary)) {
165             fprintf(stderr, "Couldn't init freetype library!\n");
166             exit(1);
167         }
168     }
169     error = FT_New_Face(ftlibrary, filename, 0, &face);
170     FT_Set_Pixel_Sizes (face, 16*loadfont_scale, 16*loadfont_scale);
171
172     if(error) {
173         fprintf(stderr, "Couldn't load file %s- not a TTF file?\n", filename);
174         return 0;
175     }
176     if(face->num_glyphs <= 0) {
177         fprintf(stderr, "File %s contains %d glyphs\n", face->num_glyphs);
178         return 0;
179     }
180
181     font = rfx_calloc(sizeof(gfxfont_t));
182     //font->style =  ((face->style_flags&FT_STYLE_FLAG_ITALIC)?FONT_STYLE_ITALIC:0) |((face->style_flags&FT_STYLE_FLAG_BOLD)?FONT_STYLE_BOLD:0);
183     //font->ascent = abs(face->ascender)*FT_SCALE*loadfont_scale*20/FT_SUBPIXELS/2; //face->bbox.xMin;
184     //font->descent = abs(face->descender)*FT_SCALE*loadfont_scale*20/FT_SUBPIXELS/2; //face->bbox.xMax;
185     //font->leading = font->layout->ascent + font->layout->descent;
186     //font->encoding = FONT_ENCODING_UNICODE;
187     font->max_unicode = 0;
188     
189     font->glyphs = rfx_calloc(face->num_glyphs*sizeof(gfxglyph_t));
190     glyph2unicode = rfx_calloc(face->num_glyphs*sizeof(int));
191     glyph2glyph = rfx_calloc(face->num_glyphs*sizeof(int));
192
193     if(FT_HAS_GLYPH_NAMES(face)) {
194         //font->glyphnames = rfx_calloc(face->num_glyphs*sizeof(char*));
195     }
196
197     name = FT_Get_Postscript_Name(face);
198     if(name && *name)
199         font->name = strdup(name);
200
201     while(1) 
202     {
203         charcode = FT_Get_First_Char(face, &gindex);
204         while(gindex != 0)
205         {
206             if(gindex >= 0 && gindex<face->num_glyphs) {
207                 if(!glyph2unicode[gindex]) {
208                     glyph2unicode[gindex] = charcode;
209                     if(charcode + 1 > font->max_unicode) {
210                         font->max_unicode = charcode + 1;
211                     }
212                 }
213             }
214             charcode = FT_Get_Next_Char(face, charcode, &gindex);
215         }
216
217         /* if we didn't find a single encoding character, try
218            the font's charmaps instead. That usually means that
219            the encoding is no longer unicode. 
220            TODO: find a way to convert the encoding to unicode
221          */
222         if(font->max_unicode == 0 && charmap < face->num_charmaps - 1) {
223             charmap++;
224             FT_Set_Charmap(face, face->charmaps[charmap]);
225             //font->encoding = 0;//anything but unicode FIXME
226             isunicode = 0;
227         } else 
228             break;
229     }
230
231     /* TODO: if isunicode is 1, we now need to permutate the character
232              order so that each character is at it's encoding position */
233
234     if(full_unicode)
235         font->max_unicode = 65535;
236     
237     font->unicode2glyph = rfx_calloc(font->max_unicode*sizeof(int));
238     glyph2unicode = (int*)rfx_calloc(face->num_glyphs*sizeof(int));
239     
240     for(t=0;t<font->max_unicode;t++) {
241         int g = FT_Get_Char_Index(face, t);
242         if(!g || g>=face->num_glyphs)
243             g = -1;
244         font->unicode2glyph[t] = g;
245         if(g>=0) {
246             max_unicode = t+1;
247             if(!glyph2unicode[g]) {
248                 glyph2unicode[g] = t;
249             }
250         }
251     }
252     font->max_unicode = max_unicode;
253
254     font->num_glyphs = 0;
255
256     glyph2glyph = (int*)rfx_calloc(face->num_glyphs*sizeof(int));
257
258     for(t=0; t < face->num_glyphs; t++) {
259         FT_Glyph glyph;
260         FT_BBox bbox;
261         FT_Matrix matrix;
262         char name[128];
263         gfxdrawer_t draw;
264         int ret;
265         char hasname = 0;
266         name[0]=0;
267         if(FT_HAS_GLYPH_NAMES(face)) {
268             error = FT_Get_Glyph_Name(face, t, name, 127);
269             if(!error && name[0] && !strstr(name, "notdef")) {
270                 font->glyphs[font->num_glyphs].name = strdup(name);
271                 hasname = 1;
272             }
273         }
274         if(!glyph2unicode[t] && !hasname && skip_unused) {
275             continue;
276         }
277         error = FT_Load_Glyph(face, t, FT_LOAD_NO_BITMAP);
278         if(error) {
279             fprintf(stderr, "Couldn't load glyph %d, error:%d\n", t, error);
280             continue;
281         }
282         error = FT_Get_Glyph(face->glyph, &glyph);
283         if(error) {
284             fprintf(stderr, "Couldn't get glyph %d, error:%d\n", t, error);
285             continue;
286         }
287
288         FT_Glyph_Get_CBox(glyph, ft_glyph_bbox_unscaled, &bbox);
289         bbox.yMin = -bbox.yMin;
290         bbox.yMax = -bbox.yMax;
291         if(bbox.xMax < bbox.xMin) {
292             // swap
293             bbox.xMax ^= bbox.xMin;
294             bbox.xMin ^= bbox.xMax;
295             bbox.xMax ^= bbox.xMin;
296         }
297         if(bbox.yMax < bbox.yMin) {
298             // swap
299             bbox.yMax ^= bbox.yMin;
300             bbox.yMin ^= bbox.yMax;
301             bbox.yMax ^= bbox.yMin;
302         }
303
304         gfxdrawer_target_gfxline(&draw);
305
306         //error = FT_Outline_Decompose(&face->glyph->outline, &outline_functions, &draw);
307         error = FT_Outline_Decompose(&face->glyph->outline, &outline_functions, &draw);
308         
309         if(error) {
310             fprintf(stderr, "Couldn't decompose glyph %d\n", t);
311             gfxline_free((gfxline_t*)draw.result(&draw));
312             continue;
313         }
314
315 #if 0
316         if(bbox.xMin > 0) {
317             font->glyph[font->num_glyphs].advance = (bbox.xMax*20*FT_SCALE)/FT_SUBPIXELS;
318         } else {
319             font->glyph[font->num_glyphs].advance = ((bbox.xMax - bbox.xMin)*20*FT_SCALE)/FT_SUBPIXELS;
320         }
321 #else
322         font->glyphs[font->num_glyphs].advance = glyph->advance.x*20/65536;
323 #endif
324         
325         font->glyphs[font->num_glyphs].line = (gfxline_t*)draw.result(&draw);
326         
327         /*font->glyphs[font->num_glyphs].bbox.xmin = (bbox.xMin*FT_SCALE*20)/FT_SUBPIXELS;
328         font->glyphs[font->num_glyphs].bbox.ymin = (bbox.yMin*FT_SCALE*20)/FT_SUBPIXELS;
329         font->glyphs[font->num_glyphs].bbox.xmax = (bbox.xMax*FT_SCALE*20)/FT_SUBPIXELS;
330         font->glyphs[font->num_glyphs].bbox.ymax = (bbox.yMax*FT_SCALE*20)/FT_SUBPIXELS;*/
331
332         FT_Done_Glyph(glyph);
333         font->glyphs[font->num_glyphs].unicode = glyph2unicode[t];
334         glyph2glyph[t] = font->num_glyphs;
335         font->num_glyphs++;
336     }
337     /* notice: if skip_unused is true, font->glyph2unicode, font->glyphnames and font->layout->bounds will 
338                have more memory allocated than just font->num_glyphs, but only the first font->numchars 
339                are used/valid */
340
341     for(t=0;t<font->max_unicode;t++) {
342         if(font->unicode2glyph[t]>=0) {
343             font->unicode2glyph[t] = glyph2glyph[font->unicode2glyph[t]];
344         }
345     }
346     rfx_free(glyph2glyph);
347     rfx_free(glyph2unicode);
348
349     FT_Done_Face(face);
350     FT_Done_FreeType(ftlibrary);ftlibrary=0;
351    
352     if(!isunicode && font->num_glyphs>0) {
353         /* if the encoding isn't unicode, remap the font
354            so that the encoding equals the char position, and
355            remove the unicode table */
356         int t;
357         gfxglyph_t*newglyphs = rfx_calloc(font->max_unicode*sizeof(gfxglyph_t));
358
359         for(t=0;t<max_unicode;t++) {
360             int c = font->unicode2glyph[t];
361             if(c>=font->num_glyphs || c<0)
362                 c = 0;
363             newglyphs[t] = cloneGlyph(&font->glyphs[c]);
364             newglyphs[t].unicode = -1;
365         }
366         for(t=0;t<font->num_glyphs;t++) {
367             glyph_clear(&font->glyphs[t]);
368         }
369         free(font->glyphs);
370         font->glyphs = newglyphs;
371         font->num_glyphs = font->max_unicode;
372
373         free(font->unicode2glyph);font->unicode2glyph = 0;
374         font->max_unicode = 0;
375     }
376
377     if(font->unicode2glyph) {
378         int t;
379         int bad = 0;
380         /* check whether the Unicode indices look o.k.
381            If they don't, disable the unicode lookup by setting
382            the unicode map to -1 everywhere */
383         for(t=0;t<font->num_glyphs;t++) {
384             int c = font->glyphs[t].unicode;
385             gfxline_t* line = font->glyphs[t].line;
386             if(c && c < 32 && (line && line->next && line->next->next)) {
387                 // the character maps into the unicode control character range
388                 // between 0001-001f. Yet it is not empty. Treat the one
389                 // mapping as broken, and look how many of those we find.
390                 bad ++;
391             }
392         }
393         if(bad>5) {
394             free(font->unicode2glyph);font->unicode2glyph = 0;
395             font->max_unicode = 0;
396             for(t=0;t<font->num_glyphs;t++) {
397                 font->glyphs[t].unicode = -1;
398             }
399         }
400     }
401
402     return font;
403 }
404 #else
405
406 gfxfont_t* gfxfont_load(char*filename)
407 {
408     fprintf(stderr, "No freetype support compiled in! Not able to load %s\n", filename);
409 }
410
411 #endif
412