added gfxline_transform(), gfxline_clone(), gfxmatrix_dump()
[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             glyph=0;
260             if(skip_unused) 
261                 continue;
262         } else {
263             error = FT_Get_Glyph(face->glyph, &glyph);
264             if(error) {
265                 fprintf(stderr, "Couldn't get glyph %d, error:%d\n", t, error);
266                 glyph=0;
267                 if(skip_unused) 
268                     continue;
269             }
270         }
271
272         if(glyph)
273             FT_Glyph_Get_CBox(glyph, ft_glyph_bbox_unscaled, &bbox);
274         else
275             memset(&bbox, 0, sizeof(bbox));
276
277         bbox.yMin = -bbox.yMin;
278         bbox.yMax = -bbox.yMax;
279         if(bbox.xMax < bbox.xMin) {
280             // swap
281             bbox.xMax ^= bbox.xMin;
282             bbox.xMin ^= bbox.xMax;
283             bbox.xMax ^= bbox.xMin;
284         }
285         if(bbox.yMax < bbox.yMin) {
286             // swap
287             bbox.yMax ^= bbox.yMin;
288             bbox.yMin ^= bbox.yMax;
289             bbox.yMax ^= bbox.yMin;
290         }
291
292         swf_Shape01DrawerInit(&draw, 0);
293
294         //error = FT_Outline_Decompose(&face->glyph->outline, &outline_functions, &draw);
295         if(glyph)
296             error = FT_Outline_Decompose(&face->glyph->outline, &outline_functions, &draw);
297         else
298             error = 0;
299         draw.finish(&draw);
300         
301         if(error) {
302             fprintf(stderr, "Couldn't decompose glyph %d\n", t);
303             draw.dealloc(&draw);
304             continue;
305         }
306
307 #if 0
308         if(bbox.xMin > 0) {
309             font->glyph[font->numchars].advance = (bbox.xMax*20*FT_SCALE)/FT_SUBPIXELS;
310         } else {
311             font->glyph[font->numchars].advance = ((bbox.xMax - bbox.xMin)*20*FT_SCALE)/FT_SUBPIXELS;
312         }
313 #else
314         if(glyph)
315             font->glyph[font->numchars].advance = glyph->advance.x*20/65536;
316         else
317             font->glyph[font->numchars].advance = 0;
318 #endif
319         
320         font->glyph[font->numchars].shape = swf_ShapeDrawerToShape(&draw);
321         
322         font->layout->bounds[font->numchars].xmin = (bbox.xMin*FT_SCALE*20)/FT_SUBPIXELS;
323         font->layout->bounds[font->numchars].ymin = (bbox.yMin*FT_SCALE*20)/FT_SUBPIXELS;
324         font->layout->bounds[font->numchars].xmax = (bbox.xMax*FT_SCALE*20)/FT_SUBPIXELS;
325         font->layout->bounds[font->numchars].ymax = (bbox.yMax*FT_SCALE*20)/FT_SUBPIXELS;
326
327         draw.dealloc(&draw);
328
329         if(glyph)
330             FT_Done_Glyph(glyph);
331         font->glyph2ascii[font->numchars] = font->glyph2ascii[t];
332         glyph2glyph[t] = font->numchars;
333         font->numchars++;
334     }
335     /* notice: if skip_unused is true, font->glyph2ascii, font->glyphnames and font->layout->bounds will 
336                have more memory allocated than just font->numchars, but only the first font->numchars 
337                are used/valid */
338
339     for(t=0;t<font->maxascii;t++) {
340         if(font->ascii2glyph[t]>=0) {
341             font->ascii2glyph[t] = glyph2glyph[font->ascii2glyph[t]];
342         }
343     }
344     rfx_free(glyph2glyph);
345
346     FT_Done_Face(face);
347     FT_Done_FreeType(ftlibrary);ftlibrary=0;
348
349     return font;
350 }
351 #else  //HAVE_FREETYPE
352
353 SWFFONT* swf_LoadTrueTypeFont(char*filename)
354 {
355     fprintf(stderr, "Warning: no freetype library- not able to load %s\n", filename);
356     return 0;
357 }
358
359 #endif
360
361 #ifdef HAVE_T1LIB
362
363 #include <t1lib.h>
364
365 static int t1lib_initialized = 0;
366
367 static int counter = 0;
368
369 SWFFONT* swf_LoadT1Font(char*filename)
370 {
371     SWFFONT * font;
372     int nr;
373     float angle,underline;
374     char*fontname,*fullname,*familyname;
375     BBox bbox;
376     int s,num;
377     char**charnames;
378     char**charname;
379     char*encoding[256];
380     int c;
381     int t;
382
383     if(!t1lib_initialized) {
384         T1_SetBitmapPad(16);
385         if ((T1_InitLib(NO_LOGFILE)==NULL)){
386             fprintf(stderr, "Initialization of t1lib failed\n");
387             return 0;
388         }
389         t1lib_initialized = 1;
390     }
391     nr = T1_AddFont(filename);
392     T1_LoadFont(nr);
393
394     charnames = T1_GetAllCharNames(nr);
395     if(!charnames) {
396         fprintf(stderr, "No Charnames record- not a Type1 Font?\n");
397         return 0;
398     }
399
400     angle = T1_GetItalicAngle(nr);
401     fontname = T1_GetFontName(nr);
402     fullname = T1_GetFullName(nr);
403     familyname = T1_GetFamilyName(nr);
404     underline = T1_GetUnderlinePosition(nr);
405     bbox = T1_GetFontBBox(nr);
406
407     font = (SWFFONT*)rfx_calloc(sizeof(SWFFONT));
408
409     font->version = 2;
410     if(fontname) 
411         font->name = (U8*)strdup(fontname);
412     else 
413         font->name = 0;
414     font->layout = (SWFLAYOUT*)rfx_calloc(sizeof(SWFLAYOUT));
415
416     num = 0;
417     charname = charnames;
418     while(*charname) {
419         charname++;
420         if(num<256) {
421             if(*charname) encoding[num] = strdup(*charname);
422             else          encoding[num] = strdup(".notdef");
423         }
424         num++;
425     }
426     for(t=num;t<256;t++)
427         encoding[t] = strdup(".notdef");
428     
429     //T1_ReencodeFont(nr, encoding);
430
431     font->maxascii = num;
432     font->numchars = num;
433     
434     font->style = (/*bold*/0?FONT_STYLE_BOLD:0) + (angle>0.05?FONT_STYLE_ITALIC:0);
435
436     font->glyph = (SWFGLYPH*)rfx_calloc(num*sizeof(SWFGLYPH));
437     font->glyph2ascii = (U16*)rfx_calloc(num*sizeof(U16));
438     font->ascii2glyph = (int*)rfx_calloc(font->maxascii*sizeof(int));
439     font->layout->ascent = (U16)(underline - bbox.lly);
440     font->layout->descent = (U16)(bbox.ury - underline);
441     font->layout->leading = (U16)(font->layout->ascent - 
442                              font->layout->descent -
443                              (bbox.lly - bbox.ury));
444     font->layout->bounds = (SRECT*)rfx_calloc(sizeof(SRECT)*num);
445     font->layout->kerningcount = 0;
446     font->layout->kerning = 0;
447     font->glyphnames = rfx_calloc(num*sizeof(char*));
448   
449     num = 0;
450
451     charname = charnames;
452     for(c=0;c<font->numchars;c++) {
453         drawer_t draw;
454         SRECT bbox;
455         T1_OUTLINE * outline;
456         FPOINT pos,last;
457         int firstx;
458         
459         outline = T1_GetCharOutline(nr, c, 100.0, 0);
460         firstx = outline->dest.x/0xffff;
461
462         pos.x = 0;
463         pos.y = 0;
464         last = pos;
465         
466         font->glyphnames[c] = strdup(*charname);
467
468         if(c<font->maxascii)
469             font->ascii2glyph[c] = c;
470         font->glyph2ascii[c] = c;
471         
472         swf_Shape01DrawerInit(&draw, 0);
473
474         while(outline) {
475             pos.x += (outline->dest.x/(float)0xffff);
476             pos.y += (outline->dest.y/(float)0xffff);
477
478             if(outline->type == T1_PATHTYPE_MOVE) {
479                 draw.moveTo(&draw,&pos);
480             } else if(outline->type == T1_PATHTYPE_LINE) {
481                 draw.lineTo(&draw,&pos);
482             } else if(outline->type == T1_PATHTYPE_BEZIER) {
483                 T1_BEZIERSEGMENT*o2 = (T1_BEZIERSEGMENT*)outline;
484                 FPOINT b,c;
485                 b.x = o2->B.x/(float)0xffff+last.x;
486                 b.y = o2->B.y/(float)0xffff+last.y;
487                 c.x = o2->C.x/(float)0xffff+last.x;
488                 c.y = o2->C.y/(float)0xffff+last.y;
489                 draw_cubicTo(&draw,&b,&c,&pos);
490             } else {
491                 fprintf(stderr, "loadT1Font: unknown outline type:%d\n", outline->type);
492             }
493             last = pos;
494             outline = outline->link;
495             printf("t1lib: (%f,%f) ", pos.x, pos.y);
496         }
497         printf("\n");
498         
499         draw.finish(&draw);
500
501         font->glyph[c].shape = swf_ShapeDrawerToShape(&draw);
502         bbox = swf_ShapeDrawerGetBBox(&draw);
503         draw.dealloc(&draw);
504             
505         font->layout->bounds[c] = bbox;
506         font->glyph[c].advance = bbox.xmax;
507         if(!font->glyph[c].advance) {
508             font->glyph[c].advance = firstx;
509         }
510         charname++;
511     }
512     T1_DeleteFont(nr);
513
514     for(t=0;t<256;t++)
515         rfx_free(encoding[t]);
516     return font;
517 }
518
519 #else
520
521 SWFFONT* swf_LoadT1Font(char*filename)
522 {
523     fprintf(stderr, "Warning: no t1lib- not able to load %s\n", filename);
524     return 0;
525 }
526
527 #endif
528
529 SWFFONT* swf_DummyFont()
530 {
531     SWFFONT*font = (SWFFONT*)rfx_calloc(sizeof(SWFFONT));
532     return font;
533 }
534
535 static int isSWF(const char*filename)
536 {
537     FILE*fi = fopen(filename, "rb");
538     char a[8];
539     if(!fi) {
540         perror(filename);
541         return -1;
542     }
543     memset(a, 0, sizeof(a));
544     fread(a, 4, 1, fi);
545     fclose(fi);
546
547     if(!strncmp(a, "FWS", 3) || !strncmp(a, "CWS", 3)) {
548         return 1;
549     }
550     return 0;
551 }
552
553 SWFFONT* swf_LoadFont(char*filename)
554 {
555     int is_swf;
556     if(filename == 0)
557         return swf_DummyFont();
558     is_swf = isSWF(filename);
559     if(is_swf<0)
560         return 0;
561     if(is_swf) {
562         return swf_ReadFont(filename);
563     }
564
565 #if defined(HAVE_FREETYPE)
566     return swf_LoadTrueTypeFont(filename);
567 #elif defined(HAVE_T1LIB)
568     return swf_LoadT1Font(filename);
569 #else
570     fprintf(stderr, "Error: Neither T1lib nor FreeType support compiled in. Could not load %s\n", filename);
571     return 0;
572 #endif
573 }
574