fixed "align to subpixels" outline quality problem.
[swftools.git] / lib / modules / swffont.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 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 static int loadfont_scale = 64;
25 static int skip_unused = 1;
26
27 void swf_SetLoadFontParameters(int _scale, int _skip_unused)
28 {
29     loadfont_scale = _scale;
30     skip_unused = _skip_unused;
31 }
32
33 #ifdef HAVE_FREETYPE
34
35 #include <freetype/freetype.h>
36 #include <freetype/ftglyph.h>
37 #include <freetype/ftsizes.h>
38 #include <freetype/ftsnames.h>
39 #include <freetype/ttnameid.h>
40 #include <freetype/ftoutln.h>
41
42 #define FT_SCALE loadfont_scale
43 #define FT_SUBPIXELS 64
44
45 static int ft_move_to(FT_Vector* _to, void* user) 
46 {
47     drawer_t* draw = (drawer_t*)user;
48     FPOINT to;
49     to.x = _to->x/(float)FT_SUBPIXELS;
50     to.y = -_to->y/(float)FT_SUBPIXELS;
51     draw->moveTo(draw, &to);
52     return 0;
53 }
54 static int ft_line_to(FT_Vector* _to, void* user) 
55 {
56     drawer_t* draw = (drawer_t*)user;
57     FPOINT to;
58     to.x = _to->x/(float)FT_SUBPIXELS;
59     to.y = -_to->y/(float)FT_SUBPIXELS;
60     draw->lineTo(draw, &to);
61     return 0;
62 }
63 static int ft_cubic_to(FT_Vector* _c1, FT_Vector* _c2, FT_Vector* _to, void* user)
64 {
65     drawer_t* draw = (drawer_t*)user;
66     FPOINT c1,c2,to;
67     to.x = _to->x/(float)FT_SUBPIXELS;
68     to.y = -_to->y/(float)FT_SUBPIXELS;
69     c1.x = _c1->x/(float)FT_SUBPIXELS;
70     c1.y = -_c1->y/(float)FT_SUBPIXELS;
71     c2.x = _c2->x/(float)FT_SUBPIXELS;
72     c2.y = -_c2->y/(float)FT_SUBPIXELS;
73     draw_cubicTo(draw, &c1, &c2, &to);
74     return 0;
75 }
76 static int ft_conic_to(FT_Vector* _c, FT_Vector* _to, void* user) 
77 {
78     drawer_t* draw = (drawer_t*)user;
79     FPOINT c,to;
80     to.x = _to->x/(float)FT_SUBPIXELS;
81     to.y = -_to->y/(float)FT_SUBPIXELS;
82     c.x = _c->x/(float)FT_SUBPIXELS;
83     c.y = -_c->y/(float)FT_SUBPIXELS;
84     draw_conicTo(draw, &c, &to);
85     return 0;
86 }
87 static FT_Outline_Funcs outline_functions =
88 {
89   ft_move_to,
90   ft_line_to,
91   ft_conic_to,
92   ft_cubic_to,
93   0,0
94 };
95
96 static FT_Library ftlibrary = 0;
97
98 SWFFONT* swf_LoadTrueTypeFont(char*filename)
99 {
100     FT_Face face;
101     FT_Error error;
102     const char* name = 0;
103     FT_ULong charcode;
104     FT_UInt gindex;
105     SWFFONT* font;
106     int t;
107     int*glyph2glyph;
108     FT_Size size;
109    
110     if(ftlibrary == 0) {
111         if(FT_Init_FreeType(&ftlibrary)) {
112             fprintf(stderr, "Couldn't init freetype library!\n");
113             exit(1);
114         }
115     }
116     error = FT_New_Face(ftlibrary, filename, 0, &face);
117     FT_Set_Pixel_Sizes (face, 16*FT_SCALE, 16*FT_SCALE);
118
119     if(error) {
120         fprintf(stderr, "Couldn't load file %s- not a TTF file?\n", filename);
121         return 0;
122     }
123     if(face->num_glyphs <= 0) {
124         fprintf(stderr, "File %s contains %d glyphs\n", face->num_glyphs);
125         return 0;
126     }
127
128     font = malloc(sizeof(SWFFONT));
129     memset(font, 0, sizeof(SWFFONT));
130     font->id = -1;
131     font->version = 2;
132     font->layout = malloc(sizeof(SWFLAYOUT));
133     memset(font->layout, 0, sizeof(SWFLAYOUT));
134     font->layout->bounds = malloc(face->num_glyphs*sizeof(SRECT));
135     font->style =  ((face->style_flags&FT_STYLE_FLAG_ITALIC)?FONT_STYLE_ITALIC:0)
136                   |((face->style_flags&FT_STYLE_FLAG_BOLD)?FONT_STYLE_BOLD:0);
137     font->encoding = FONT_ENCODING_UNICODE;
138     font->glyph2ascii = malloc(face->num_glyphs*sizeof(U16));
139     memset(font->glyph2ascii, 0, face->num_glyphs*sizeof(U16));
140     font->maxascii = 0;
141     font->glyph = malloc(face->num_glyphs*sizeof(SWFGLYPH));
142     memset(font->glyph, 0, face->num_glyphs*sizeof(SWFGLYPH));
143     if(FT_HAS_GLYPH_NAMES(face)) {
144         font->glyphnames = malloc(face->num_glyphs*sizeof(char*));
145         memset(font->glyphnames,0,face->num_glyphs*sizeof(char*));
146     }
147
148     font->layout->ascent = face->ascender*20/FT_SUBPIXELS; //face->bbox.xMin;
149     font->layout->descent = abs(face->descender)*20/FT_SUBPIXELS; //face->bbox.xMax;
150     font->layout->leading = abs(face->bbox.yMin - face->bbox.yMax); //-face->bbox.xMin*20/FT_SUBPIXELS;
151     font->layout->kerningcount = 0;
152     
153     name = FT_Get_Postscript_Name(face);
154     if(name && *name)
155         font->name = (U8*)strdup(name);
156
157 /*    // Map Glyphs to Unicode, version 1 (quick and dirty):
158     int t;
159     for(t=0;t<65536;t++) {
160         int index = FT_Get_Char_Index(face, t);
161         if(index>=0 && index<face->num_glyphs) {
162             if(font->glyph2ascii[index]<0)
163                 font->glyph2ascii[index] = t;
164         }
165     }*/
166     
167     // Map Glyphs to Unicode, version 2 (much nicer):
168     // (The third way would be the AGL algorithm, as proposed
169     //  by Werner Lemberg on freetype@freetype.org)
170
171     charcode = FT_Get_First_Char(face, &gindex);
172     while(gindex != 0)
173     {
174         if(gindex >= 0 && gindex<face->num_glyphs) {
175             if(!font->glyph2ascii[gindex]) {
176                 font->glyph2ascii[gindex] = charcode;
177                 if(charcode + 1 > font->maxascii) {
178                     font->maxascii = charcode + 1;
179                 }
180             }
181         }
182         charcode = FT_Get_Next_Char(face, charcode, &gindex);
183     }
184     
185     font->ascii2glyph = malloc(font->maxascii*sizeof(int));
186     
187     for(t=0;t<font->maxascii;t++) {
188         int g = FT_Get_Char_Index(face, t);
189         if(!g || g>=face->num_glyphs)
190             g = -1;
191         font->ascii2glyph[t] = g;
192         if(g>=0) {
193             if(!font->glyph2ascii[g]) {
194                 font->glyph2ascii[g] = t;
195             }
196         }
197     }
198
199     font->numchars = 0;
200
201     glyph2glyph = (int*)malloc(face->num_glyphs*sizeof(int));
202
203     for(t=0; t < face->num_glyphs; t++) {
204         FT_Glyph glyph;
205         FT_BBox bbox;
206         FT_Matrix matrix;
207         char name[128];
208         drawer_t draw;
209         int ret;
210         char hasname = 0;
211         name[0]=0;
212         if(FT_HAS_GLYPH_NAMES(face)) {
213             error = FT_Get_Glyph_Name(face, t, name, 127);
214             if(!error && name[0] && !strstr(name, "notdef")) {
215                 font->glyphnames[font->numchars] = strdup(name);
216                 hasname = 1;
217             }
218         }
219         if(!font->glyph2ascii[t] && !hasname && skip_unused) {
220             continue;
221         }
222         error = FT_Load_Glyph(face, t, FT_LOAD_NO_BITMAP);
223         if(error) {
224             fprintf(stderr, "Couldn't load glyph %d, error:%d\n", t, error);
225             continue;
226         }
227         error = FT_Get_Glyph(face->glyph, &glyph);
228         if(error) {
229             fprintf(stderr, "Couldn't get glyph %d, error:%d\n", t, error);
230             continue;
231         }
232
233         FT_Glyph_Get_CBox(glyph, ft_glyph_bbox_unscaled, &bbox);
234         bbox.yMin = -bbox.yMin;
235         bbox.yMax = -bbox.yMax;
236         if(bbox.xMax < bbox.xMin) {
237             // swap
238             bbox.xMax ^= bbox.xMin;
239             bbox.xMin ^= bbox.xMax;
240             bbox.xMax ^= bbox.xMin;
241         }
242         if(bbox.yMax < bbox.yMin) {
243             // swap
244             bbox.yMax ^= bbox.yMin;
245             bbox.yMin ^= bbox.yMax;
246             bbox.yMax ^= bbox.yMin;
247         }
248
249         swf_Shape01DrawerInit(&draw, 0);
250
251         //error = FT_Outline_Decompose(&face->glyph->outline, &outline_functions, &draw);
252         error = FT_Outline_Decompose(&face->glyph->outline, &outline_functions, &draw);
253         draw.finish(&draw);
254         
255         if(error) {
256             fprintf(stderr, "Couldn't decompose glyph %d\n", t);
257             draw.dealloc(&draw);
258             continue;
259         }
260
261         draw.finish(&draw);
262
263 #if 0
264         if(bbox.xMin > 0) {
265             font->glyph[font->numchars].advance = (bbox.xMax*FT_SCALE)/FT_SUBPIXELS;
266         } else {
267             font->glyph[font->numchars].advance = ((bbox.xMax - bbox.xMin)*FT_SCALE)/FT_SUBPIXELS;
268         }
269 #else
270         font->glyph[font->numchars].advance = glyph->advance.x*20/65536;
271 #endif
272         
273         font->glyph[font->numchars].shape = swf_ShapeDrawerToShape(&draw);
274         
275         font->layout->bounds[font->numchars].xmin = (bbox.xMin*FT_SCALE*20)/FT_SUBPIXELS;
276         font->layout->bounds[font->numchars].ymin = (bbox.yMin*FT_SCALE*20)/FT_SUBPIXELS;
277         font->layout->bounds[font->numchars].xmax = (bbox.xMax*FT_SCALE*20)/FT_SUBPIXELS;
278         font->layout->bounds[font->numchars].ymax = (bbox.yMax*FT_SCALE*20)/FT_SUBPIXELS;
279
280         draw.dealloc(&draw);
281
282         FT_Done_Glyph(glyph);
283         font->glyph2ascii[font->numchars] = font->glyph2ascii[t];
284         glyph2glyph[t] = font->numchars;
285         font->numchars++;
286     }
287     /* notice: if skip_unused is true, font->glyph2ascii, font->glyphnames and font->layout->bounds will 
288                have more memory allocated than just font->numchars, but only the first font->numchars 
289                are used/valid */
290
291     for(t=0;t<font->maxascii;t++) {
292         if(font->ascii2glyph[t]>=0) {
293             font->ascii2glyph[t] = glyph2glyph[font->ascii2glyph[t]];
294         }
295     }
296     free(glyph2glyph);
297
298     FT_Done_Face(face);
299     FT_Done_FreeType(ftlibrary);ftlibrary=0;
300
301     return font;
302 }
303 #else  //HAVE_FREETYPE
304
305 SWFFONT* swf_LoadTrueTypeFont(char*filename)
306 {
307     fprintf(stderr, "Warning: no freetype library- not able to load %s\n", filename);
308     return 0;
309 }
310
311 #endif
312
313 #ifdef HAVE_T1LIB
314
315 #include <t1lib.h>
316
317 static int t1lib_initialized = 0;
318
319 static int counter = 0;
320
321 SWFFONT* swf_LoadT1Font(char*filename)
322 {
323     SWFFONT * font;
324     int nr;
325     float angle,underline;
326     char*fontname,*fullname,*familyname;
327     BBox bbox;
328     int s,num;
329     char**charnames;
330     char**charname;
331     char*encoding[256];
332     int c;
333     int t;
334
335     if(!t1lib_initialized) {
336         T1_SetBitmapPad(16);
337         if ((T1_InitLib(NO_LOGFILE)==NULL)){
338             fprintf(stderr, "Initialization of t1lib failed\n");
339             return 0;
340         }
341         t1lib_initialized = 1;
342     }
343     nr = T1_AddFont(filename);
344     T1_LoadFont(nr);
345
346     charnames = T1_GetAllCharNames(nr);
347     if(!charnames) {
348         fprintf(stderr, "No Charnames record- not a Type1 Font?\n");
349         return 0;
350     }
351
352     angle = T1_GetItalicAngle(nr);
353     fontname = T1_GetFontName(nr);
354     fullname = T1_GetFullName(nr);
355     familyname = T1_GetFamilyName(nr);
356     underline = T1_GetUnderlinePosition(nr);
357     bbox = T1_GetFontBBox(nr);
358
359     font = (SWFFONT*)malloc(sizeof(SWFFONT));
360     memset(font, 0, sizeof(SWFFONT));
361
362     font->version = 2;
363     if(fontname) 
364         font->name = (U8*)strdup(fontname);
365     else 
366         font->name = 0;
367     font->layout = (SWFLAYOUT*)malloc(sizeof(SWFLAYOUT));
368     memset(font->layout, 0, sizeof(SWFLAYOUT));
369
370     num = 0;
371     charname = charnames;
372     while(*charname) {
373         charname++;
374         if(num<256) {
375             if(*charname) encoding[num] = strdup(*charname);
376             else          encoding[num] = strdup(".notdef");
377         }
378         num++;
379     }
380     for(t=num;t<256;t++)
381         encoding[t] = strdup(".notdef");
382     
383     //T1_ReencodeFont(nr, encoding);
384
385     font->maxascii = num;
386     font->numchars = num;
387     
388     font->style = (/*bold*/0?FONT_STYLE_BOLD:0) + (angle>0.05?FONT_STYLE_ITALIC:0);
389
390     font->glyph = (SWFGLYPH*)malloc(num*sizeof(SWFGLYPH));
391     memset(font->glyph, 0, num*sizeof(SWFGLYPH));
392     font->glyph2ascii = (U16*)malloc(num*sizeof(U16));
393     memset(font->glyph2ascii, 0, num*sizeof(U16));
394     font->ascii2glyph = (int*)malloc(font->maxascii*sizeof(int));
395     memset(font->ascii2glyph, -1, font->maxascii*sizeof(int));
396     font->layout->ascent = (U16)(underline - bbox.lly);
397     font->layout->descent = (U16)(bbox.ury - underline);
398     font->layout->leading = (U16)(font->layout->ascent - 
399                              font->layout->descent -
400                              (bbox.lly - bbox.ury));
401     font->layout->bounds = (SRECT*)malloc(sizeof(SRECT)*num);
402     memset(font->layout->bounds, 0, sizeof(SRECT)*num);
403     font->layout->kerningcount = 0;
404     font->layout->kerning = 0;
405     font->glyphnames = malloc(num*sizeof(char*));
406     memset(font->glyphnames, 0, num*sizeof(char*));
407   
408     num = 0;
409
410     charname = charnames;
411     for(c=0;c<font->numchars;c++) {
412         drawer_t draw;
413         SRECT bbox;
414         T1_OUTLINE * outline;
415         FPOINT pos,last;
416         int firstx;
417         
418         outline = T1_GetCharOutline(nr, c, 100.0, 0);
419         firstx = outline->dest.x/0xffff;
420
421         pos.x = 0;
422         pos.y = 0;
423         last = pos;
424         
425         font->glyphnames[c] = strdup(*charname);
426
427         if(c<font->maxascii)
428             font->ascii2glyph[c] = c;
429         font->glyph2ascii[c] = c;
430         
431         swf_Shape01DrawerInit(&draw, 0);
432
433         while(outline) {
434             pos.x += (outline->dest.x/(float)0xffff);
435             pos.y += (outline->dest.y/(float)0xffff);
436
437             if(outline->type == T1_PATHTYPE_MOVE) {
438                 draw.moveTo(&draw,&pos);
439             } else if(outline->type == T1_PATHTYPE_LINE) {
440                 draw.lineTo(&draw,&pos);
441             } else if(outline->type == T1_PATHTYPE_BEZIER) {
442                 T1_BEZIERSEGMENT*o2 = (T1_BEZIERSEGMENT*)outline;
443                 FPOINT b,c;
444                 b.x = o2->B.x/(float)0xffff+last.x;
445                 b.y = o2->B.y/(float)0xffff+last.y;
446                 c.x = o2->C.x/(float)0xffff+last.x;
447                 c.y = o2->C.y/(float)0xffff+last.y;
448                 draw_cubicTo(&draw,&b,&c,&pos);
449             } else {
450                 fprintf(stderr, "loadT1Font: unknown outline type:%d\n", outline->type);
451             }
452             last = pos;
453             outline = outline->link;
454             printf("(%f,%f) ", pos.x, pos.y);
455         }
456         printf("\n");
457         
458         draw.finish(&draw);
459
460         font->glyph[c].shape = swf_ShapeDrawerToShape(&draw);
461         bbox = swf_ShapeDrawerGetBBox(&draw);
462         draw.dealloc(&draw);
463             
464         font->layout->bounds[c] = bbox;
465         font->glyph[c].advance = bbox.xmax;
466         if(!font->glyph[c].advance) {
467             font->glyph[c].advance = firstx;
468         }
469         charname++;
470     }
471     T1_DeleteFont(nr);
472
473     for(t=0;t<256;t++)
474         free(encoding[t]);
475     return font;
476 }
477
478 #else
479
480 SWFFONT* swf_LoadT1Font(char*filename)
481 {
482     fprintf(stderr, "Warning: no t1lib- not able to load %s\n", filename);
483     return 0;
484 }
485
486 #endif
487
488 SWFFONT* swf_DummyFont()
489 {
490     SWFFONT*font = (SWFFONT*)malloc(sizeof(SWFFONT));
491     memset(font, 0, sizeof(SWFFONT));
492     return font;
493 }
494
495 static int isSWF(const char*filename)
496 {
497     FILE*fi = fopen(filename, "rb");
498     char a[8];
499     if(!fi) {
500         perror(filename);
501         return -1;
502     }
503     memset(a, 0, sizeof(a));
504     fread(a, 4, 1, fi);
505     fclose(fi);
506
507     if(!strncmp(a, "FWS", 3) || !strncmp(a, "CWS", 3)) {
508         return 1;
509     }
510     return 0;
511 }
512
513 SWFFONT* swf_LoadFont(char*filename)
514 {
515     int is_swf;
516     if(filename == 0)
517         return swf_DummyFont();
518     is_swf = isSWF(filename);
519     if(is_swf<0)
520         return 0;
521     if(is_swf) {
522         return swf_ReadFont(filename);
523     }
524
525 #if defined(HAVE_FREETYPE)
526     return swf_LoadTrueTypeFont(filename);
527 #elif defined(HAVE_T1LIB)
528     return swf_LoadT1Font(filename);
529 #else
530     fprintf(stderr, "Error: Neither T1lib nor FreeType support compiled in. Could not load %s\n", filename);
531     return 0;
532 #endif
533 }
534