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