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