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