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