fixed format warnings
[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, char flashtype)
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     int scale = flashtype?20:1;
146     FT_Set_Pixel_Sizes (face, 16*loadfont_scale*scale, 16*loadfont_scale*scale);
147
148     if(face->num_glyphs <= 0) {
149         fprintf(stderr, "File %s contains %d glyphs\n", filename, (int)face->num_glyphs);
150         return 0;
151     }
152
153     font = (SWFFONT*)rfx_calloc(sizeof(SWFFONT));
154     font->id = -1;
155     font->version = flashtype?3:2;
156
157     font->layout = (SWFLAYOUT*)rfx_calloc(sizeof(SWFLAYOUT));
158     font->layout->bounds = (SRECT*)rfx_calloc(face->num_glyphs*sizeof(SRECT));
159     font->style =  ((face->style_flags&FT_STYLE_FLAG_ITALIC)?FONT_STYLE_ITALIC:0)
160                   |((face->style_flags&FT_STYLE_FLAG_BOLD)?FONT_STYLE_BOLD:0);
161     font->encoding = FONT_ENCODING_UNICODE;
162     font->glyph2ascii = (U16*)rfx_calloc(face->num_glyphs*sizeof(U16));
163     font->maxascii = 0;
164     font->glyph = (SWFGLYPH*)rfx_calloc(face->num_glyphs*sizeof(SWFGLYPH));
165     if(FT_HAS_GLYPH_NAMES(face)) {
166         font->glyphnames = (char**)rfx_calloc(face->num_glyphs*sizeof(char*));
167     }
168     
169     font->layout->kerningcount = 0;
170     
171     name = face->family_name;
172     if(!(name && *name))
173         name = FT_Get_Postscript_Name(face);
174     if(name && *name)
175         font->name = (U8*)strdup(name);
176
177     while(1) 
178     {
179     /*    // Map Glyphs to Unicode, version 1 (quick and dirty):
180         int t;
181         for(t=0;t<65536;t++) {
182             int index = FT_Get_Char_Index(face, t);
183             if(index>=0 && index<face->num_glyphs) {
184                 if(font->glyph2ascii[index]<0)
185                     font->glyph2ascii[index] = t;
186             }
187         }*/
188         
189         // Map Glyphs to Unicode, version 2 (much nicer):
190         // (The third way would be the AGL algorithm, as proposed
191         //  by Werner Lemberg on freetype@freetype.org)
192
193         charcode = FT_Get_First_Char(face, &gindex);
194         while(gindex != 0)
195         {
196             if(gindex >= 0 && gindex<face->num_glyphs) {
197                 if(!font->glyph2ascii[gindex]) {
198                     font->glyph2ascii[gindex] = charcode;
199                     if(charcode + 1 > font->maxascii) {
200                         font->maxascii = charcode + 1;
201                     }
202                 }
203             }
204             charcode = FT_Get_Next_Char(face, charcode, &gindex);
205         }
206
207         /* if we didn't find a single encoding character, try
208            the font's charmaps instead. That usually means that
209            the encoding is no longer unicode. 
210            TODO: find a way to convert the encoding to unicode
211          */
212         if(font->maxascii == 0 && charmap < face->num_charmaps - 1) {
213             charmap++;
214             FT_Set_Charmap(face, face->charmaps[charmap]);
215             font->encoding = 0;//anything but unicode FIXME
216         } else 
217             break;
218     }
219
220     if(full_unicode)
221         font->maxascii = 65535;
222     
223     font->ascii2glyph = (int*)rfx_calloc(font->maxascii*sizeof(int));
224     
225     for(t=0;t<font->maxascii;t++) {
226         int g = FT_Get_Char_Index(face, t);
227         if(!g || g>=face->num_glyphs)
228             g = -1;
229         font->ascii2glyph[t] = g;
230         if(g>=0) {
231             max_unicode = t+1;
232             if(!font->glyph2ascii[g]) {
233                 font->glyph2ascii[g] = t;
234             }
235         }
236     }
237     font->maxascii = max_unicode;
238
239     font->numchars = 0;
240
241     glyph2glyph = (int*)rfx_calloc(face->num_glyphs*sizeof(int));
242
243     SRECT fontbbox = {0,0,0,0};
244
245     for(t=0; t < face->num_glyphs; t++) {
246         FT_Glyph glyph;
247         FT_BBox bbox;
248         char name[128];
249         drawer_t draw;
250         char hasname = 0;
251         name[0]=0;
252         if(FT_HAS_GLYPH_NAMES(face)) {
253             error = FT_Get_Glyph_Name(face, t, name, 127);
254             if(!error && name[0] && !strstr(name, "notdef")) {
255                 font->glyphnames[font->numchars] = strdup(name);
256                 hasname = 1;
257             }
258         }
259         if(!font->glyph2ascii[t] && !hasname && skip_unused) {
260             continue;
261         }
262         error = FT_Load_Glyph(face, t, FT_LOAD_NO_BITMAP);
263         if(error) {
264             //tends to happen with some pdfs
265             fprintf(stderr, "Warning: Glyph %d has return code %d\n", t, error);
266             glyph=0;
267             if(skip_unused) 
268                 continue;
269         } else {
270             error = FT_Get_Glyph(face->glyph, &glyph);
271             if(error) {
272                 fprintf(stderr, "Couldn't get glyph %d, error:%d\n", t, error);
273                 glyph=0;
274                 if(skip_unused) 
275                     continue;
276             }
277         }
278
279         if(glyph)
280             FT_Glyph_Get_CBox(glyph, ft_glyph_bbox_unscaled, &bbox);
281         else
282             memset(&bbox, 0, sizeof(bbox));
283
284         bbox.yMin = -bbox.yMin;
285         bbox.yMax = -bbox.yMax;
286         if(bbox.xMax < bbox.xMin) {
287             // swap
288             bbox.xMax ^= bbox.xMin;
289             bbox.xMin ^= bbox.xMax;
290             bbox.xMax ^= bbox.xMin;
291         }
292         if(bbox.yMax < bbox.yMin) {
293             // swap
294             bbox.yMax ^= bbox.yMin;
295             bbox.yMin ^= bbox.yMax;
296             bbox.yMax ^= bbox.yMin;
297         }
298
299         swf_Shape01DrawerInit(&draw, 0);
300
301         //error = FT_Outline_Decompose(&face->glyph->outline, &outline_functions, &draw);
302         if(glyph)
303             error = FT_Outline_Decompose(&face->glyph->outline, &outline_functions, &draw);
304         else
305             error = 0;
306         draw.finish(&draw);
307         
308         if(error) {
309             fprintf(stderr, "Couldn't decompose glyph %d\n", t);
310             draw.dealloc(&draw);
311             continue;
312         }
313
314 #if 0
315         if(bbox.xMin > 0) {
316             font->glyph[font->numchars].advance = (bbox.xMax*20*FT_SCALE)/FT_SUBPIXELS;
317         } else {
318             font->glyph[font->numchars].advance = ((bbox.xMax - bbox.xMin)*20*FT_SCALE)/FT_SUBPIXELS;
319         }
320 #else
321         if(glyph)
322             font->glyph[font->numchars].advance = glyph->advance.x*20/65536;
323         else
324             font->glyph[font->numchars].advance = 0;
325 #endif
326         
327         SRECT b = swf_ShapeDrawerGetBBox(&draw);
328
329         //font->layout->bounds[font->numchars].xmin = (bbox.xMin*FT_SCALE*20)/FT_SUBPIXELS;
330         //font->layout->bounds[font->numchars].ymin = (bbox.yMin*FT_SCALE*20)/FT_SUBPIXELS;
331         //font->layout->bounds[font->numchars].xmax = (bbox.xMax*FT_SCALE*20)/FT_SUBPIXELS;
332         //font->layout->bounds[font->numchars].ymax = (bbox.yMax*FT_SCALE*20)/FT_SUBPIXELS;
333
334         font->layout->bounds[font->numchars] = b;
335         font->glyph[font->numchars].shape = swf_ShapeDrawerToShape(&draw);
336
337         swf_ExpandRect2(&fontbbox, &font->layout->bounds[font->numchars]);
338
339         draw.dealloc(&draw);
340
341         if(glyph)
342             FT_Done_Glyph(glyph);
343         font->glyph2ascii[font->numchars] = font->glyph2ascii[t];
344         glyph2glyph[t] = font->numchars;
345         font->numchars++;
346     }
347     
348     //font->layout->ascent = abs(face->ascender)*FT_SCALE*loadfont_scale*20/FT_SUBPIXELS/2; //face->bbox.xMin;
349     //font->layout->descent = abs(face->descender)*FT_SCALE*loadfont_scale*20/FT_SUBPIXELS/2; //face->bbox.xMax;
350     //font->layout->leading = font->layout->ascent + font->layout->descent;
351
352     if(-fontbbox.ymin < 0)
353         font->layout->ascent = 0;
354     else
355         font->layout->ascent = -fontbbox.ymin;
356
357     if(fontbbox.ymax < 0)
358         font->layout->descent = 0;
359     else
360         font->layout->descent = fontbbox.ymax;
361
362     int leading = fontbbox.ymax - fontbbox.ymin;
363     font->layout->leading = leading>0x7fff?0x7fff:leading;
364
365     /* notice: if skip_unused is true, font->glyph2ascii, font->glyphnames and font->layout->bounds will 
366                have more memory allocated than just font->numchars, but only the first font->numchars 
367                are used/valid */
368
369     for(t=0;t<font->maxascii;t++) {
370         if(font->ascii2glyph[t]>=0) {
371             font->ascii2glyph[t] = glyph2glyph[font->ascii2glyph[t]];
372         }
373     }
374     rfx_free(glyph2glyph);
375
376     FT_Done_Face(face);
377     FT_Done_FreeType(ftlibrary);ftlibrary=0;
378
379     return font;
380 }
381 #else  //HAVE_FREETYPE
382
383 SWFFONT* swf_LoadTrueTypeFont(const char*filename, char flashtype)
384 {
385     fprintf(stderr, "Warning: no freetype library- not able to load %s\n", filename);
386     return 0;
387 }
388
389 #endif
390
391 #ifdef HAVE_T1LIB
392
393 #include <t1lib.h>
394
395 static int t1lib_initialized = 0;
396
397 static int counter = 0;
398
399 SWFFONT* swf_LoadT1Font(const char*filename)
400 {
401     SWFFONT * font;
402     int nr;
403     float angle,underline;
404     char*fontname,*fullname,*familyname;
405     BBox bbox;
406     int s,num;
407     char**charnames;
408     char**charname;
409     char*encoding[256];
410     int c;
411     int t;
412
413     if(!t1lib_initialized) {
414         T1_SetBitmapPad(16);
415         if ((T1_InitLib(NO_LOGFILE)==NULL)){
416             fprintf(stderr, "Initialization of t1lib failed\n");
417             return 0;
418         }
419         t1lib_initialized = 1;
420     }
421     nr = T1_AddFont(filename);
422     T1_LoadFont(nr);
423
424     charnames = T1_GetAllCharNames(nr);
425     if(!charnames) {
426         fprintf(stderr, "No Charnames record- not a Type1 Font?\n");
427         return 0;
428     }
429
430     angle = T1_GetItalicAngle(nr);
431     fontname = T1_GetFontName(nr);
432     fullname = T1_GetFullName(nr);
433     familyname = T1_GetFamilyName(nr);
434     underline = T1_GetUnderlinePosition(nr);
435     bbox = T1_GetFontBBox(nr);
436
437     font = (SWFFONT*)rfx_calloc(sizeof(SWFFONT));
438
439     font->version = 2;
440     if(fontname) 
441         font->name = (U8*)strdup(fontname);
442     else 
443         font->name = 0;
444     font->layout = (SWFLAYOUT*)rfx_calloc(sizeof(SWFLAYOUT));
445
446     num = 0;
447     charname = charnames;
448     while(*charname) {
449         charname++;
450         if(num<256) {
451             if(*charname) encoding[num] = strdup(*charname);
452             else          encoding[num] = strdup(".notdef");
453         }
454         num++;
455     }
456     for(t=num;t<256;t++)
457         encoding[t] = strdup(".notdef");
458     
459     //T1_ReencodeFont(nr, encoding);
460
461     font->maxascii = num;
462     font->numchars = num;
463     
464     font->style = (/*bold*/0?FONT_STYLE_BOLD:0) + (angle>0.05?FONT_STYLE_ITALIC:0);
465
466     font->glyph = (SWFGLYPH*)rfx_calloc(num*sizeof(SWFGLYPH));
467     font->glyph2ascii = (U16*)rfx_calloc(num*sizeof(U16));
468     font->ascii2glyph = (int*)rfx_calloc(font->maxascii*sizeof(int));
469     font->layout->ascent = (U16)(underline - bbox.lly);
470     font->layout->descent = (U16)(bbox.ury - underline);
471     font->layout->leading = (U16)(font->layout->ascent - 
472                              font->layout->descent -
473                              (bbox.lly - bbox.ury));
474     font->layout->bounds = (SRECT*)rfx_calloc(sizeof(SRECT)*num);
475     font->layout->kerningcount = 0;
476     font->layout->kerning = 0;
477     font->glyphnames = rfx_calloc(num*sizeof(char*));
478   
479     num = 0;
480
481     charname = charnames;
482     for(c=0;c<font->numchars;c++) {
483         drawer_t draw;
484         SRECT bbox;
485         T1_OUTLINE * outline;
486         FPOINT pos,last;
487         int firstx;
488         
489         outline = T1_GetCharOutline(nr, c, 100.0, 0);
490         firstx = outline->dest.x/0xffff;
491
492         pos.x = 0;
493         pos.y = 0;
494         last = pos;
495         
496         font->glyphnames[c] = strdup(*charname);
497
498         if(c<font->maxascii)
499             font->ascii2glyph[c] = c;
500         font->glyph2ascii[c] = c;
501         
502         swf_Shape01DrawerInit(&draw, 0);
503
504         while(outline) {
505             pos.x += (outline->dest.x/(float)0xffff);
506             pos.y += (outline->dest.y/(float)0xffff);
507
508             if(outline->type == T1_PATHTYPE_MOVE) {
509                 draw.moveTo(&draw,&pos);
510             } else if(outline->type == T1_PATHTYPE_LINE) {
511                 draw.lineTo(&draw,&pos);
512             } else if(outline->type == T1_PATHTYPE_BEZIER) {
513                 T1_BEZIERSEGMENT*o2 = (T1_BEZIERSEGMENT*)outline;
514                 FPOINT b,c;
515                 b.x = o2->B.x/(float)0xffff+last.x;
516                 b.y = o2->B.y/(float)0xffff+last.y;
517                 c.x = o2->C.x/(float)0xffff+last.x;
518                 c.y = o2->C.y/(float)0xffff+last.y;
519                 draw_cubicTo(&draw,&b,&c,&pos);
520             } else {
521                 fprintf(stderr, "loadT1Font: unknown outline type:%d\n", outline->type);
522             }
523             last = pos;
524             outline = outline->link;
525         }
526         
527         draw.finish(&draw);
528
529         font->glyph[c].shape = swf_ShapeDrawerToShape(&draw);
530         bbox = swf_ShapeDrawerGetBBox(&draw);
531         draw.dealloc(&draw);
532             
533         font->layout->bounds[c] = bbox;
534         font->glyph[c].advance = bbox.xmax;
535         if(!font->glyph[c].advance) {
536             font->glyph[c].advance = firstx;
537         }
538         charname++;
539     }
540     T1_DeleteFont(nr);
541
542     for(t=0;t<256;t++)
543         rfx_free(encoding[t]);
544     return font;
545 }
546
547 #else
548
549 SWFFONT* swf_LoadT1Font(const char*filename)
550 {
551     fprintf(stderr, "Warning: no t1lib- not able to load %s\n", filename);
552     return 0;
553 }
554
555 #endif
556
557 SWFFONT* swf_DummyFont()
558 {
559     SWFFONT*font = (SWFFONT*)rfx_calloc(sizeof(SWFFONT));
560     return font;
561 }
562
563 static int isSWF(const char*filename)
564 {
565     FILE*fi = fopen(filename, "rb");
566     char a[8];
567     if(!fi) {
568         perror(filename);
569         return -1;
570     }
571     memset(a, 0, sizeof(a));
572     fread(a, 4, 1, fi);
573     fclose(fi);
574
575     if(!strncmp(a, "FWS", 3) || !strncmp(a, "CWS", 3)) {
576         return 1;
577     }
578     return 0;
579 }
580
581 SWFFONT* swf_LoadFont(const char*filename, char flashtype)
582 {
583     int is_swf;
584     if(filename == 0)
585         return swf_DummyFont();
586     is_swf = isSWF(filename);
587     if(is_swf<0)
588         return 0;
589     if(is_swf) {
590         SWFFONT*font = swf_ReadFont(filename);
591         if(flashtype && font->version==2)
592             fprintf(stderr, "Warning: Can't load font v2 file as flashtype (%s)\n", filename);
593         return font;
594     }
595
596 #if defined(HAVE_FREETYPE)
597     return swf_LoadTrueTypeFont(filename, flashtype);
598 #elif defined(HAVE_T1LIB)
599     return swf_LoadT1Font(filename);
600 #else
601     fprintf(stderr, "Error: Neither T1lib nor FreeType support compiled in. Could not load %s\n", filename);
602     return 0;
603 #endif
604 }
605