1 #include <libxslt/extensions.h>
2 #include <libxslt/xsltutils.h>
3 #include <libxslt/variables.h>
4 #include <libxml/xpathInternals.h>
5 #include "swft.h"
6 #include <sys/types.h>
7 #include <sys/stat.h>
8 #include <cstring>
9 #include <cmath>
10 #include <cwchar>
11
12 #include "gSWF.h"
13 #include <ft2build.h>
14 #include FT_FREETYPE_H
15 #include FT_OUTLINE_H
16 #include "SWFShapeMaker.h"
17
18 #include FT_TRUETYPE_TABLES_H
19
20 using namespace SWF;
21
22 #define TMP_STRLEN 0xff
23
moveTo(const FT_Vector * to,void * shaper)24 int moveTo(const FT_Vector *to, void *shaper) {
25 ((ShapeMaker*)shaper)->setup(to->x, to->y);
26
27 return 0;
28 }
29
lineTo(const FT_Vector * to,void * shaper)30 int lineTo( const FT_Vector *to, void *shaper ) {
31 ((ShapeMaker*)shaper)->lineTo(to->x, to->y);
32
33 return 0;
34 }
35
conicTo(const FT_Vector * control,const FT_Vector * to,void * shaper)36 int conicTo( const FT_Vector *control, const FT_Vector *to, void *shaper ) {
37 ((ShapeMaker*)shaper)->curveTo(control->x, control->y, to->x, to->y);
38
39 return 0;
40 }
41
cubicTo(const FT_Vector * control1,const FT_Vector * control2,const FT_Vector * to,void * shaper)42 int cubicTo( const FT_Vector *control1, const FT_Vector *control2, const FT_Vector *to, void *shaper ) {
43
44 ((ShapeMaker*)shaper)->cubicTo(
45 control1->x, control1->y,
46 control2->x, control2->y,
47 to->x, to->y);
48
49 return 0;
50 }
51
52 FT_Outline_Funcs decomposeFunctions = {
53 (FT_Outline_MoveToFunc)moveTo,
54 (FT_Outline_LineToFunc)lineTo,
55 (FT_Outline_ConicToFunc)conicTo,
56 (FT_Outline_CubicToFunc)cubicTo,
57 0,
58 0
59 };
60
emptyGlyph(FT_Face face,FT_ULong wc)61 bool emptyGlyph(FT_Face face, FT_ULong wc) {
62 return wc != 32 && face->glyph->outline.n_points == 0;
63 }
64
compareGlyphs(const void * a,const void * b)65 int compareGlyphs(const void *a, const void *b) {
66 int _a = *(int*)a;
67 int _b = *(int*)b;
68 return _a - _b;
69 }
70
getVerticalMetrics(FT_Face face,int & ascender,int & descender,int & lineGap)71 void getVerticalMetrics (FT_Face face, int &ascender, int &descender, int &lineGap) {
72 TT_OS2 *pOS2 = (TT_OS2 *)FT_Get_Sfnt_Table(face, ft_sfnt_os2);
73 if (pOS2) {
74 ascender = pOS2->usWinAscent;
75 descender = pOS2->usWinDescent;
76 lineGap = pOS2->sTypoLineGap;
77 } else {
78 TT_HoriHeader *pHhea = (TT_HoriHeader *)FT_Get_Sfnt_Table(face, ft_sfnt_hhea);
79 if (pHhea) {
80 ascender = pHhea->Ascender;
81 descender = -pHhea->Descender;
82 lineGap = pHhea->Line_Gap;
83 }
84 }
85 }
86
importDefineFont2(DefineFont2 * tag,const char * filename,const char * fontname,const xmlChar * glyphs_xml,Context * ctx,swft_ctx * swftctx,int offset)87 void importDefineFont2(DefineFont2 *tag, const char *filename, const char *fontname, const xmlChar *glyphs_xml, Context *ctx, swft_ctx *swftctx, int offset) {
88 FT_Library swfft_library;
89 FT_Face face;
90 int error;
91 FT_UInt glyph_index;
92 FT_ULong character;
93 FT_Outline *outline;
94 int *glyphs = NULL;
95 int i=0;
96 char *font_ascentmap;
97
98 GlyphList *glyphList = tag->getglyphs();
99 List<Short>* advance = tag->getadvance();
100 List<Rectangle>* bounds = tag->getbounds();
101 List<WideKerning>* kernings = tag->getwideKerning();
102 // NYI: kerning
103
104 GlyphShape *shape;
105 int nGlyphs, glyph;
106
107 const int SCALING_FACTOR = 1024;
108
109 if (FT_Init_FreeType(&swfft_library)) {
110 fprintf(stderr, "WARNING: could not initialize FreeType\n");
111 goto fail;
112 }
113
114 error = FT_New_Face(swfft_library, filename, 0, &face);
115 if (error) {
116 fprintf(stderr, "WARNING: FreeType does not like %s\n", filename);
117 goto fail;
118 }
119
120 if (face->num_faces > 1) {
121 fprintf(stderr,
122 "WARNING: %s contains %li faces, but only the first is "
123 "imported.\n", filename, face->num_faces);
124 }
125
126 FT_Set_Pixel_Sizes(face, SCALING_FACTOR, SCALING_FACTOR);
127
128 // count availably glyphs, yes we have to load them to check if they're empty, sorry.
129 nGlyphs = 0;
130 if (!glyphs_xml) {
131 if ((character = FT_Get_First_Char(face, &glyph_index)) != 0) {
132 nGlyphs++;
133 }
134 while ((character = FT_Get_Next_Char(face, character, &glyph_index)) != 0) {
135 if (FT_Load_Glyph(face, glyph_index, FT_LOAD_NO_BITMAP)) {
136 fprintf(stderr,
137 "WARNING: cannot load glyph %lu ('%lc') from "
138 "%s.\n", character, (wint_t) character, filename);
139 goto fail;
140 }
141 if (face->glyph->format != FT_GLYPH_FORMAT_OUTLINE) {
142 fprintf(stderr, "WARNING: %s seems to be a bitmap font.\n", filename);
143 goto fail;
144 }
145
146 nGlyphs++;
147 }
148
149 glyphs = new int[nGlyphs+1];
150 i=0;
151 if ((character = FT_Get_First_Char(face, &glyph_index)) != 0) {
152 glyphs[i++] = character;
153 }
154 while ((character = FT_Get_Next_Char(face, character, &glyph_index)) != 0) {
155 glyphs[i++] = character;
156 }
157 } else {
158 int nGlyphs_ = xmlUTF8Strlen(glyphs_xml);
159 glyphs = new int[nGlyphs_];
160 int len=0, idx=0;
161 const unsigned char *str;
162
163 for (int i=0; i<nGlyphs_; i++) {
164 str = (const unsigned char *)&glyphs_xml[idx];
165 len=4;
166 glyphs[i]=xmlGetUTF8Char(str, &len)-offset;
167 idx+=len;
168 }
169
170 // sort the list of glyphs
171 qsort(glyphs, nGlyphs_, sizeof(int), compareGlyphs);
172
173 nGlyphs = nGlyphs_;
174 }
175
176 glyphList->allocate(nGlyphs);
177 tag->setglyphCount(nGlyphs);
178
179 tag->sethasLayout(1);
180
181 {
182 int ascender, descender, lineGap;
183 getVerticalMetrics(face, ascender, descender, lineGap);
184 tag->setascent(ascender * SCALING_FACTOR / face->units_per_EM);
185 tag->setdescent(descender * SCALING_FACTOR / face->units_per_EM);
186 tag->setleading(lineGap * SCALING_FACTOR / face->units_per_EM);
187 }
188
189 tag->setwideGlyphOffsets(1);
190 tag->setwideMap(1);
191
192 if (face->style_flags & FT_STYLE_FLAG_ITALIC) {
193 tag->setitalic(true);
194 }
195 if (face->style_flags & FT_STYLE_FLAG_BOLD) {
196 tag->setbold(true);
197 }
198 if (!fontname) {
199 fontname = face->family_name;
200 }
201 tag->setname(strdup(fontname));
202
203 if (!ctx->quiet) {
204 fprintf(stderr, "Importing TTF: '%s' - '%s'%s%s (%i glyphs) charcode offset %i\n", filename, fontname,
205 face->style_flags & FT_STYLE_FLAG_BOLD ? " bold" : "",
206 face->style_flags & FT_STYLE_FLAG_ITALIC ? " italic" : "",
207 nGlyphs, offset);
208 }
209
210 for (glyph=0; glyph<nGlyphs; glyph++) {
211 character = glyphs[glyph];
212 glyph_index = FT_Get_Char_Index(face, character);
213
214 if (FT_Load_Glyph(face, glyph_index, FT_LOAD_NO_BITMAP)) {
215 fprintf(stderr, "WARNING: cannot load glyph %lu ('%lc') "
216 "from %s.\n", character, (wint_t) character, filename);
217 goto fail;
218 }
219
220 if (face->glyph->format != FT_GLYPH_FORMAT_OUTLINE) {
221 fprintf(stderr, "WARNING: %s seems to be a bitmap font.\n", filename);
222 goto fail;
223 }
224
225 outline = &face->glyph->outline;
226
227 Short *adv = new Short();
228 adv->setvalue((short)(ceil(1+(face->glyph->advance.x >> 6))));
229 advance->append(adv);
230
231 glyphList->setMapN(glyph, character+offset);
232 shape = glyphList->getShapeN(glyph);
233 ShapeMaker shaper(shape->getedges(), (1.0/64), -(1.0/64), 0, 0);
234 shaper.setStyle(1, -1, -1);
235
236 FT_Outline_Decompose(outline, &decomposeFunctions, &shaper);
237
238 shaper.finish();
239
240 Rectangle *r = new Rectangle();
241
242 Rect rect = shaper.getBounds();
243 r->setleft((int)rect.left);
244 r->settop((int)rect.top);
245 r->setright((int)rect.right);
246 r->setbottom((int)rect.bottom);
247
248 r->setbits(SWFMaxBitsNeeded( true, 3, r->gettop(), r->getright(), r->getbottom()));
249 bounds->append(r);
250 }
251
252 if (FT_HAS_KERNING(face)) {
253 FT_Vector vec;
254 int l, r;
255 for (int left=0; left<nGlyphs; left++) {
256 for (int right=0; right<nGlyphs; right++) {
257 l = FT_Get_Char_Index(face, glyphs[left]);
258 r = FT_Get_Char_Index(face, glyphs[right]);
259 if (!FT_Get_Kerning(face, l, r, FT_KERNING_DEFAULT, &vec)) {
260 if (vec.x) {
261 WideKerning *kern = new WideKerning();
262 kern->seta(glyphs[left]);
263 kern->setb(glyphs[right]);
264 kern->setadjustment((short)(floor(vec.x >> 6)));
265 kernings->append(kern);
266 }
267 }
268 }
269 }
270 }
271
272 if (glyphs) {
273 delete glyphs;
274 }
275
276 // hacky: store the ascent in the idmap.
277 font_ascentmap = new char[0xff];
278 snprintf( font_ascentmap, 0xff, "%s_ascent", fontname );
279 swftctx->setMap( font_ascentmap, 1+(SCALING_FACTOR * face->ascender) / face->units_per_EM );
280 delete font_ascentmap;
281
282 return;
283
284 fail:
285 fprintf( stderr, "WARNING: could not import %s\n", filename );
286 return;
287 }
288
importDefineFont3(DefineFont3 * tag,const char * filename,const char * fontname,const xmlChar * glyphs_xml,Context * ctx,swft_ctx * swftctx,int offset)289 void importDefineFont3(DefineFont3 *tag, const char *filename, const char *fontname, const xmlChar *glyphs_xml, Context *ctx, swft_ctx *swftctx, int offset) {
290 FT_Library swfft_library;
291 FT_Face face;
292 int error;
293 FT_UInt glyph_index;
294 FT_ULong character;
295 FT_Outline *outline;
296 int *glyphs = NULL;
297 int i=0;
298 char *font_ascentmap;
299
300 GlyphList *glyphList = tag->getglyphs();
301 List<Short>* advance = tag->getadvance();
302 List<Rectangle>* bounds = tag->getbounds();
303 List<WideKerning>* kernings = tag->getwideKerning();
304 // NYI: kerning
305
306 GlyphShape *shape;
307 int nGlyphs, glyph;
308
309 const int SCALING_FACTOR = 1024 * 20;
310
311 if (FT_Init_FreeType(&swfft_library)) {
312 fprintf(stderr, "WARNING: could not initialize FreeType\n");
313 goto fail;
314 }
315
316 error = FT_New_Face(swfft_library, filename, 0, &face);
317 if (error) {
318 fprintf(stderr, "WARNING: FreeType does not like %s\n", filename);
319 goto fail;
320 }
321
322 if (face->num_faces > 1) {
323 fprintf(stderr, "WARNING: %s contains %li faces, but only the "
324 "first is imported.\n", filename, face->num_faces);
325 }
326
327 FT_Set_Char_Size(face, SCALING_FACTOR << 6, SCALING_FACTOR << 6, 72, 72);
328
329 // count availably glyphs, yes we have to load them to check if they're empty, sorry.
330 nGlyphs = 0;
331 if (!glyphs_xml) {
332 if ((character = FT_Get_First_Char(face, &glyph_index)) != 0) {
333 nGlyphs++;
334 }
335 while ((character = FT_Get_Next_Char(face, character, &glyph_index)) != 0) {
336 if (FT_Load_Glyph(face, glyph_index, FT_LOAD_NO_BITMAP)) {
337 fprintf(stderr,
338 "WARNING: cannot load glyph %lu ('%lc') "
339 "from %s.\n", character, (wint_t) character, filename);
340 goto fail;
341 }
342 if (face->glyph->format != FT_GLYPH_FORMAT_OUTLINE) {
343 fprintf(stderr, "WARNING: %s seems to be a bitmap font.\n", filename);
344 goto fail;
345 }
346 nGlyphs++;
347 }
348
349 glyphs = new int[nGlyphs+1];
350 i=0;
351 if ((character = FT_Get_First_Char(face, &glyph_index)) != 0) {
352 glyphs[i++] = character;
353 }
354 while ((character = FT_Get_Next_Char(face, character, &glyph_index)) != 0) {
355 glyphs[i++] = character;
356 }
357 } else {
358 int nGlyphs_ = xmlUTF8Strlen(glyphs_xml);
359 glyphs = new int[nGlyphs_];
360 int len=0, idx=0;
361 const unsigned char *str;
362
363 for (int i=0; i<nGlyphs_; i++) {
364 str = (const unsigned char *)&glyphs_xml[idx];
365 len=4;
366 glyphs[i]=xmlGetUTF8Char(str, &len)-offset;
367 idx+=len;
368 }
369
370 // sort the list of glyphs
371 qsort(glyphs, nGlyphs_, sizeof(int), compareGlyphs);
372
373 nGlyphs = nGlyphs_;
374 }
375
376 glyphList->allocate(nGlyphs);
377 tag->setglyphCount(nGlyphs);
378
379 tag->sethasLayout(1);
380
381 {
382 int ascender, descender, lineGap;
383 getVerticalMetrics(face, ascender, descender, lineGap);
384 tag->setascent(ascender * SCALING_FACTOR / face->units_per_EM);
385 tag->setdescent(descender * SCALING_FACTOR / face->units_per_EM);
386 tag->setleading(lineGap * SCALING_FACTOR / face->units_per_EM);
387 }
388
389 tag->setwideGlyphOffsets(1);
390 tag->setwideMap(1);
391
392 if (face->style_flags & FT_STYLE_FLAG_ITALIC) {
393 tag->setitalic(true);
394 }
395 if (face->style_flags & FT_STYLE_FLAG_BOLD) {
396 tag->setbold(true);
397 }
398 if (!fontname) {
399 fontname = face->family_name;
400 }
401 tag->setname(strdup(fontname));
402
403 if (!ctx->quiet) {
404 fprintf(stderr, "Importing TTF: '%s' - '%s'%s%s (%i glyphs) charcode offset %i\n", filename, fontname,
405 face->style_flags & FT_STYLE_FLAG_BOLD ? " bold" : "",
406 face->style_flags & FT_STYLE_FLAG_ITALIC ? " italic" : "",
407 nGlyphs, offset);
408 }
409
410 for (glyph=0; glyph<nGlyphs; glyph++) {
411 character = glyphs[glyph];
412 glyph_index = FT_Get_Char_Index(face, character);
413
414 if (FT_Load_Glyph(face, glyph_index, FT_LOAD_NO_BITMAP)) {
415 fprintf(stderr,
416 "WARNING: cannot load glyph %lu ('%lc') from %s.\n",
417 character, (wint_t) character, filename);
418 goto fail;
419 }
420
421 if (face->glyph->format != FT_GLYPH_FORMAT_OUTLINE) {
422 fprintf(stderr, "WARNING: %s seems to be a bitmap font.\n", filename);
423 goto fail;
424 }
425
426 outline = &face->glyph->outline;
427
428 Short *adv = new Short();
429 adv->setvalue((short)(ceil(1+(face->glyph->advance.x >> 6))));
430 advance->append(adv);
431
432 glyphList->setMapN(glyph, character+offset);
433 shape = glyphList->getShapeN(glyph);
434 ShapeMaker shaper(shape->getedges(), (1.0/64), -(1.0/64), 0, 0);
435 shaper.setStyle(1, -1, -1);
436
437 FT_Outline_Decompose(outline, &decomposeFunctions, &shaper);
438
439 shaper.finish();
440
441 Rectangle *r = new Rectangle();
442
443 Rect rect = shaper.getBounds();
444 r->setleft((int)rect.left);
445 r->settop((int)rect.top);
446 r->setright((int)rect.right);
447 r->setbottom((int)rect.bottom);
448
449 r->setbits(SWFMaxBitsNeeded(true, 3, r->gettop(), r->getright(), r->getbottom()));
450 bounds->append(r);
451 }
452
453 if (FT_HAS_KERNING(face)) {
454 FT_Vector vec;
455 int l, r;
456 for (int left=0; left<nGlyphs; left++) {
457 for (int right=0; right<nGlyphs; right++) {
458 l = FT_Get_Char_Index(face, glyphs[left]);
459 r = FT_Get_Char_Index(face, glyphs[right]);
460 if (!FT_Get_Kerning(face, l, r, FT_KERNING_DEFAULT, &vec)) {
461 if (vec.x) {
462 WideKerning *kern = new WideKerning();
463 kern->seta(glyphs[left]);
464 kern->setb(glyphs[right]);
465 kern->setadjustment((short)(floor(vec.x >> 6)));
466 kernings->append(kern);
467 }
468 }
469 }
470 }
471 }
472
473 if (glyphs) {
474 delete glyphs;
475 }
476
477 // hacky: store the ascent in the idmap.
478 font_ascentmap = new char[0xff];
479 snprintf(font_ascentmap, 0xff, "%s_ascent", fontname);
480 swftctx->setMap(font_ascentmap, 1+(SCALING_FACTOR * face->ascender) / face->units_per_EM);
481 delete font_ascentmap;
482
483 return;
484
485 fail:
486 fprintf(stderr, "WARNING: could not import %s\n", filename);
487 return;
488 }
489
490
swft_import_ttf(xmlXPathParserContextPtr ctx,int nargs)491 void swft_import_ttf(xmlXPathParserContextPtr ctx, int nargs) {
492 xsltTransformContextPtr tctx;
493 char *filename;
494 xmlDocPtr doc = NULL;
495 xmlNodePtr node;
496 Context swfctx;
497 xmlChar *glyphs = NULL;
498 int offset = 0;
499 double movieVersion;
500 bool quiet = true;
501 xmlXPathObjectPtr quietObj = NULL;
502 swft_ctx *c = NULL;
503
504 const char *fontname = NULL;
505
506 if ((nargs < 2) || (nargs > 5)) {
507 xmlXPathSetArityError(ctx);
508 return;
509 }
510
511 if (nargs >= 5) {
512 offset = (int)xmlXPathPopNumber(ctx);
513 }
514 if (nargs >= 4) {
515 fontname = (const char *)xmlXPathPopString(ctx);
516 if (fontname[0] == 0) {
517 fontname = NULL;
518 }
519 }
520 if (nargs >= 3) {
521 glyphs = xmlXPathPopString(ctx);
522 if (glyphs[0] == 0) {
523 glyphs = NULL;
524 }
525 }
526
527 movieVersion = xmlXPathPopNumber(ctx);
528 filename = swft_get_filename(xmlXPathPopString(ctx), ctx->context->doc->URL);
529 if (xmlXPathCheckError(ctx)) {
530 goto fail;
531 }
532
533 tctx = xsltXPathGetTransformContext(ctx);
534
535 quietObj = xsltVariableLookup(tctx, (const xmlChar*)"quiet", NULL);
536 if (quietObj && quietObj->stringval) {
537 quiet = !strcmp("true",(const char*)quietObj->stringval);
538 }
539 swfctx.quiet = quiet;
540
541 doc = xmlNewDoc((const xmlChar *)"1.0");
542 doc->xmlRootNode = xmlNewDocNode(doc, NULL, (const xmlChar*)"ttf", NULL);
543 node = doc->xmlRootNode;
544
545 c = (swft_ctx*)xsltGetExtData(xsltXPathGetTransformContext(ctx), SWFT_NAMESPACE);
546
547 // create the font tag
548 if (movieVersion >= 8) {
549 DefineFont3 *tag = new DefineFont3();
550 importDefineFont3(tag, (const char *)filename, fontname, glyphs, &swfctx, c, offset);
551 tag->writeXML(node, &swfctx);
552 } else {
553 DefineFont2 *tag = new DefineFont2();
554 importDefineFont2(tag, (const char *)filename, fontname, glyphs, &swfctx, c, offset);
555 tag->writeXML(node, &swfctx);
556 }
557
558 if (glyphs) {
559 xmlFree(glyphs);
560 }
561
562 valuePush(ctx, xmlXPathNewNodeSet((xmlNodePtr)doc));
563 goto end;
564
565 fail:
566 fprintf(stderr, "WARNING: could not import %s\n", filename);
567
568 end:
569 delete filename;
570 }
571