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