reworked compression handling logic
[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(const 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(const 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_conic_to(const FT_Vector* _c, const FT_Vector* _to, void* user) 
81 {
82     drawer_t* draw = (drawer_t*)user;
83     FPOINT c,to;
84     to.x = _to->x*FT_SCALE/(float)FT_SUBPIXELS;
85     to.y = -_to->y*FT_SCALE/(float)FT_SUBPIXELS;
86     c.x = _c->x*FT_SCALE/(float)FT_SUBPIXELS;
87     c.y = -_c->y*FT_SCALE/(float)FT_SUBPIXELS;
88     draw_conicTo(draw, &c, &to);
89     return 0;
90 }
91 static int ft_cubic_to(const FT_Vector* _c1, const FT_Vector* _c2, const FT_Vector* _to, void* user)
92 {
93     drawer_t* draw = (drawer_t*)user;
94     FPOINT c1,c2,to;
95     to.x = _to->x*FT_SCALE/(float)FT_SUBPIXELS;
96     to.y = -_to->y*FT_SCALE/(float)FT_SUBPIXELS;
97     c1.x = _c1->x*FT_SCALE/(float)FT_SUBPIXELS;
98     c1.y = -_c1->y*FT_SCALE/(float)FT_SUBPIXELS;
99     c2.x = _c2->x*FT_SCALE/(float)FT_SUBPIXELS;
100     c2.y = -_c2->y*FT_SCALE/(float)FT_SUBPIXELS;
101     draw_cubicTo(draw, &c1, &c2, &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     int max_unicode = 0;
126     int charmap = -1;
127
128     if(ftlibrary == 0) {
129         if(FT_Init_FreeType(&ftlibrary)) {
130             fprintf(stderr, "Couldn't init freetype library!\n");
131             exit(1);
132         }
133     }
134     error = FT_New_Face(ftlibrary, filename, 0, &face);
135
136     if(error || !face) {
137         fprintf(stderr, "Couldn't load file %s- not a TTF file?\n", filename);
138         return 0;
139     }
140     
141     FT_Set_Pixel_Sizes (face, 16*loadfont_scale, 16*loadfont_scale);
142
143     if(face->num_glyphs <= 0) {
144         fprintf(stderr, "File %s contains %d glyphs\n", face->num_glyphs);
145         return 0;
146     }
147
148     font = (SWFFONT*)rfx_calloc(sizeof(SWFFONT));
149     font->id = -1;
150     font->version = 2;
151     font->layout = (SWFLAYOUT*)rfx_calloc(sizeof(SWFLAYOUT));
152     font->layout->bounds = (SRECT*)rfx_calloc(face->num_glyphs*sizeof(SRECT));
153     font->style =  ((face->style_flags&FT_STYLE_FLAG_ITALIC)?FONT_STYLE_ITALIC:0)
154                   |((face->style_flags&FT_STYLE_FLAG_BOLD)?FONT_STYLE_BOLD:0);
155     font->encoding = FONT_ENCODING_UNICODE;
156     font->glyph2ascii = (U16*)rfx_calloc(face->num_glyphs*sizeof(U16));
157     font->maxascii = 0;
158     font->glyph = (SWFGLYPH*)rfx_calloc(face->num_glyphs*sizeof(SWFGLYPH));
159     if(FT_HAS_GLYPH_NAMES(face)) {
160         font->glyphnames = (char**)rfx_calloc(face->num_glyphs*sizeof(char*));
161     }
162
163     font->layout->ascent = abs(face->ascender)*FT_SCALE*loadfont_scale*20/FT_SUBPIXELS/2; //face->bbox.xMin;
164     font->layout->descent = abs(face->descender)*FT_SCALE*loadfont_scale*20/FT_SUBPIXELS/2; //face->bbox.xMax;
165     font->layout->leading = font->layout->ascent + font->layout->descent;
166     font->layout->kerningcount = 0;
167     
168     name = FT_Get_Postscript_Name(face);
169     if(name && *name)
170         font->name = (U8*)strdup(name);
171
172     while(1) 
173     {
174     /*    // Map Glyphs to Unicode, version 1 (quick and dirty):
175         int t;
176         for(t=0;t<65536;t++) {
177             int index = FT_Get_Char_Index(face, t);
178             if(index>=0 && index<face->num_glyphs) {
179                 if(font->glyph2ascii[index]<0)
180                     font->glyph2ascii[index] = t;
181             }
182         }*/
183         
184         // Map Glyphs to Unicode, version 2 (much nicer):
185         // (The third way would be the AGL algorithm, as proposed
186         //  by Werner Lemberg on freetype@freetype.org)
187
188         charcode = FT_Get_First_Char(face, &gindex);
189         while(gindex != 0)
190         {
191             if(gindex >= 0 && gindex<face->num_glyphs) {
192                 if(!font->glyph2ascii[gindex]) {
193                     font->glyph2ascii[gindex] = charcode;
194                     if(charcode + 1 > font->maxascii) {
195                         font->maxascii = charcode + 1;
196                     }
197                 }
198             }
199             charcode = FT_Get_Next_Char(face, charcode, &gindex);
200         }
201
202         /* if we didn't find a single encoding character, try
203            the font's charmaps instead. That usually means that
204            the encoding is no longer unicode. 
205            TODO: find a way to convert the encoding to unicode
206          */
207         if(font->maxascii == 0 && charmap < face->num_charmaps - 1) {
208             charmap++;
209             FT_Set_Charmap(face, face->charmaps[charmap]);
210             font->encoding = 0;//anything but unicode FIXME
211         } else 
212             break;
213     }
214
215     if(full_unicode)
216         font->maxascii = 65535;
217     
218     font->ascii2glyph = (int*)rfx_calloc(font->maxascii*sizeof(int));
219     
220     for(t=0;t<font->maxascii;t++) {
221         int g = FT_Get_Char_Index(face, t);
222         if(!g || g>=face->num_glyphs)
223             g = -1;
224         font->ascii2glyph[t] = g;
225         if(g>=0) {
226             max_unicode = t+1;
227             if(!font->glyph2ascii[g]) {
228                 font->glyph2ascii[g] = t;
229             }
230         }
231     }
232     font->maxascii = max_unicode;
233
234     font->numchars = 0;
235
236     glyph2glyph = (int*)rfx_calloc(face->num_glyphs*sizeof(int));
237
238     for(t=0; t < face->num_glyphs; t++) {
239         FT_Glyph glyph;
240         FT_BBox bbox;
241         char name[128];
242         drawer_t draw;
243         char hasname = 0;
244         name[0]=0;
245         if(FT_HAS_GLYPH_NAMES(face)) {
246             error = FT_Get_Glyph_Name(face, t, name, 127);
247             if(!error && name[0] && !strstr(name, "notdef")) {
248                 font->glyphnames[font->numchars] = strdup(name);
249                 hasname = 1;
250             }
251         }
252         if(!font->glyph2ascii[t] && !hasname && skip_unused) {
253             continue;
254         }
255         error = FT_Load_Glyph(face, t, FT_LOAD_NO_BITMAP);
256         if(error) {
257             //tends to happen with some pdfs
258             fprintf(stderr, "Warning: Glyph %d has return code %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         }
496         
497         draw.finish(&draw);
498
499         font->glyph[c].shape = swf_ShapeDrawerToShape(&draw);
500         bbox = swf_ShapeDrawerGetBBox(&draw);
501         draw.dealloc(&draw);
502             
503         font->layout->bounds[c] = bbox;
504         font->glyph[c].advance = bbox.xmax;
505         if(!font->glyph[c].advance) {
506             font->glyph[c].advance = firstx;
507         }
508         charname++;
509     }
510     T1_DeleteFont(nr);
511
512     for(t=0;t<256;t++)
513         rfx_free(encoding[t]);
514     return font;
515 }
516
517 #else
518
519 SWFFONT* swf_LoadT1Font(char*filename)
520 {
521     fprintf(stderr, "Warning: no t1lib- not able to load %s\n", filename);
522     return 0;
523 }
524
525 #endif
526
527 SWFFONT* swf_DummyFont()
528 {
529     SWFFONT*font = (SWFFONT*)rfx_calloc(sizeof(SWFFONT));
530     return font;
531 }
532
533 static int isSWF(const char*filename)
534 {
535     FILE*fi = fopen(filename, "rb");
536     char a[8];
537     if(!fi) {
538         perror(filename);
539         return -1;
540     }
541     memset(a, 0, sizeof(a));
542     fread(a, 4, 1, fi);
543     fclose(fi);
544
545     if(!strncmp(a, "FWS", 3) || !strncmp(a, "CWS", 3)) {
546         return 1;
547     }
548     return 0;
549 }
550
551 SWFFONT* swf_LoadFont(char*filename)
552 {
553     int is_swf;
554     if(filename == 0)
555         return swf_DummyFont();
556     is_swf = isSWF(filename);
557     if(is_swf<0)
558         return 0;
559     if(is_swf) {
560         return swf_ReadFont(filename);
561     }
562
563 #if defined(HAVE_FREETYPE)
564     return swf_LoadTrueTypeFont(filename);
565 #elif defined(HAVE_T1LIB)
566     return swf_LoadT1Font(filename);
567 #else
568     fprintf(stderr, "Error: Neither T1lib nor FreeType support compiled in. Could not load %s\n", filename);
569     return 0;
570 #endif
571 }
572