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