1 /* swftext.c
2 
3    Text and font routines
4 
5    Extension module for the rfxswf library.
6    Part of the swftools package.
7 
8    Copyright (c) 2001 Rainer B�hme <rfxswf@reflex-studio.de>
9    Copyright (c) 2003,2004,2005,2006,2007,2008,2009 Matthias Kramm
10 
11    This program is free software; you can redistribute it and/or modify
12    it under the terms of the GNU General Public License as published by
13    the Free Software Foundation; either version 2 of the License, or
14    (at your option) any later version.
15 
16    This program is distributed in the hope that it will be useful,
17    but WITHOUT ANY WARRANTY; without even the implied warranty of
18    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19    GNU General Public License for more details.
20 
21    You should have received a copy of the GNU General Public License
22    along with this program; if not, write to the Free Software
23    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA */
24 
25 #include "../rfxswf.h"
26 
readUTF8char(U8 ** text)27 U32 readUTF8char(U8 ** text)
28 {
29     U32 c = 0;
30     if (!(*(*text) & 0x80))
31 	return *((*text)++);
32 
33     /* 0000 0080-0000 07FF   110xxxxx 10xxxxxx */
34     if (((*text)[0] & 0xe0) == 0xc0 && (*text)[1]) {
35 	c = ((*text)[0] & 0x1f) << 6 | ((*text)[1] & 0x3f);
36 	(*text) += 2;
37 	return c;
38     }
39     /* 0000 0800-0000 FFFF   1110xxxx 10xxxxxx 10xxxxxx */
40     if (((*text)[0] & 0xf0) == 0xe0 && (*text)[1] && (*text)[2]) {
41 	c = ((*text)[0] & 0x0f) << 12 | ((*text)[1] & 0x3f) << 6 | ((*text)[2] & 0x3f);
42 	(*text) += 3;
43 	return c;
44     }
45     /* 0001 0000-001F FFFF   11110xxx 10xxxxxx 10xxxxxx 10xxxxxx */
46     if (((*text)[0] & 0xf8) == 0xf0 && (*text)[1] && (*text)[2]
47 	&& (*text)[3]) {
48 	c = ((*text)[0] & 0x07) << 18 | ((*text)[1] & 0x3f) << 12 | ((*text)[2] & 0x3f) << 6 | ((*text)[3] & 0x3f);
49 	(*text) += 4;
50 	return c;
51     }
52     /* 0020 0000-03FF FFFF   111110xx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx */
53     if (((*text)[0] & 0xfc) == 0xf8 && (*text)[1] && (*text)[2]
54 	&& (*text)[3]
55 	&& (*text)[4]) {
56 	c = ((*text)[0] & 0x03) << 24 | ((*text)[1] & 0x3f) << 18 | ((*text)[2] & 0x3f) << 12 | ((*text)[3] & 0x3f) << 6 | ((*text)[4] & 0x3f);
57 	(*text) += 5;
58 	return c;
59     }
60     /* 0400 0000-7FFF FFFF   1111110x 10xxxxxx ... 10xxxxxx */
61     if (((*text)[0] & 0xfe) == 0xfc && (*text)[1] && (*text)[2]
62 	&& (*text)[3]
63 	&& (*text)[4] && (*text)[5]) {
64 	c = ((*text)[0] & 0x01) << 30 | ((*text)[1] & 0x3f) << 24 |
65 	    ((*text)[2] & 0x3f) << 18 | ((*text)[3] & 0x3f) << 12 | ((*text)[4] & 0x3f) << 6 | ((*text)[5] & 0x3f) << 6;
66 	(*text) += 6;
67 	return c;
68     }
69     return *((*text)++);
70 }
71 
72 #define TF_TEXTCONTROL  0x80
73 #define TF_HASFONT      0x08
74 #define TF_HASCOLOR     0x04
75 #define TF_HASYOFFSET   0x02
76 #define TF_HASXOFFSET   0x01
77 
78 #define FF_WIDECODES    0x01
79 #define FF_BOLD         0x02
80 #define FF_ITALIC       0x04
81 #define FF_ANSI         0x08
82 #define FF_SHIFTJIS     0x10
83 #define FF_UNICODE      0x20
84 
85 #define FF2_BOLD         0x01
86 #define FF2_ITALIC       0x02
87 #define FF2_WIDECODES    0x04
88 #define FF2_WIDEOFFSETS  0x08
89 #define FF2_ANSI         0x10
90 #define FF2_UNICODE      0x20
91 #define FF2_SHIFTJIS     0x40
92 #define FF2_LAYOUT	 0x80
93 
swf_FontIsItalic(SWFFONT * f)94 int swf_FontIsItalic(SWFFONT * f)
95 {
96     return f->style & FONT_STYLE_ITALIC;
97 }
98 
swf_FontIsBold(SWFFONT * f)99 int swf_FontIsBold(SWFFONT * f)
100 {
101     return f->style & FONT_STYLE_BOLD;
102 }
103 
104 static const int WRITEFONTID = 0x4e46;	// font id for WriteFont and ReadFont
105 
swf_FontEnumerate(SWF * swf,void (* FontCallback)(void *,U16,U8 *),void * self)106 int swf_FontEnumerate(SWF * swf, void (*FontCallback) (void*, U16, U8 *), void*self)
107 {
108     int n;
109     TAG *t;
110     if (!swf)
111 	return -1;
112     t = swf->firstTag;
113     n = 0;
114 
115     while (t) {
116 	if (swf_isFontTag(t)) {
117 	    n++;
118 	    if (FontCallback) {
119 		U16 id;
120 		int l;
121 		U8 s[257];
122 		s[0] = 0;
123 		swf_SetTagPos(t, 0);
124 
125 		id = swf_GetU16(t);
126 		if (swf_GetTagID(t) == ST_DEFINEFONT2 || swf_GetTagID(t) == ST_DEFINEFONTINFO || swf_GetTagID(t) == ST_DEFINEFONTINFO2) {
127 		    swf_GetU16(t);
128 		    l = swf_GetU8(t);
129 		    swf_GetBlock(t, s, l);
130 		    s[l] = 0;
131 		}
132 
133 		(FontCallback) (self, id, s);
134 	    }
135 	}
136 	t = swf_NextTag(t);
137     }
138     return n;
139 }
140 
swf_FontExtract_DefineFont(int id,SWFFONT * f,TAG * t)141 int swf_FontExtract_DefineFont(int id, SWFFONT * f, TAG * t)
142 {
143     U16 fid;
144     swf_SetTagPos(t, 0);
145 
146     fid = swf_GetU16(t);
147     if ((!id) || (id == fid)) {
148 	U16 of;
149 	int n, i;
150 
151 	id = fid;
152 	f->version = 1;
153 	f->id = fid;
154 
155 	of = swf_GetU16(t);
156 	n = of / 2;
157 	f->numchars = n;
158 	f->glyph = (SWFGLYPH*)rfx_calloc(sizeof(SWFGLYPH) * n);
159 
160 	for (i = 1; i < n; i++)
161 	    swf_GetU16(t);
162 	for (i = 0; i < n; i++)
163 	    swf_GetSimpleShape(t, &f->glyph[i].shape);
164     }
165     return id;
166 }
167 
swf_FontExtract_DefineFontInfo(int id,SWFFONT * f,TAG * t)168 int swf_FontExtract_DefineFontInfo(int id, SWFFONT * f, TAG * t)
169 {
170     U16 fid;
171     U16 maxcode;
172     U8 flags;
173     swf_SetTagPos(t, 0);
174 
175     fid = swf_GetU16(t);
176     if (fid == id) {
177 	U8 l = swf_GetU8(t);
178 	int i;
179 
180 	if (f->version > 1) {
181 	    /* Especially with Flash MX, DefineFont2 may have FontInfo fields,
182 	       too. However, they only add little information to what's already
183 	       inside the DefineFont2 tag */
184 	    return id;
185 	}
186 
187 	if (f->name)
188 	    rfx_free(f->name);
189 
190 	f->name = (U8 *) rfx_alloc(l + 1);
191 	swf_GetBlock(t, f->name, l);
192 	f->name[l] = 0;
193 
194 	flags = swf_GetU8(t);
195 	if (flags & 2)
196 	    f->style |= FONT_STYLE_BOLD;
197 	if (flags & 4)
198 	    f->style |= FONT_STYLE_ITALIC;
199 	if (flags & 8)
200 	    f->encoding |= FONT_ENCODING_ANSI;
201 	if (flags & 16)
202 	    f->encoding |= FONT_ENCODING_SHIFTJIS;
203 	if (flags & 32)
204 	    f->encoding |= FONT_ENCODING_UNICODE;
205 
206 	if (t->id == ST_DEFINEFONTINFO2) {
207 	    f->language = swf_GetU8(t);
208 	}
209 
210 	f->glyph2ascii = (U16 *) rfx_alloc(sizeof(U16) * f->numchars);
211 	maxcode = 0;
212 	for (i = 0; i < f->numchars; i++) {
213 	    f->glyph2ascii[i] = ((flags & FF_WIDECODES) ? swf_GetU16(t) : swf_GetU8(t));
214 	    if (f->glyph2ascii[i] > maxcode)
215 		maxcode = f->glyph2ascii[i];
216 	}
217 	maxcode++;
218 	if (maxcode < 256)
219 	    maxcode = 256;
220 	f->maxascii = maxcode;
221 	f->ascii2glyph = (int *) rfx_alloc(sizeof(int) * maxcode);
222 	memset(f->ascii2glyph, -1, sizeof(int) * maxcode);
223 
224 	for (i = 0; i < f->numchars; i++)
225 	    f->ascii2glyph[f->glyph2ascii[i]] = i;
226     }
227     return id;
228 }
229 
swf_FontExtract_GlyphNames(int id,SWFFONT * f,TAG * tag)230 int swf_FontExtract_GlyphNames(int id, SWFFONT * f, TAG * tag)
231 {
232     U16 fid;
233     swf_SetTagPos(tag, 0);
234 
235     fid = swf_GetU16(tag);
236 
237     if (fid == id) {
238 	int num = swf_GetU16(tag);
239 	int t;
240 	f->glyphnames = (char**)rfx_alloc(sizeof(char *) * num);
241 	for (t = 0; t < num; t++) {
242 	    f->glyphnames[t] = strdup(swf_GetString(tag));
243 	}
244     }
245     return id;
246 }
247 
248 
swf_FontExtract_DefineFont2(int id,SWFFONT * font,TAG * tag)249 int swf_FontExtract_DefineFont2(int id, SWFFONT * font, TAG * tag)
250 {
251     int t, glyphcount;
252     int maxcode;
253     int fid;
254     U32 offset_start;
255     U32 *offset;
256     U8 flags1, langcode, namelen;
257     swf_SetTagPos(tag, 0);
258     font->version = tag->id==ST_DEFINEFONT3?3:2;
259     fid = swf_GetU16(tag);
260     if (id && id != fid)
261 	return id;
262     font->id = fid;
263     flags1 = swf_GetU8(tag);
264     langcode = swf_GetU8(tag);	//reserved flags
265 
266     if (flags1 & 1)
267 	font->style |= FONT_STYLE_BOLD;
268     if (flags1 & 2)
269 	font->style |= FONT_STYLE_ITALIC;
270     if (flags1 & 16)
271 	font->encoding |= FONT_ENCODING_ANSI;
272     if (flags1 & 32)
273 	font->encoding |= FONT_ENCODING_UNICODE;
274     if (flags1 & 64)
275 	font->encoding |= FONT_ENCODING_SHIFTJIS;
276 
277     namelen = swf_GetU8(tag);
278     font->name = (U8 *) rfx_alloc(namelen + 1);
279     font->name[namelen] = 0;
280     swf_GetBlock(tag, font->name, namelen);
281     glyphcount = swf_GetU16(tag);
282     font->numchars = glyphcount;
283 
284     font->glyph = (SWFGLYPH *) rfx_calloc(sizeof(SWFGLYPH) * glyphcount);
285     font->glyph2ascii = (U16 *) rfx_calloc(sizeof(U16) * glyphcount);
286 
287     offset = (U32*)rfx_calloc(sizeof(U32)*(glyphcount+1));
288     offset_start = tag->pos;
289 
290     if (flags1 & 8) {		// wide offsets
291 	for (t = 0; t < glyphcount; t++)
292 	    offset[t] = swf_GetU32(tag);	//offset[t]
293 
294 	if (glyphcount)		/* this _if_ is not in the specs */
295 	    offset[glyphcount] = swf_GetU32(tag);	// fontcodeoffset
296 	else
297 	    offset[glyphcount] = tag->pos;
298     } else {
299 	for (t = 0; t < glyphcount; t++)
300 	    offset[t] = swf_GetU16(tag);	//offset[t]
301 
302 	if (glyphcount)		/* this _if_ is not in the specs */
303 	    offset[glyphcount] = swf_GetU16(tag);	// fontcodeoffset
304 	else
305 	    offset[glyphcount] = tag->pos;
306     }
307     for (t = 0; t < glyphcount; t++) {
308 	swf_SetTagPos(tag, offset[t]+offset_start);
309 	swf_GetSimpleShape(tag, &(font->glyph[t].shape));
310     }
311 
312     if(glyphcount)
313         swf_SetTagPos(tag, offset[glyphcount]+offset_start);
314 
315     free(offset);
316 
317     maxcode = 0;
318     for (t = 0; t < glyphcount; t++) {
319 	int code;
320 	if (flags1 & 4)		// wide codes (always on for definefont3)
321 	    code = swf_GetU16(tag);
322 	else
323 	    code = swf_GetU8(tag);
324 	font->glyph2ascii[t] = code;
325 	if (code > maxcode)
326 	    maxcode = code;
327     }
328     maxcode++;
329     if (maxcode < 256)
330 	maxcode = 256;
331     font->maxascii = maxcode;
332     font->ascii2glyph = (int *) rfx_alloc(sizeof(int) * maxcode);
333     memset(font->ascii2glyph, -1, sizeof(int) * maxcode);
334     for (t = 0; t < glyphcount; t++) {
335 	font->ascii2glyph[font->glyph2ascii[t]] = t;
336     }
337 
338     if (flags1 & 128) {		// has layout
339 	U16 kerningcount;
340 	font->layout = (SWFLAYOUT *) rfx_alloc(sizeof(SWFLAYOUT));
341 	font->layout->ascent = swf_GetU16(tag);
342 	font->layout->descent = swf_GetU16(tag);
343 	font->layout->leading = swf_GetU16(tag);
344 	for (t = 0; t < glyphcount; t++) {
345 	    S16 advance = swf_GetS16(tag);
346 	    font->glyph[t].advance = advance;
347 	}
348 	font->layout->bounds = (SRECT*)rfx_alloc(glyphcount * sizeof(SRECT));
349 	for (t = 0; t < glyphcount; t++) {
350 	    swf_ResetReadBits(tag);
351 	    swf_GetRect(tag, &font->layout->bounds[t]);
352 	    SRECT b = font->layout->bounds[t];
353 	    if((b.xmin|b.xmax|b.ymin|b.ymax) == 0) {
354 		// recalculate bounding box
355 		SHAPE2 *shape2 = swf_ShapeToShape2(font->glyph[t].shape);
356 		font->layout->bounds[t] = swf_GetShapeBoundingBox(shape2);
357 		swf_Shape2Free(shape2);free(shape2);
358 	    }
359 	}
360 
361 	kerningcount = swf_GetU16(tag);
362 	font->layout->kerningcount = kerningcount;
363 
364 	font->layout->kerning = (SWFKERNING *) rfx_alloc(sizeof(SWFKERNING) * kerningcount);
365 	if (kerningcount) {
366 	    font->layout->kerning = (SWFKERNING*)rfx_alloc(sizeof(*font->layout->kerning) * kerningcount);
367 	    for (t = 0; t < kerningcount; t++) {
368 		if (flags1 & 4) {	// wide codes
369 		    font->layout->kerning[t].char1 = swf_GetU16(tag);
370 		    font->layout->kerning[t].char2 = swf_GetU16(tag);
371 		} else {
372 		    font->layout->kerning[t].char1 = swf_GetU8(tag);
373 		    font->layout->kerning[t].char2 = swf_GetU8(tag);
374 		}
375 		font->layout->kerning[t].adjustment = swf_GetS16(tag);
376 	    }
377 	}
378     }
379     return font->id;
380 }
381 
swf_FontExtract_DefineFontAlignZones(int id,SWFFONT * font,TAG * tag)382 int swf_FontExtract_DefineFontAlignZones(int id, SWFFONT * font, TAG * tag)
383 {
384     U16 fid;
385     swf_SetTagPos(tag, 0);
386     fid = swf_GetU16(tag);
387 
388     if (fid == id) {
389 	font->alignzone_flags = swf_GetU8(tag);
390 	font->alignzones = rfx_calloc(sizeof(ALIGNZONE)*font->numchars);
391 	int i=0;
392 	while(tag->pos < tag->len) {
393 	    if(i>=font->numchars)
394 		break;
395 	    int nr = swf_GetU8(tag); // should be 2
396 	    if(nr!=1 && nr!=2) {
397 		fprintf(stderr, "rfxswf: Can't parse alignzone tags with %d zones", nr);
398 		break;
399 	    }
400 	    U16 x = swf_GetU16(tag);
401 	    U16 y = swf_GetU16(tag);
402 	    U16 dx = (nr==2)?swf_GetU16(tag):0xffff;
403 	    U16 dy = (nr==2)?swf_GetU16(tag):0xffff;
404 	    U8 xy = swf_GetU8(tag);
405 
406 #ifdef DEBUG_RFXSWF
407 	    if((!(xy&1) && (x!=0 || (dx!=0 && dx!=0xffff))) ||
408 	       (!(xy&2) && (y!=0 || (dy!=0 && dy!=0xffff)))) {
409 		fprintf(stderr, "Warning: weird combination of alignzone bits and values (%d x:%04x-%04x y:%04x-%04x)\n", xy,
410 			x,dx,y,dy);
411 	    }
412 #endif
413 	    if(!(xy&1)) {
414 		x = 0xffff;
415 		dx = 0xffff;
416 	    } else if(!(xy&2)) {
417 		y = 0xffff;
418 		dy = 0xffff;
419 	    }
420 	    font->alignzones[i].x = x;
421 	    font->alignzones[i].y = y;
422 	    font->alignzones[i].dx = dx;
423 	    font->alignzones[i].dy = dy;
424 	    i++;
425 	}
426     }
427     return id;
428 }
429 
430 
431 #define FEDTJ_PRINT  0x01
432 #define FEDTJ_MODIFY 0x02
433 #define FEDTJ_CALLBACK 0x04
434 
435 static int
swf_FontExtract_DefineTextCallback(int id,SWFFONT * f,TAG * t,int jobs,void (* callback)(void * self,int * chars,int * xpos,int nr,int fontid,int fontsize,int xstart,int ystart,RGBA * color),void * self)436 swf_FontExtract_DefineTextCallback(int id, SWFFONT * f, TAG * t, int jobs,
437 				   void (*callback) (void *self,
438 						     int *chars, int *xpos, int nr, int fontid, int fontsize, int xstart, int ystart, RGBA * color), void *self)
439 {
440     U16 cid;
441     SRECT r;
442     MATRIX m;
443     U8 gbits, abits;
444     int fid = -1;
445     RGBA color;
446     int x = 0, y = 0;
447     int fontsize = 0;
448 
449     memset(&color, 0, sizeof(color));
450 
451     swf_SetTagPos(t, 0);
452 
453     cid = swf_GetU16(t);
454     swf_GetRect(t, &r);
455     swf_GetMatrix(t, &m);
456     gbits = swf_GetU8(t);
457     abits = swf_GetU8(t);
458 
459     while (1) {
460 	int flags, num;
461 	flags = swf_GetU8(t);
462 	if (!flags)
463 	    break;
464 
465 	if (flags & TF_TEXTCONTROL) {
466 	    if (flags & TF_HASFONT)
467 		fid = swf_GetU16(t);
468 	    if (flags & TF_HASCOLOR) {
469 		color.r = swf_GetU8(t);	// rgb
470 		color.g = swf_GetU8(t);
471 		color.b = swf_GetU8(t);
472 		if (swf_GetTagID(t) == ST_DEFINETEXT2)
473 		    color.a = swf_GetU8(t);
474 		else
475 		    color.a = 255;
476 	    }
477 	    if (flags & TF_HASXOFFSET)
478 		x = swf_GetS16(t);
479 	    if (flags & TF_HASYOFFSET)
480 		y = swf_GetS16(t);
481 	    if (flags & TF_HASFONT)
482 		fontsize = swf_GetU16(t);
483 	}
484 
485 	num = swf_GetU8(t);
486 	if (!num)
487 	    break;
488 
489 	{
490 	    int i;
491 	    int buf[256];
492 	    int advance[256];
493 	    int xpos = 0;
494 	    for (i = 0; i < num; i++) {
495 		int glyph;
496 		int adv = 0;
497 		advance[i] = xpos;
498 		glyph = swf_GetBits(t, gbits);
499 		adv = swf_GetBits(t, abits);
500 		xpos += adv;
501 
502 		if (id == fid) {
503 		    if (jobs & FEDTJ_PRINT) {
504 			int code = f->glyph2ascii[glyph];
505 			printf("%lc", code);
506 		    }
507 		    if (jobs & FEDTJ_MODIFY)
508 			f->glyph[glyph].advance = adv * 20;	//?
509 		}
510 
511 		buf[i] = glyph;
512 	    }
513 	    if ((id == fid) && (jobs & FEDTJ_PRINT))
514 		printf("\n");
515 	    if (jobs & FEDTJ_CALLBACK)
516 		callback(self, buf, advance, num, fid, fontsize, x, y, &color);
517 	    x += xpos;
518 	}
519     }
520 
521     return id;
522 }
523 
swf_ParseDefineText(TAG * tag,void (* callback)(void * self,int * chars,int * xpos,int nr,int fontid,int fontsize,int xstart,int ystart,RGBA * color),void * self)524 int swf_ParseDefineText(TAG * tag,
525 		    void (*callback) (void *self, int *chars, int *xpos, int nr, int fontid, int fontsize, int xstart, int ystart, RGBA * color), void *self)
526 {
527     return swf_FontExtract_DefineTextCallback(-1, 0, tag, FEDTJ_CALLBACK, callback, self);
528 }
529 
swf_FontExtract_DefineText(int id,SWFFONT * f,TAG * t,int jobs)530 int swf_FontExtract_DefineText(int id, SWFFONT * f, TAG * t, int jobs)
531 {
532     return swf_FontExtract_DefineTextCallback(id, f, t, jobs, 0, 0);
533 }
534 
535 typedef struct _usagetmp {
536     SWFFONT*font;
537     int lastx,lasty;
538     int last;
539 } usagetmp_t;
updateusage(void * self,int * chars,int * xpos,int nr,int fontid,int fontsize,int xstart,int ystart,RGBA * color)540 static void updateusage(void *self, int *chars, int *xpos, int nr,
541 	                int fontid, int fontsize, int xstart, int ystart, RGBA * color)
542 {
543     usagetmp_t*u = (usagetmp_t*)self;
544     if(!u->font->use) {
545 	swf_FontInitUsage(u->font);
546     }
547     if(fontid!=u->font->id)
548 	return;
549 
550     int t;
551     for(t=0;t<nr;t++) {
552 	int x=xpos[t];
553 	int y=ystart;
554 	int c = chars[t];
555 	if(c<0 || c>u->font->numchars)
556 	    continue;
557 	swf_FontUseGlyph(u->font, c, fontsize);
558 	if(u->lasty == y && x>=u->lastx-200 && abs(u->lastx-x)<200 &&
559 	   u->last!=c && !swf_ShapeIsEmpty(u->font->glyph[u->last].shape) &&
560 	   !swf_ShapeIsEmpty(u->font->glyph[c].shape))
561 	{
562 	    swf_FontUsePair(u->font, u->last, c);
563 	}
564 	u->lasty = y;
565 	/* FIXME: do we still need to divide advance by 20 for definefont3? */
566 	u->lastx = x + (u->font->glyph[c].advance*fontsize/20480);
567 	u->last = c;
568     }
569 }
570 
swf_FontUpdateUsage(SWFFONT * f,TAG * tag)571 void swf_FontUpdateUsage(SWFFONT*f, TAG* tag)
572 {
573     usagetmp_t u;
574     u.font = f;
575     u.lastx = -0x80000000;
576     u.lasty = -0x80000000;
577     u.last = 0;
578     swf_ParseDefineText(tag, updateusage, &u);
579 }
580 
swf_FontExtract(SWF * swf,int id,SWFFONT ** font)581 int swf_FontExtract(SWF * swf, int id, SWFFONT * *font)
582 {
583     TAG *t;
584     SWFFONT *f;
585 
586     if ((!swf) || (!font))
587 	return -1;
588 
589     f = (SWFFONT *) rfx_calloc(sizeof(SWFFONT));
590 
591     t = swf->firstTag;
592 
593     while (t) {
594 	int nid = 0;
595 	switch (swf_GetTagID(t)) {
596 	case ST_DEFINEFONT:
597 	    nid = swf_FontExtract_DefineFont(id, f, t);
598 	    break;
599 
600 	case ST_DEFINEFONT2:
601 	case ST_DEFINEFONT3:
602 	    nid = swf_FontExtract_DefineFont2(id, f, t);
603 	    break;
604 
605 	case ST_DEFINEFONTALIGNZONES:
606 	    nid = swf_FontExtract_DefineFontAlignZones(id, f, t);
607 	    break;
608 
609 	case ST_DEFINEFONTINFO:
610 	case ST_DEFINEFONTINFO2:
611 	    nid = swf_FontExtract_DefineFontInfo(id, f, t);
612 	    break;
613 
614 	case ST_DEFINETEXT:
615 	case ST_DEFINETEXT2:
616 	    if(!f->layout) {
617 		nid = swf_FontExtract_DefineText(id, f, t, FEDTJ_MODIFY);
618 	    }
619 	    if(f->version>=3 && f->layout)
620 		swf_FontUpdateUsage(f, t);
621 	    break;
622 
623 	case ST_GLYPHNAMES:
624 	    nid = swf_FontExtract_GlyphNames(id, f, t);
625 	    break;
626 	}
627 	if (nid > 0)
628 	    id = nid;
629 	t = swf_NextTag(t);
630     }
631     if (f->id != id) {
632 	rfx_free(f);
633 	f = 0;
634     }
635     font[0] = f;
636     return 0;
637 }
638 
swf_FontSetID(SWFFONT * f,U16 id)639 int swf_FontSetID(SWFFONT * f, U16 id)
640 {
641     if (!f)
642 	return -1;
643     f->id = id;
644     return 0;
645 }
646 
swf_LayoutFree(SWFLAYOUT * l)647 void swf_LayoutFree(SWFLAYOUT * l)
648 {
649     if (l) {
650 	if (l->kerning)
651 	    rfx_free(l->kerning);
652 	l->kerning = NULL;
653 	if (l->bounds)
654 	    rfx_free(l->bounds);
655 	l->bounds = NULL;
656     }
657     rfx_free(l);
658 }
659 
660 
font_freeglyphnames(SWFFONT * f)661 static void font_freeglyphnames(SWFFONT*f)
662 {
663     if (f->glyphnames)
664     {
665         int t;
666         for (t = 0; t < f->numchars; t++)
667         {
668             if (f->glyphnames[t])
669             {
670                 rfx_free(f->glyphnames[t]);
671                 f->glyphnames[t] = 0;
672             }
673         }
674         rfx_free(f->glyphnames);
675         f->glyphnames = 0;
676 	}
677 }
font_freeusage(SWFFONT * f)678 static void font_freeusage(SWFFONT*f)
679 {
680     if (f->use) {
681 	if(f->use->chars) {
682 	    rfx_free(f->use->chars);f->use->chars = 0;
683 	}
684 	if(f->use->neighbors) {
685 	    rfx_free(f->use->neighbors);f->use->neighbors = 0;
686 	}
687 	if(f->use->neighbors_hash) {
688 	    rfx_free(f->use->neighbors_hash);f->use->neighbors_hash = 0;
689 	}
690 	rfx_free(f->use); f->use = 0;
691     }
692 }
font_freelayout(SWFFONT * f)693 static void font_freelayout(SWFFONT*f)
694 {
695     if (f->layout) {
696 	swf_LayoutFree(f->layout);
697 	f->layout = 0;
698     }
699 }
font_freename(SWFFONT * f)700 static void font_freename(SWFFONT*f)
701 {
702     if (f->name) {
703 	rfx_free(f->name);
704 	f->name = 0;
705     }
706 }
707 
swf_FontReduce_old(SWFFONT * f)708 int swf_FontReduce_old(SWFFONT * f)
709 {
710     int i, j;
711     int max_unicode = 0;
712     if ((!f) || (!f->use) || f->use->is_reduced)
713 	return -1;
714 
715     j = 0;
716 
717     for (i = 0; i < f->numchars; i++) {
718 	if (f->glyph[i].shape && f->use->chars[i]) {
719 	    f->glyph2ascii[j] = f->glyph2ascii[i];
720 	    f->glyph[j] = f->glyph[i];
721 	    f->use->chars[i] = j;
722 	    j++;
723 	} else {
724 	    f->glyph2ascii[i] = 0;
725 	    if(f->glyph[i].shape) {
726 		swf_ShapeFree(f->glyph[i].shape);
727 		f->glyph[i].shape = 0;
728 		f->glyph[i].advance = 0;
729 	    }
730 	    f->use->chars[i] = -1;
731 	    j++; //TODO: remove
732 	}
733     }
734     for (i = 0; i < f->maxascii; i++) {
735 	if(f->use->chars[f->ascii2glyph[i]]<0) {
736 	    f->ascii2glyph[i] = -1;
737 	} else {
738 	    f->ascii2glyph[i] = f->use->chars[f->ascii2glyph[i]];
739 	    max_unicode = i;
740 	}
741     }
742     f->maxascii = max_unicode;
743     f->use->is_reduced = 1;
744     f->numchars = j;
745     font_freelayout(f);
746     font_freeglyphnames(f);
747     font_freename(f);
748     return j;
749 }
750 
swf_FontReduce_swfc(SWFFONT * f)751 int swf_FontReduce_swfc(SWFFONT * f)
752 {
753     int i, j;
754     int max_unicode = 0;
755     if ((!f) || (!f->use) || f->use->is_reduced)
756 	return -1;
757 
758     font_freeglyphnames(f);
759 
760     j = 0;
761     for (i = 0; i < f->numchars; i++) {
762 	if (f->glyph[i].shape && f->use->chars[i]) {
763 	    f->glyph2ascii[j] = f->glyph2ascii[i];
764 	    if (f->layout)
765 	    	f->layout->bounds[j] = f->layout->bounds[i];
766 	    f->glyph[j] = f->glyph[i];
767 	    f->use->chars[i] = j;
768 	    j++;
769 	} else {
770 	    f->glyph2ascii[i] = 0;
771 	    if(f->glyph[i].shape) {
772 		swf_ShapeFree(f->glyph[i].shape);
773 		f->glyph[i].shape = 0;
774 		f->glyph[i].advance = 0;
775 	    }
776 	    f->use->chars[i] = -1;
777 	}
778     }
779     f->use->used_glyphs = j;
780     for (i = 0; i < f->maxascii; i++) {
781 	if(f->ascii2glyph[i] > -1) {
782 	    if (f->use->chars[f->ascii2glyph[i]]<0) {
783 	    	f->use->chars[f->ascii2glyph[i]] = 0;
784 	    	f->ascii2glyph[i] = -1;
785 	    } else {
786 	    	f->ascii2glyph[i] = f->use->chars[f->ascii2glyph[i]];
787 	    	f->use->chars[f->ascii2glyph[i]] = 1;
788 	    	max_unicode = i + 1;
789 	    }
790 	}
791     }
792     f->maxascii = max_unicode;
793     f->use->is_reduced = 1;
794     f->numchars = j;
795     font_freename(f);
796     return j;
797 }
798 
swf_FontReduce(SWFFONT * f)799 int swf_FontReduce(SWFFONT * f)
800 {
801     int i;
802     int max_unicode = 0;
803     int max_glyph = 0;
804     if ((!f) || (!f->use) || f->use->is_reduced)
805 	return -1;
806 
807     font_freelayout(f);
808     font_freeglyphnames(f);
809 
810     f->use->used_glyphs= 0;
811     for (i = 0; i < f->numchars; i++) {
812 	if(!f->use->chars[i]) {
813 	    if(f->glyph2ascii) {
814 		f->glyph2ascii[i] = 0;
815 	    }
816 	    if(f->glyph[i].shape) {
817 		swf_ShapeFree(f->glyph[i].shape);
818 		f->glyph[i].shape = 0;
819 		f->glyph[i].advance = 0;
820 	    }
821 //	    f->use->used_glyphs++;
822 	} else {
823 	    f->use->used_glyphs++;
824 	    max_glyph = i+1;
825 	}
826     }
827     for (i = 0; i < f->maxascii; i++) {
828 	if(f->ascii2glyph[i] > -1 && !f->use->chars[f->ascii2glyph[i]]) {
829 	    if(f->ascii2glyph) {
830 		f->ascii2glyph[i] = -1;
831 	    }
832 	} else {
833 	    max_unicode = i+1;
834 	}
835     }
836     f->maxascii = max_unicode;
837     f->numchars = max_glyph;
838     font_freename(f);
839     return 0;
840 }
841 
842 static SWFFONT* font_to_sort;
cmp_chars(const void * a,const void * b)843 int cmp_chars(const void*a, const void*b)
844 {
845     int x = *(const int*)a;
846     int y = *(const int*)b;
847     return 0;
848 }
849 
swf_FontSort(SWFFONT * font)850 void swf_FontSort(SWFFONT * font)
851 {
852     int i, j;
853     int *newplace;
854     int *newpos;
855     if (!font)
856 	return;
857 
858     newplace = (int*)rfx_alloc(sizeof(int) * font->numchars);
859 
860     for (i = 0; i < font->numchars; i++) {
861 	newplace[i] = i;
862     }
863     //qsort(newplace, sizeof(newplace[0]), font->numchars, cmp_chars);
864 
865     for (i = 0; i < font->numchars; i++)
866 	for (j = 0; j < i; j++) {
867 	    if (font->glyph2ascii[i] < font->glyph2ascii[j]) {
868 		int n1, n2;
869 		char *c1, *c2;
870 		SWFGLYPH g1, g2;
871 		SRECT r1, r2;
872 		n1 = newplace[i];
873 		n2 = newplace[j];
874 		newplace[j] = n1;
875 		newplace[i] = n2;
876 		n1 = font->glyph2ascii[i];
877 		n2 = font->glyph2ascii[j];
878 		font->glyph2ascii[j] = n1;
879 		font->glyph2ascii[i] = n2;
880 		g1 = font->glyph[i];
881 		g2 = font->glyph[j];
882 		font->glyph[j] = g1;
883 		font->glyph[i] = g2;
884 		if (font->glyphnames) {
885 		    c1 = font->glyphnames[i];
886 		    c2 = font->glyphnames[j];
887 		    font->glyphnames[j] = c1;
888 		    font->glyphnames[i] = c2;
889 		}
890 		if (font->layout) {
891 		    r1 = font->layout->bounds[i];
892 		    r2 = font->layout->bounds[j];
893 		    font->layout->bounds[j] = r1;
894 		    font->layout->bounds[i] = r2;
895 		}
896 	    }
897 	}
898     newpos = (int*)rfx_alloc(sizeof(int) * font->numchars);
899     for (i = 0; i < font->numchars; i++) {
900 	newpos[newplace[i]] = i;
901     }
902     for (i = 0; i < font->maxascii; i++) {
903 	if (font->ascii2glyph[i] >= 0)
904 	    font->ascii2glyph[i] = newpos[font->ascii2glyph[i]];
905     }
906 
907     rfx_free(newplace);
908     font->glyph2glyph = newpos;
909 }
910 
swf_FontPrepareForEditText(SWFFONT * font)911 void swf_FontPrepareForEditText(SWFFONT * font)
912 {
913     if (!font->layout)
914 	swf_FontCreateLayout(font);
915     swf_FontSort(font);
916 }
917 
swf_FontInitUsage(SWFFONT * f)918 int swf_FontInitUsage(SWFFONT * f)
919 {
920     if (!f)
921 	return -1;
922     if(f->use) {
923 	fprintf(stderr, "Usage initialized twice");
924 	return -1;
925     }
926     f->use = (FONTUSAGE*)rfx_calloc(sizeof(FONTUSAGE));
927     f->use->smallest_size = 0xffff;
928     f->use->chars = (int*)rfx_calloc(sizeof(f->use->chars[0]) * f->numchars);
929     return 0;
930 }
931 
swf_FontClearUsage(SWFFONT * f)932 void swf_FontClearUsage(SWFFONT * f)
933 {
934     if (!f || !f->use)
935 	return;
936     rfx_free(f->use->chars); f->use->chars = 0;
937     rfx_free(f->use); f->use = 0;
938 }
939 
swf_FontUse(SWFFONT * f,U8 * s)940 int swf_FontUse(SWFFONT * f, U8 * s)
941 {
942     if( (!s))
943 	return -1;
944     while (*s) {
945 	if(*s < f->maxascii && f->ascii2glyph[*s]>=0)
946 	    swf_FontUseGlyph(f, f->ascii2glyph[*s], /*FIXME*/0xffff);
947 	s++;
948     }
949     return 0;
950 }
951 
swf_FontUseUTF8(SWFFONT * f,const U8 * s,U16 size)952 int swf_FontUseUTF8(SWFFONT * f, const U8 * s, U16 size)
953 {
954     if( (!s))
955 	return -1;
956     int ascii;
957     while (*s)
958     {
959     	ascii = readUTF8char((U8**)&s);
960 	if(ascii < f->maxascii && f->ascii2glyph[ascii]>=0)
961 	    swf_FontUseGlyph(f, f->ascii2glyph[ascii], size);
962     }
963     return 0;
964 }
965 
swf_FontUseAll(SWFFONT * f)966 int swf_FontUseAll(SWFFONT* f)
967 {
968     int i;
969 
970     if (!f->use)
971 	swf_FontInitUsage(f);
972     for (i = 0; i < f->numchars; i++)
973     	f->use->chars[i] = 1;
974     f->use->used_glyphs = f->numchars;
975     return 0;
976 }
977 
hash2(int char1,int char2)978 static unsigned hash2(int char1, int char2)
979 {
980     unsigned hash = char1^(char2<<8);
981     hash += (hash << 3);
982     hash ^= (hash >> 11);
983     hash += (hash << 15);
984     return hash;
985 }
hashadd(FONTUSAGE * u,int char1,int char2,int nr)986 static void hashadd(FONTUSAGE*u, int char1, int char2, int nr)
987 {
988     unsigned hash = hash2(char1, char2);
989     while(1) {
990 	hash = hash%u->neighbors_hash_size;
991 	if(!u->neighbors_hash[hash]) {
992 	   u->neighbors_hash[hash] = nr+1;
993 	   return;
994 	}
995 	hash++;
996     }
997 }
swf_FontUseGetPair(SWFFONT * f,int char1,int char2)998 int swf_FontUseGetPair(SWFFONT * f, int char1, int char2)
999 {
1000     FONTUSAGE*u = f->use;
1001     if(!u || !u->neighbors_hash_size)
1002 	return 0;
1003     unsigned hash = hash2(char1, char2);
1004     while(1) {
1005 	hash = hash%u->neighbors_hash_size;
1006 	int pos = u->neighbors_hash[hash];
1007 	if(!pos)
1008 	    return 0;
1009 	if(pos &&
1010 	   u->neighbors[pos-1].char1 == char1 &&
1011 	   u->neighbors[pos-1].char2 == char2) {
1012 	    return pos;
1013 	}
1014 	hash++;
1015     }
1016 
1017 }
swf_FontUsePair(SWFFONT * f,int char1,int char2)1018 void swf_FontUsePair(SWFFONT * f, int char1, int char2)
1019 {
1020     if (!f->use)
1021 	swf_FontInitUsage(f);
1022     FONTUSAGE*u = f->use;
1023 
1024     if(u->num_neighbors*3 >= u->neighbors_hash_size*2) {
1025 	if(u->neighbors_hash) {
1026 	    free(u->neighbors_hash);
1027 	}
1028 	u->neighbors_hash_size = u->neighbors_hash_size?u->neighbors_hash_size*2:1024;
1029 	u->neighbors_hash = rfx_calloc(u->neighbors_hash_size*sizeof(int));
1030 	int t;
1031 	for(t=0;t<u->num_neighbors;t++) {
1032 	    hashadd(u, u->neighbors[t].char1, u->neighbors[t].char2, t);
1033 	}
1034     }
1035 
1036     int nr = swf_FontUseGetPair(f, char1, char2);
1037     if(!nr) {
1038 	if(u->num_neighbors == u->neighbors_size) {
1039 	    u->neighbors_size += 4096;
1040 	    u->neighbors = rfx_realloc(u->neighbors, sizeof(SWFGLYPHPAIR)*u->neighbors_size);
1041 	}
1042 	u->neighbors[u->num_neighbors].char1 = char1;
1043 	u->neighbors[u->num_neighbors].char2 = char2;
1044 	u->neighbors[u->num_neighbors].num = 1;
1045 	hashadd(u, char1, char2, u->num_neighbors);
1046 	u->num_neighbors++;
1047     } else {
1048 	u->neighbors[nr-1].num++;
1049     }
1050 }
1051 
swf_FontUseGlyph(SWFFONT * f,int glyph,U16 size)1052 int swf_FontUseGlyph(SWFFONT * f, int glyph, U16 size)
1053 {
1054     if (!f->use)
1055 	swf_FontInitUsage(f);
1056     if(glyph < 0 || glyph >= f->numchars)
1057 	return -1;
1058     if(!f->use->chars[glyph])
1059 	f->use->used_glyphs++;
1060     f->use->chars[glyph] = 1;
1061     if(size && size < f->use->smallest_size)
1062 	f->use->smallest_size = size;
1063     return 0;
1064 }
1065 
swf_FontSetDefine(TAG * t,SWFFONT * f)1066 int swf_FontSetDefine(TAG * t, SWFFONT * f)
1067 {
1068     U16 *ofs = (U16 *) rfx_alloc(f->numchars * 2);
1069     int p, i, j;
1070 
1071     if ((!t) || (!f))
1072 	return -1;
1073     swf_ResetWriteBits(t);
1074     swf_SetU16(t, f->id);
1075 
1076     p = 0;
1077     j = 0;
1078     for (i = 0; i < f->numchars; i++)
1079 	if (f->glyph[i].shape) {
1080 	    ofs[j++] = p;
1081 	    p += swf_SetSimpleShape(NULL, f->glyph[i].shape);
1082 	}
1083 
1084     for (i = 0; i < j; i++)
1085 	swf_SetU16(t, ofs[i] + j * 2);
1086     if (!j) {
1087 	fprintf(stderr, "rfxswf: warning: Font is empty\n");
1088 	swf_SetU16(t, 0);
1089     }
1090 
1091     for (i = 0; i < f->numchars; i++)
1092 	if (f->glyph[i].shape)
1093 	    swf_SetSimpleShape(t, f->glyph[i].shape);
1094 
1095     swf_ResetWriteBits(t);
1096     rfx_free(ofs);
1097     return 0;
1098 }
1099 
fontSize(SWFFONT * font)1100 static inline int fontSize(SWFFONT * font)
1101 {
1102     int t;
1103     int size = 0;
1104     for (t = 0; t < font->numchars; t++) {
1105 	int l = 0;
1106 	if(font->glyph[t].shape)
1107 	    l = (font->glyph[t].shape->bitlen + 7) / 8;
1108 	else
1109 	    l = 8;
1110 	size += l + 1;
1111     }
1112     return size + (font->numchars + 1) * 2;
1113 }
1114 
swf_FontSetDefine2(TAG * tag,SWFFONT * f)1115 int swf_FontSetDefine2(TAG * tag, SWFFONT * f)
1116 {
1117     U8 flags = 0;
1118     int t;
1119     int pos;
1120     swf_SetU16(tag, f->id);
1121 
1122     if (f->layout) flags |= 128;		// haslayout
1123     if (f->numchars > 256)
1124 	flags |= 4;		// widecodes
1125     if (f->style & FONT_STYLE_BOLD)
1126 	flags |= 1;		// bold
1127     if (f->style & FONT_STYLE_ITALIC)
1128 	flags |= 2;		// italic
1129     if (f->maxascii >= 256)
1130 	flags |= 4;		//wide codecs
1131     if (fontSize(f) > 65535)
1132 	flags |= 8;		//wide offsets
1133     flags |= 8 | 4;		//FIXME: the above check doesn't work
1134 
1135     if (f->encoding & FONT_ENCODING_ANSI)
1136 	flags |= 16;		// ansi
1137     if (f->encoding & FONT_ENCODING_UNICODE)
1138 	flags |= 32;		// unicode
1139     if (f->encoding & FONT_ENCODING_SHIFTJIS)
1140 	flags |= 64;		// shiftjis
1141 
1142     swf_SetU8(tag, flags);
1143     swf_SetU8(tag, 0);		//reserved flags
1144     if (f->name) {
1145 	/* font name */
1146 	swf_SetU8(tag, strlen((const char*)f->name)+1);
1147 	swf_SetBlock(tag, f->name, strlen((const char*)f->name)+1);
1148     } else {
1149 	/* font name (="") */
1150 	swf_SetU8(tag, 1);
1151 	swf_SetU8(tag, 0);
1152     }
1153     /* number of glyphs */
1154     swf_SetU16(tag, f->numchars);
1155     /* font offset table */
1156     pos = tag->len;
1157     for (t = 0; t <= f->numchars; t++) {
1158 	if (flags & 8)
1159 	    swf_SetU32(tag, /* fontoffset */ 0);	/*placeholder */
1160 	else
1161 	    swf_SetU16(tag, /* fontoffset */ 0);	/*placeholder */
1162     }
1163 
1164     for (t = 0; t <= f->numchars; t++) {
1165 	if (flags & 8) {
1166 	    tag->data[pos + t * 4] = (tag->len - pos);
1167 	    tag->data[pos + t * 4 + 1] = (tag->len - pos) >> 8;
1168 	    tag->data[pos + t * 4 + 2] = (tag->len - pos) >> 16;
1169 	    tag->data[pos + t * 4 + 3] = (tag->len - pos) >> 24;
1170 	} else {
1171 	    if (tag->len - pos > 65535) {
1172 		fprintf(stderr, "Internal error: Font too big and WideOffsets flag not set\n");
1173 		exit(1);
1174 	    }
1175 	    tag->data[pos + t * 2] = (tag->len - pos);
1176 	    tag->data[pos + t * 2 + 1] = (tag->len - pos) >> 8;
1177 	}
1178 	if (t < f->numchars) {
1179 	    if(f->glyph[t].shape) {
1180 		swf_SetSimpleShape(tag, f->glyph[t].shape);
1181 	    } else {
1182 		swf_SetU8(tag, 0); //non-edge(1) + edge flags(5)
1183 	    }
1184 	}
1185     }
1186 
1187 
1188     /* font code table */
1189     for (t = 0; t < f->numchars; t++) {
1190 	if (flags & 4) {		/* wide codes */
1191 	    if(f->glyph2ascii[t]) {
1192 		swf_SetU16(tag, f->glyph2ascii[t]);
1193 	    } else {
1194 		swf_SetU16(tag, 0);
1195 	    }
1196 	} else {
1197 	    if(f->glyph2ascii[t]) {
1198 		swf_SetU8(tag, f->glyph2ascii[t]);
1199 	    } else {
1200 		swf_SetU8(tag, 0);
1201 	    }
1202 	}
1203     }
1204 
1205     if (f->layout) {
1206 	swf_SetU16(tag, f->layout->ascent);
1207 	swf_SetU16(tag, f->layout->descent);
1208 	swf_SetU16(tag, 0); // flash ignores leading
1209 
1210 	for (t = 0; t < f->numchars; t++)
1211 	    swf_SetU16(tag, f->glyph[t].advance);
1212 	for (t = 0; t < f->numchars; t++) {
1213 	    swf_ResetWriteBits(tag);
1214 	    /* not used by flash, so leave this empty */
1215 	    SRECT b = {0,0,0,0};
1216 	    swf_SetRect(tag, &b);
1217 	}
1218 	swf_SetU16(tag, f->layout->kerningcount);
1219 	for (t = 0; t < f->layout->kerningcount; t++) {
1220 	    if (flags & 4) {	/* wide codes */
1221 		swf_SetU16(tag, f->layout->kerning[t].char1);
1222 		swf_SetU16(tag, f->layout->kerning[t].char2);
1223 	    } else {
1224 		swf_SetU8(tag, f->layout->kerning[t].char1);
1225 		swf_SetU8(tag, f->layout->kerning[t].char2);
1226 	    }
1227 	    swf_SetU16(tag, f->layout->kerning[t].adjustment);
1228 	}
1229     }
1230     return 0;
1231 }
1232 
swf_FontAddLayout(SWFFONT * f,int ascent,int descent,int leading)1233 void swf_FontAddLayout(SWFFONT * f, int ascent, int descent, int leading)
1234 {
1235     f->layout = (SWFLAYOUT *) rfx_alloc(sizeof(SWFLAYOUT));
1236     f->layout->ascent = ascent;
1237     f->layout->descent = descent;
1238     f->layout->leading = leading;
1239     f->layout->kerningcount = 0;
1240     f->layout->kerning = 0;
1241     f->layout->bounds = (SRECT *) rfx_calloc(sizeof(SRECT) * f->numchars);
1242 }
1243 
swf_FontSetInfo(TAG * t,SWFFONT * f)1244 int swf_FontSetInfo(TAG * t, SWFFONT * f)
1245 {
1246     int l, i;
1247     U8 wide = 0;
1248     U8 flags = 0;
1249     if ((!t) || (!f))
1250 	return -1;
1251     swf_ResetWriteBits(t);
1252     swf_SetU16(t, f->id);
1253     l = f->name ? strlen((const char *)f->name) : 0;
1254     if (l > 255)
1255 	l = 255;
1256     swf_SetU8(t, l);
1257     if (l)
1258 	swf_SetBlock(t, f->name, l);
1259     if (f->numchars >= 256)
1260 	wide = 1;
1261 
1262     if (f->style & FONT_STYLE_BOLD)
1263 	flags |= 2;
1264     if (f->style & FONT_STYLE_ITALIC)
1265 	flags |= 4;
1266     if (f->style & FONT_ENCODING_ANSI)
1267 	flags |= 8;
1268     if (f->style & FONT_ENCODING_SHIFTJIS)
1269 	flags |= 16;
1270     if (f->style & FONT_ENCODING_UNICODE)
1271 	flags |= 32;
1272 
1273     swf_SetU8(t, (flags & 0xfe) | wide);
1274 
1275     for (i = 0; i < f->numchars; i++) {
1276 	if (f->glyph[i].shape) {
1277 	    int g2a = f->glyph2ascii?f->glyph2ascii[i]:0;
1278 	    wide ? swf_SetU16(t, g2a) : swf_SetU8(t, g2a);
1279 	}
1280     }
1281 
1282     return 0;
1283 }
1284 
swf_TextPrintDefineText(TAG * t,SWFFONT * f)1285 int swf_TextPrintDefineText(TAG * t, SWFFONT * f)
1286 {
1287     int id = swf_GetTagID(t);
1288     if ((id == ST_DEFINETEXT) || (id == ST_DEFINETEXT2))
1289 	swf_FontExtract_DefineText(f->id, f, t, FEDTJ_PRINT);
1290     else
1291 	return -1;
1292     return 0;
1293 }
1294 
font_freealignzones(SWFFONT * f)1295 static void font_freealignzones(SWFFONT * f)
1296 {
1297     if(f->alignzones)
1298 	free(f->alignzones);
1299     f->alignzones = 0;
1300 }
1301 
swf_FontFree(SWFFONT * f)1302 void swf_FontFree(SWFFONT * f)
1303 {
1304     int i;
1305     if (!f)
1306         return;
1307 
1308     if (f->glyph)
1309     {
1310         for (i = 0; i < f->numchars; i++)
1311             if (f->glyph[i].shape)
1312             {
1313                 swf_ShapeFree(f->glyph[i].shape);
1314                 f->glyph[i].shape = NULL;
1315             }
1316             rfx_free(f->glyph);
1317             f->glyph = NULL;
1318     }
1319     if (f->ascii2glyph)
1320     {
1321         rfx_free(f->ascii2glyph);
1322         f->ascii2glyph = NULL;
1323     }
1324     if (f->glyph2ascii)
1325     {
1326         rfx_free(f->glyph2ascii);
1327         f->glyph2ascii = NULL;
1328     }
1329     if (f->glyph2glyph) {
1330 	rfx_free(f->glyph2glyph);
1331 	f->glyph2glyph = NULL;
1332     }
1333     font_freename(f);
1334     font_freelayout(f);
1335     font_freeglyphnames(f);
1336     font_freeusage(f);
1337     font_freealignzones(f);
1338 
1339     rfx_free(f);
1340 }
1341 
swf_TextSetInfoRecord(TAG * t,SWFFONT * font,U16 size,RGBA * color,int x,int y)1342 int swf_TextSetInfoRecord(TAG * t, SWFFONT * font, U16 size, RGBA * color, int x, int y)
1343 {
1344     U8 flags;
1345     if (!t)
1346 	return -1;
1347 
1348     flags = TF_TEXTCONTROL | (font ? TF_HASFONT : 0) | (color ? TF_HASCOLOR : 0) | (x ? TF_HASXOFFSET : 0)
1349 	| (y ? TF_HASYOFFSET : 0);
1350 
1351     swf_SetU8(t, flags);
1352     if (font)
1353 	swf_SetU16(t, font->id);
1354     if (color) {
1355 	if (swf_GetTagID(t) == ST_DEFINETEXT2)
1356 	    swf_SetRGBA(t, color);
1357 	else
1358 	    swf_SetRGB(t, color);
1359     }
1360     if (x) {
1361 	if(x != SET_TO_ZERO) {
1362 	    if(x>32767 || x<-32768)
1363 		fprintf(stderr, "Warning: Horizontal char position overflow: %d\n", x);
1364 	    swf_SetS16(t, x);
1365 	} else {
1366 	    swf_SetS16(t, 0);
1367 	}
1368     }
1369     if (y) {
1370 	if(y != SET_TO_ZERO) {
1371 	    if(y>32767 || y<-32768)
1372 		fprintf(stderr, "Warning: Vertical char position overflow: %d\n", y);
1373 	    swf_SetS16(t, y);
1374 	} else {
1375 	    swf_SetS16(t, 0);
1376 	}
1377     }
1378     if (font)
1379 	swf_SetU16(t, size);
1380 
1381     return 0;
1382 }
1383 
swf_TextCountBits2(SWFFONT * font,U8 * s,int scale,U8 * gbits,U8 * abits,char * encoding)1384 static int swf_TextCountBits2(SWFFONT * font, U8 * s, int scale, U8 * gbits, U8 * abits, char *encoding)
1385 {
1386     U16 g, a;
1387     char utf8 = 0;
1388     if ((!s) || (!font) || ((!gbits) && (!abits)) || (!font->ascii2glyph))
1389 	return -1;
1390     g = a = 0;
1391 
1392     if (!strcmp(encoding, "UTF8"))
1393 	utf8 = 1;
1394     else if (!strcmp(encoding, "iso-8859-1"))
1395 	utf8 = 0;
1396     else
1397 	fprintf(stderr, "Unknown encoding: %s", encoding);
1398 
1399     while (*s) {
1400 	int glyph = -1, c;
1401 
1402 	if (!utf8)
1403 	    c = *s++;
1404 	else
1405 	    c = readUTF8char(&s);
1406 
1407 	if (c < font->maxascii)
1408 	    glyph = font->ascii2glyph[c];
1409 	if (glyph >= 0) {
1410 	    g = swf_CountUBits(glyph, g);
1411 	    a = swf_CountBits(((((U32) font->glyph[glyph].advance) * scale) / 20) / 100, a);
1412 	}
1413     }
1414 
1415     if (gbits)
1416 	gbits[0] = (U8) g;
1417     if (abits)
1418 	abits[0] = (U8) a;
1419     return 0;
1420 }
1421 
swf_TextSetCharRecord2(TAG * t,SWFFONT * font,U8 * s,int scale,U8 gbits,U8 abits,char * encoding)1422 static int swf_TextSetCharRecord2(TAG * t, SWFFONT * font, U8 * s, int scale, U8 gbits, U8 abits, char *encoding)
1423 {
1424     int l = 0, pos;
1425     char utf8 = 0;
1426 
1427     if ((!t) || (!font) || (!s) || (!font->ascii2glyph))
1428 	return -1;
1429 
1430     if (!strcmp(encoding, "UTF8"))
1431 	utf8 = 1;
1432     else if (!strcmp(encoding, "iso-8859-1"))
1433 	utf8 = 0;
1434     else
1435 	fprintf(stderr, "Unknown encoding: %s", encoding);
1436 
1437     pos = t->len;
1438     swf_SetU8(t, l);		//placeholder
1439 
1440     while (*s) {
1441 	int g = -1, c;
1442 
1443 	if (!utf8)
1444 	    c = *s++;
1445 	else
1446 	    c = readUTF8char(&s);
1447 
1448 	if (c < font->maxascii)
1449 	    g = font->ascii2glyph[c];
1450 	if (g >= 0) {
1451 	    swf_SetBits(t, g, gbits);
1452 	    swf_SetBits(t, ((((U32) font->glyph[g].advance) * scale) / 20) / 100, abits);
1453 	    l++;
1454 	    /* We split into 127 characters per text field.
1455 	       We could do 255, by the (formerly wrong) flash specification,
1456 	       but some SWF parsing code out there still assumes that char blocks
1457 	       are at max 127 characters, and it would save only a few bits.
1458 	    */
1459 	    if (l == 0x7f)
1460 		break;
1461 	}
1462     }
1463 
1464     PUT8(&t->data[pos], l);
1465 
1466     swf_ResetWriteBits(t);
1467     return 0;
1468 }
1469 
swf_TextCountBits(SWFFONT * font,U8 * s,int scale,U8 * gbits,U8 * abits)1470 int swf_TextCountBits(SWFFONT * font, U8 * s, int scale, U8 * gbits, U8 * abits)
1471 {
1472     return swf_TextCountBits2(font, s, scale, gbits, abits, "iso-8859-1");
1473 }
1474 
swf_TextSetCharRecord(TAG * t,SWFFONT * font,U8 * s,int scale,U8 gbits,U8 abits)1475 int swf_TextSetCharRecord(TAG * t, SWFFONT * font, U8 * s, int scale, U8 gbits, U8 abits)
1476 {
1477     return swf_TextSetCharRecord2(t, font, s, scale, gbits, abits, "iso-8859-1");
1478 }
1479 
swf_TextCountBitsUTF8(SWFFONT * font,U8 * s,int scale,U8 * gbits,U8 * abits)1480 int swf_TextCountBitsUTF8(SWFFONT * font, U8 * s, int scale, U8 * gbits, U8 * abits)
1481 {
1482     return swf_TextCountBits2(font, s, scale, gbits, abits, "UTF8");
1483 }
1484 
swf_TextSetCharRecordUTF8(TAG * t,SWFFONT * font,U8 * s,int scale,U8 gbits,U8 abits)1485 int swf_TextSetCharRecordUTF8(TAG * t, SWFFONT * font, U8 * s, int scale, U8 gbits, U8 abits)
1486 {
1487     return swf_TextSetCharRecord2(t, font, s, scale, gbits, abits, "UTF8");
1488 }
1489 
swf_TextGetWidth(SWFFONT * font,U8 * s,int scale)1490 U32 swf_TextGetWidth(SWFFONT * font, U8 * s, int scale)
1491 {
1492     U32 res = 0;
1493 
1494     if (font && s) {
1495 	while (s[0]) {
1496 	    int g = -1;
1497 	    if (*s < font->maxascii)
1498 		g = font->ascii2glyph[*s];
1499 	    if (g >= 0)
1500 		res += font->glyph[g].advance / 20;
1501 	    s++;
1502 	}
1503 	if (scale)
1504 	    res = (res * scale) / 100;
1505     }
1506     return res;
1507 }
1508 
swf_TextCalculateBBoxUTF8(SWFFONT * font,U8 * s,int scale)1509 SRECT swf_TextCalculateBBoxUTF8(SWFFONT * font, U8 * s, int scale)
1510 {
1511     int xpos = 0;
1512     int ypos = 0;
1513     SRECT r;
1514     swf_GetRect(0, &r);
1515     while (*s) {
1516 	int c = readUTF8char(&s);
1517 	if(c==13 || c==10) {
1518 	    if(s[0] == 10) {
1519 		s++;
1520 	    }
1521 	    xpos=0;
1522 	    ypos+=font->layout->leading;
1523 	    continue;
1524 	}
1525 	if (c < font->maxascii) {
1526 	    int g = font->ascii2glyph[c];
1527 	    if (g >= 0) {
1528 		SRECT rn = font->layout->bounds[g];
1529 		rn.xmin = (rn.xmin * scale) / 20 / 100 + xpos;
1530 		rn.xmax = (rn.xmax * scale) / 20 / 100 + xpos;
1531 		rn.ymin = (rn.ymin * scale) / 20 / 100 + ypos;
1532 		rn.ymax = (rn.ymax * scale) / 20 / 100 + ypos;
1533 		swf_ExpandRect2(&r, &rn);
1534 		xpos += (font->glyph[g].advance * scale) / 20 / 100;
1535 	    }
1536 	}
1537     }
1538     return r;
1539 }
1540 
1541 
swf_ReadFont(const char * filename)1542 SWFFONT *swf_ReadFont(const char *filename)
1543 {
1544     int f;
1545     SWF swf;
1546     if (!filename)
1547 	return 0;
1548     f = open(filename, O_RDONLY|O_BINARY);
1549 
1550     if (f < 0 || swf_ReadSWF(f, &swf) < 0) {
1551 	fprintf(stderr, "%s is not a valid SWF font file or contains errors.\n", filename);
1552 	close(f);
1553 	return 0;
1554     } else {
1555 	SWFFONT *font;
1556 	close(f);
1557 	if (swf_FontExtract(&swf, WRITEFONTID, &font) < 0)
1558 	    return 0;
1559 	swf_FreeTags(&swf);
1560 	return font;
1561     }
1562 }
1563 
swf_SetEditText(TAG * tag,U16 flags,SRECT r,const char * text,RGBA * color,int maxlength,U16 font,U16 height,EditTextLayout * layout,const char * variable)1564 void swf_SetEditText(TAG * tag, U16 flags, SRECT r, const char *text, RGBA * color, int maxlength, U16 font, U16 height, EditTextLayout * layout, const char *variable)
1565 {
1566     swf_SetRect(tag, &r);
1567     swf_ResetWriteBits(tag);
1568 
1569     flags &= ~(ET_HASTEXT | ET_HASTEXTCOLOR | ET_HASMAXLENGTH | ET_HASFONT | ET_HASLAYOUT);
1570     if (text)
1571 	flags |= ET_HASTEXT;
1572     if (color)
1573 	flags |= ET_HASTEXTCOLOR;
1574     if (maxlength)
1575 	flags |= ET_HASMAXLENGTH;
1576     if (font)
1577 	flags |= ET_HASFONT;
1578     if (layout)
1579 	flags |= ET_HASLAYOUT;
1580 
1581     swf_SetBits(tag, flags, 16);
1582 
1583     if (flags & ET_HASFONT) {
1584 	swf_SetU16(tag, font);	//font
1585 	swf_SetU16(tag, height);	//fontheight
1586     }
1587     if (flags & ET_HASTEXTCOLOR) {
1588 	swf_SetRGBA(tag, color);
1589     }
1590     if (flags & ET_HASMAXLENGTH) {
1591 	swf_SetU16(tag, maxlength);	//maxlength
1592     }
1593     if (flags & ET_HASLAYOUT) {
1594 	swf_SetU8(tag, layout->align);	//align
1595 	swf_SetU16(tag, layout->leftmargin);	//left margin
1596 	swf_SetU16(tag, layout->rightmargin);	//right margin
1597 	swf_SetU16(tag, layout->indent);	//indent
1598 	swf_SetU16(tag, layout->leading);	//leading
1599     }
1600     swf_SetString(tag, variable);
1601     if (flags & ET_HASTEXT)
1602 	swf_SetString(tag, text);
1603 }
1604 
swf_SetDefineText(TAG * tag,SWFFONT * font,RGBA * rgb,const char * text,int scale)1605 SRECT swf_SetDefineText(TAG * tag, SWFFONT * font, RGBA * rgb, const char *text, int scale)
1606 {
1607     SRECT r;
1608     U8 gbits, abits;
1609     U8 *utext = (U8 *) strdup(text);
1610     U8 *upos = utext;
1611     int x = 0, y = 0;
1612     int pos = 0;
1613     int ystep = 0;
1614     if (font->layout) {
1615 	r = swf_TextCalculateBBoxUTF8(font, (U8*)text, scale * 20);
1616 	ystep = font->layout->leading;
1617     } else {
1618 	fprintf(stderr, "No layout information- can't compute text bbox accurately");
1619 	/* Hm, without layout information, we can't compute a bounding
1620 	   box. We could call swf_FontCreateLayout to create a layout,
1621 	   but the caller probably doesn't want us to mess up his font
1622 	   structure.
1623 	 */
1624 	r.xmin = r.ymin = 0;
1625 	r.xmax = r.ymax = 1024 * 20;
1626 	ystep = 100;
1627     }
1628 
1629     swf_SetRect(tag, &r);
1630 
1631     /* The text matrix is pretty boring, as it doesn't apply to
1632        individual characters, but rather whole text objects (or
1633        at least whole char records- haven't tested).
1634        So it can't do anything which we can't already do with
1635        the placeobject tag we use for placing the text on the scene.
1636      */
1637     swf_SetMatrix(tag, 0);
1638 
1639     swf_TextCountBitsUTF8(font, (U8*)text, scale * 20, &gbits, &abits);
1640     swf_SetU8(tag, gbits);
1641     swf_SetU8(tag, abits);
1642 
1643     while(*upos) {
1644 	U8*next = upos;
1645 	int count = 0;
1646 
1647 	swf_TextSetInfoRecord(tag, font, (scale * 1024) / 100, rgb, x, y);	//scale
1648 	x = 0;
1649 
1650 	while(*next && *next!=13 && *next!=10 && count<127) {
1651 	    readUTF8char(&next);
1652 	    count++;
1653 	}
1654 	if(next[0] == 13 || next[0] == 10) {
1655 	    x = SET_TO_ZERO;
1656 	    y += ystep;
1657 	}
1658 
1659 	if(next[0] == 13 && next[1] == 10)
1660 	    next++;
1661 
1662 	if(next[0] == 13 || next[0] == 10) {
1663 	    *next = 0;
1664 	    next++;
1665 	}
1666 
1667 	/* now set the text params- notice that a font size of
1668 	   1024 (or 1024*20 for definefont3) means that the glyphs will
1669 	   be displayed exactly as they would be in/with a defineshape.
1670 	   This is not documented in the specs.
1671 	 */
1672 
1673 	/* set the actual text- notice that we just pass our scale
1674 	   parameter over, as TextSetCharRecord calculates with
1675 	   percent, too */
1676 	swf_TextSetCharRecordUTF8(tag, font, upos, scale * 20, gbits, abits);
1677 
1678 	upos= next;
1679     }
1680     free(utext);
1681 
1682     swf_SetU8(tag, 0);
1683     return r;
1684 }
1685 
swf_FontCreateLayout(SWFFONT * f)1686 void swf_FontCreateLayout(SWFFONT * f)
1687 {
1688     S16 leading = 0;
1689     int t;
1690     if (f->layout)
1691 	return;
1692     if (!f->numchars)
1693 	return;
1694 
1695     f->layout = (SWFLAYOUT *) rfx_calloc(sizeof(SWFLAYOUT));
1696     f->layout->bounds = (SRECT *) rfx_alloc(f->numchars * sizeof(SRECT));
1697     f->layout->ascent = 0;
1698     f->layout->descent = 0;
1699 
1700     for (t = 0; t < f->numchars; t++) {
1701 	SHAPE2 *shape2;
1702 	SRECT bbox;
1703 	int width;
1704 	shape2 = swf_ShapeToShape2(f->glyph[t].shape);
1705 	if (!shape2) {
1706 	    fprintf(stderr, "Shape parse error\n");
1707 	    exit(1);
1708 	}
1709 	bbox = swf_GetShapeBoundingBox(shape2);
1710 	swf_Shape2Free(shape2);
1711 	f->layout->bounds[t] = bbox;
1712 
1713 	width = (bbox.xmax);
1714 
1715 	/* The following is a heuristic- it may be that extractfont_DefineText
1716 	   has already found out some widths for individual characters (from the way
1717 	   they are used)- we now have to guess whether that width might be possible,
1718 	   which is the case if it isn't either much too big or much too small */
1719 	if (width > f->glyph[t].advance * 3 / 2 || width < f->glyph[t].advance / 2)
1720 	    f->glyph[t].advance = width;
1721 
1722 	if (-bbox.ymin > f->layout->ascent)
1723 	    f->layout->ascent = -bbox.ymin;
1724 	if (bbox.ymax > f->layout->descent)
1725 	    f->layout->descent = bbox.ymax;
1726     }
1727 }
1728 
swf_DrawText(drawer_t * draw,SWFFONT * font,int size,const char * text)1729 void swf_DrawText(drawer_t * draw, SWFFONT * font, int size, const char *text)
1730 {
1731     U8 *s = (U8 *) text;
1732     int advance = 0;
1733     while (*s) {
1734 	SHAPE *shape;
1735 	SHAPE2 *shape2;
1736 	SHAPELINE *l;
1737 	U32 c = readUTF8char(&s);
1738 	int g = font->ascii2glyph[c];
1739 	shape = font->glyph[g].shape;
1740 	if (((int) g) < 0) {
1741 	    fprintf(stderr, "No char %d in font %s\n", c, font->name ? (char *) font->name : "?");
1742 	    continue;
1743 	}
1744 	shape2 = swf_ShapeToShape2(shape);
1745 	l = shape2->lines;
1746 	while (l) {
1747 	    if (l->type == moveTo) {
1748 		FPOINT to;
1749 		to.x = l->x * size / 100.0 / 20.0 + advance;
1750 		to.y = l->y * size / 100.0 / 20.0;
1751 		draw->moveTo(draw, &to);
1752 	    } else if (l->type == lineTo) {
1753 		FPOINT to;
1754 		to.x = l->x * size / 100.0 / 20.0 + advance;
1755 		to.y = l->y * size / 100.0 / 20.0;
1756 		draw->lineTo(draw, &to);
1757 	    } else if (l->type == splineTo) {
1758 		FPOINT mid, to;
1759 		mid.x = l->sx * size / 100.0 / 20.0 + advance;
1760 		mid.y = l->sy * size / 100.0 / 20.0;
1761 		to.x = l->x * size / 100.0 / 20.0 + advance;
1762 		to.y = l->y * size / 100.0 / 20.0;
1763 		draw->splineTo(draw, &mid, &to);
1764 	    }
1765 	    l = l->next;
1766 	}
1767 	swf_Shape2Free(shape2);
1768 	advance += font->glyph[g].advance * size / 100.0 / 20.0;
1769     }
1770 }
1771 
swf_WriteFont_AS3(SWFFONT * font,char * filename)1772 void swf_WriteFont_AS3(SWFFONT * font, char *filename)
1773 {
1774     if(!font->layout)
1775 	swf_FontCreateLayout(font);
1776 
1777     SWF swf;
1778     memset(&swf, 0, sizeof(SWF));
1779     swf.fileVersion = 9;
1780     swf.frameRate = 0x4000;
1781     swf.movieSize.xmax = 200;
1782     swf.movieSize.ymax = 200;
1783 
1784     if(!font->id) font->id=1;
1785 
1786     TAG *tag;
1787     swf.firstTag = tag = swf_InsertTag(tag, ST_DEFINEFONT3);
1788     swf_FontSetDefine2(tag, font);
1789 
1790     char*name = font->name?(char*)font->name:"font";
1791 
1792     tag = swf_InsertTag(tag, ST_NAMECHARACTER);
1793     swf_SetU16(tag, font->id);
1794     swf_SetString(tag, name);
1795     tag = swf_InsertTag(tag, ST_EXPORTASSETS);
1796     swf_SetU16(tag, 1);
1797     swf_SetU16(tag, font->id);
1798     swf_SetString(tag, name);
1799     tag = swf_AddAS3FontDefine(tag, font->id, (char*)font->name);
1800 
1801     tag = swf_InsertTag(tag, ST_END);
1802     swf_SaveSWF(&swf, filename);
1803     swf_FreeTags(&swf);
1804 }
1805 
swf_WriteFont(SWFFONT * font,char * filename)1806 void swf_WriteFont(SWFFONT * font, char *filename)
1807 {
1808     if(!font->layout)
1809 	swf_FontCreateLayout(font);
1810 
1811     char viewer = 1;
1812     U16 id = 1;
1813     U16 depth = 1;
1814 
1815     font->id = id++;
1816 
1817     SWF swf;
1818     memset(&swf, 0, sizeof(SWF));
1819     swf.fileVersion = 8;
1820     swf.frameRate = 0x4000;
1821     swf.movieSize.xmax = 1024*20;
1822     swf.movieSize.ymax = 768*20;
1823 
1824     TAG *tag;
1825     swf.firstTag = tag = swf_InsertTag(NULL, ST_SETBACKGROUNDCOLOR);
1826     swf_SetU8(tag, 0xe0);swf_SetU8(tag, 0xe0);swf_SetU8(tag, 0xff);
1827 
1828     tag = swf_InsertTag(tag, ST_DEFINEFONT3);
1829     swf_FontSetDefine2(tag, font);
1830 
1831     if(font->glyphnames) {
1832 	int c;
1833 	tag = swf_InsertTag(tag, ST_GLYPHNAMES);
1834 	swf_SetU16(tag, font->id);
1835 	swf_SetU16(tag, font->numchars);
1836 	for (c = 0; c < font->numchars; c++) {
1837 	    if (font->glyphnames[c])
1838 		swf_SetString(tag, font->glyphnames[c]);
1839 	    else
1840 		swf_SetString(tag, "");
1841 	}
1842     }
1843 
1844     if(viewer)
1845     {
1846 	RGBA white = {255,255,255,255};
1847 	RGBA black = {255,0,0,0};
1848 	RGBA gray50 = {255,128,128,128};
1849 	RGBA green = {255,0,255,0};
1850 	int t;
1851 	SCOORD miny = SCOORD_MAX;
1852 	SCOORD maxy = SCOORD_MIN;
1853 	double width = 0;
1854 	U16 max_advance = 0;
1855 	char*flags = rfx_calloc(font->numchars);
1856 	double*xmin = rfx_calloc(sizeof(double)*(font->numchars+1));
1857 	double*xmax = rfx_calloc(sizeof(double)*(font->numchars+1));
1858 	int*xpos = rfx_calloc(sizeof(int)*(font->numchars+1));
1859 	for(t=0;t<font->numchars;t++) {
1860 	    SHAPE*s = font->glyph[t].shape;
1861 	    SHAPE2*s2 = swf_ShapeToShape2(s);
1862 	    SRECT r = swf_GetShapeBoundingBox(s2);
1863 
1864 	    // inside a definefont3, everything is 20x the resolution:
1865 	    double rx1 = r.xmin / 20.0;
1866 	    double ry1 = r.ymin / 20.0;
1867 	    double rx2 = r.xmax / 20.0;
1868 	    double ry2 = r.ymax / 20.0;
1869 
1870 	    xmin[t]= rx1;
1871 	    xmax[t]= rx2;
1872 
1873 	    if(ry1<miny) {miny=ry1;}
1874 	    if(ry2>maxy) {maxy=ry2;}
1875 	    swf_Shape2Free(s2);free(s2);
1876 	    width += font->glyph[t].advance;
1877 	    if(font->glyph[t].advance>max_advance)
1878 		max_advance = font->glyph[t].advance;
1879 	}
1880 
1881 	if(miny==SCOORD_MAX) miny=maxy=0;
1882 	if(miny==maxy) maxy=miny+1;
1883 
1884 	/* scale the font so that it's 256 pixels high */
1885 	double scale = (int)((256.0*1024.0/(maxy-miny))*20.0);
1886 	double overlarge_factor;
1887 	int fontsize;
1888 	if(scale > 32767) {
1889 	    fontsize = 32767;
1890 	    overlarge_factor = scale / 32767.0;
1891 	} else {
1892 	    fontsize = scale;
1893 	    overlarge_factor = 1.0;
1894 	}
1895 
1896 	int textid = id++;
1897 	int spriteid = id++;
1898 	SRECT r;
1899 	r.xmin = 0;
1900 	r.ymin = miny*fontsize/1024;
1901 	r.xmax = width*fontsize/20480;
1902 	r.ymax = maxy*fontsize/1024;
1903 	tag = swf_InsertTag(tag, ST_DEFINETEXT);
1904 	swf_SetU16(tag, textid);
1905 	swf_SetRect(tag, &r);
1906 	swf_SetMatrix(tag, NULL);
1907 
1908 	U8 abits = 15;
1909 	U8 gbits = swf_CountBits(font->numchars, 0);
1910 	swf_SetU8(tag, gbits);
1911 	swf_SetU8(tag, abits);
1912 
1913 	RGBA rgb = {255,0,0,0};
1914 
1915 	swf_TextSetInfoRecord(tag, font, fontsize, &rgb, SET_TO_ZERO, SET_TO_ZERO);
1916 	ActionTAG*array = 0;
1917 	double x=0;
1918 	array = action_PushString(array, "xpos");
1919 	for(t=0;t<font->numchars;t++) {
1920 	    swf_SetU8(tag, 1);
1921 	    int width = abs((xmax[t] - xmin[t+1])*fontsize/1024) + 60;
1922 	    array = action_PushInt(array, x/20 +(xmin[t]*scale/1024)/20);
1923 	    x += width * overlarge_factor;
1924 	    swf_SetBits(tag, t, gbits);
1925 	    swf_SetBits(tag, width, abits);
1926 	    swf_SetU8(tag, 128);
1927 	}
1928 	array = action_PushInt(array, x/20);
1929 	array = action_PushInt(array, font->numchars+1);
1930 	array = action_InitArray(array);
1931 	array = action_SetVariable(array);
1932 	swf_SetU8(tag, 0);
1933 
1934 	if(font->layout) {
1935 	    tag = swf_InsertTag(tag, ST_DEFINESHAPE2);
1936 	    SHAPE* s;
1937 	    swf_ShapeNew(&s);
1938 	    int ls = swf_ShapeAddLineStyle(s,20,&white);
1939 	    int shapeid = id++;
1940 	    swf_SetU16(tag,shapeid);
1941 	    SRECT r;
1942 	    r.xmin = 0;
1943 	    r.xmax = 1024*20;
1944 	    r.ymin = 0;
1945 	    r.ymax = 256*20;
1946 	    swf_SetRect(tag,&r);
1947 	    swf_SetShapeHeader(tag,s);
1948 	    swf_ShapeSetAll(tag,s,0,0,ls,0,0);
1949 
1950 	    /* � and � are good chars to test ascent/descent extend */
1951 	    int y1 = (-font->layout->ascent-miny*20.0)*256.0/(maxy-miny);
1952 	    int y2 = (font->layout->descent-miny*20.0)*256.0/(maxy-miny);
1953 
1954 	    swf_ShapeSetMove(tag,s,0,y1);
1955 	    swf_ShapeSetLine(tag,s,width,0);
1956 	    swf_ShapeSetMove(tag,s,0,y2);
1957 	    swf_ShapeSetLine(tag,s,width,0);
1958 
1959 	    swf_ShapeSetEnd(tag);
1960 	    swf_ShapeFree(s);
1961 	    tag = swf_InsertTag(tag, ST_PLACEOBJECT2);
1962 	    swf_ObjectPlace(tag, shapeid, depth++, NULL, NULL, NULL);
1963 	}
1964 
1965 	/* shapes */
1966 
1967 	for(t=0;t<font->numchars;t++) {
1968 	    tag = swf_InsertTag(tag, ST_DEFINESHAPE2);
1969 	    SHAPE* s;
1970 	    swf_ShapeNew(&s);
1971 	    int ls = swf_ShapeAddLineStyle(s,20*2,&black);
1972 	    int ls2 = swf_ShapeAddLineStyle(s,20*2,&green);
1973 	    int fs = swf_ShapeAddSolidFillStyle(s, &gray50);
1974 	    int shapeid = id++;
1975 	    swf_SetU16(tag,shapeid);
1976 	    SRECT r;
1977 	    r.xmin = 0;
1978 	    r.xmax = 1024*20;
1979 	    r.ymin = 0;
1980 	    r.ymax = 512*20;
1981 	    swf_SetRect(tag,&r);
1982 	    swf_SetShapeHeader(tag,s);
1983 	    swf_ShapeSetAll(tag,s,0,0,ls,fs,0);
1984 	    SHAPE2*s2 = swf_ShapeToShape2(font->glyph[t].shape);
1985 	    SHAPELINE*l = s2->lines;
1986 	    int lastx=0,lasty=0;
1987 
1988 	    double x1 = (1024*20 - (xmax[t] - xmin[t])*20*2*scale/20480.0)/2;
1989 	    double y1 = -miny*20*scale*2/20480.0;
1990 	    double scalex = scale*2/20480.0;
1991 	    double scaley = scale*2/20480.0;
1992 
1993 	    while(l) {
1994 		int lx = (l->x)*scalex+x1;
1995 		int ly = (l->y)*scaley+y1;
1996 		int sx = (l->sx)*scalex+x1;
1997 		int sy = (l->sy)*scaley+y1;
1998 		if(l->type == moveTo) {
1999 		    swf_ShapeSetMove(tag,s,lx,ly);
2000 		} else if(l->type == lineTo) {
2001 		    swf_ShapeSetLine(tag,s,lx-lastx,ly-lasty);
2002 		} else if(l->type == splineTo) {
2003 		    swf_ShapeSetCurve(tag,s,sx-lastx,sy-lasty,lx-sx,ly-sy);
2004 		}
2005 		lastx = lx;
2006 		lasty = ly;
2007 		l = l->next;
2008 	    }
2009 
2010 	    if(font->alignzones) {
2011 		ALIGNZONE*zone = &font->alignzones[t];
2012 		swf_ShapeSetAll(tag,s,0,0,ls2,SET_TO_ZERO,SET_TO_ZERO);
2013 		if((zone->x&zone->dx)!=0xffff) {
2014 		    double x = F16toFloat(zone->x)*20480.0*scalex+x1;
2015 		    double dx = (F16toFloat(zone->x)+F16toFloat(zone->dx))*20480.0*scalex+x1;
2016 		    swf_ShapeSetMove(tag,s,x,0);
2017 		    swf_ShapeSetLine(tag,s,0,1024*20);
2018 		    swf_ShapeSetMove(tag,s,dx,0);
2019 		    swf_ShapeSetLine(tag,s,0,1024*20);
2020 		}
2021 		if((zone->y&zone->dy)!=0xffff) {
2022 		    double y = -F16toFloat(zone->y)*20480.0*scaley+y1;
2023 		    double dy = -(F16toFloat(zone->y)+F16toFloat(zone->dy))*20480.0*scaley+y1;
2024 		    swf_ShapeSetMove(tag,s,0,y);
2025 		    swf_ShapeSetLine(tag,s,1024*20,0);
2026 		    swf_ShapeSetMove(tag,s,0,dy);
2027 		    swf_ShapeSetLine(tag,s,1024*20,0);
2028 		}
2029 	    }
2030 
2031 	    swf_ShapeSetEnd(tag);
2032 	    swf_ShapeFree(s);
2033 
2034 	    tag = swf_InsertTag(tag, ST_DEFINESPRITE);
2035 	    U16 spriteid=id++;
2036 	    swf_SetU16(tag, spriteid);
2037 	    swf_SetU16(tag, 1);
2038 	    tag = swf_InsertTag(tag, ST_PLACEOBJECT2);
2039 	    swf_ObjectPlace(tag, shapeid, 1, NULL, NULL, NULL);
2040 	    tag = swf_InsertTag(tag, ST_END);
2041 	    tag = swf_InsertTag(tag, ST_PLACEOBJECT2);
2042 	    MATRIX m;
2043 	    swf_GetMatrix(0, &m);
2044 	    m.ty = 20000;
2045 	    char txt[80];
2046 	    sprintf(txt, "char%d", font->numchars-t);
2047 	    swf_ObjectPlace(tag, spriteid, depth++, &m, NULL, txt);
2048 	}
2049 
2050 	/* marker */
2051 	tag = swf_InsertTag(tag, ST_DEFINESHAPE2);
2052 	int shapeid=id++;
2053 	RGBA blue = {0xff,0xc0,0xc0,0xff};
2054 	swf_ShapeSetRectangle(tag, shapeid, 20, 20, &blue);
2055 	tag = swf_InsertTag(tag, ST_DEFINESPRITE);
2056 	U16 spriteid2=id++;
2057 	swf_SetU16(tag, spriteid2);
2058 	swf_SetU16(tag, 1);
2059 	tag = swf_InsertTag(tag, ST_PLACEOBJECT2);
2060 	swf_ObjectPlace(tag, shapeid, 1, NULL, NULL, NULL);
2061 	tag = swf_InsertTag(tag, ST_END);
2062 	tag = swf_InsertTag(tag, ST_PLACEOBJECT2);
2063 	swf_ObjectPlace(tag, spriteid2, depth++, NULL, NULL, "marker");
2064 
2065 	/* textbar */
2066 	tag = swf_InsertTag(tag, ST_DEFINESPRITE);
2067 	swf_SetU16(tag, spriteid);
2068 	swf_SetU16(tag, 1);
2069 	tag = swf_InsertTag(tag, ST_PLACEOBJECT2);
2070 	MATRIX m;
2071 	swf_GetMatrix(0, &m);
2072 	m.sx = 65536 * overlarge_factor;
2073 	m.sy = 65536 * overlarge_factor;
2074 	m.tx = 0;
2075 	m.ty = -miny*256*20/(maxy-miny);
2076 	swf_ObjectPlace(tag, textid, 1, &m, NULL, NULL);
2077 	tag = swf_InsertTag(tag, ST_END);
2078 	tag = swf_InsertTag(tag, ST_PLACEOBJECT2);
2079 	swf_ObjectPlace(tag, spriteid, depth++, NULL, NULL, "textbar");
2080 
2081 	/* marker2 */
2082 	RGBA blue2 = {0x80,0x80,0xff,0x80};
2083 	tag = swf_InsertTag(tag, ST_DEFINESHAPE3);
2084 	int shapeid2=id++;
2085 	swf_ShapeSetRectangleWithBorder(tag, shapeid2, 20, 20, &blue2, 0, &white);
2086 	tag = swf_InsertTag(tag, ST_DEFINESPRITE);
2087 	U16 spriteid3=id++;
2088 	swf_SetU16(tag, spriteid3);
2089 	swf_SetU16(tag, 1);
2090 	tag = swf_InsertTag(tag, ST_PLACEOBJECT2);
2091 	swf_ObjectPlace(tag, shapeid2, 1, NULL, NULL, NULL);
2092 	tag = swf_InsertTag(tag, ST_END);
2093 	tag = swf_InsertTag(tag, ST_PLACEOBJECT2);
2094 	swf_ObjectPlace(tag, spriteid3, depth++, NULL, NULL, "marker2");
2095 
2096 
2097 char*data =
2098 " var mouseListener = new Object();"
2099 " var speed = 0;"
2100 " var myx = 0;"
2101 " var currentMouseOver, currentChar;"
2102 " mouseListener.onMouseDown = function() { "
2103 "     eval(\"_root.char\"+currentChar)._y = 20000;"
2104 "     currentChar = currentMouseOver;"
2105 "     var i = currentMouseOver;"
2106 "     eval(\"_root.char\"+i)._y = 256;"
2107 "     _root.marker2._yscale=256*100;"
2108 "     _root.marker2._xscale=(xpos[i-1]-xpos[i])*100;"
2109 "     _root.marker2._x=xpos[i]+myx;"
2110 " };"
2111 " mouseListener.onMouseMove = function() { "
2112 "     if(_ymouse<256) {"
2113 "          speed = Math.abs(_xmouse-512)>256?(512-_xmouse)/8:0;"
2114 "     } else {"
2115 "	   speed = 0;"
2116 "     }; "
2117 " }; "
2118 " setInterval( function(){ "
2119 "     if(_ymouse<256) {"
2120 "         var i, x=_xmouse-_root.textbar._x;"
2121 "         for(i=xpos.length-1;i>0;i--) {"
2122 "             if(x<xpos[i-1]) break;"
2123 "         }"
2124 "         currentMouseOver = i;"
2125 "         _root.marker._yscale=256*100;"
2126 "         _root.marker._xscale=(xpos[i-1]-xpos[i])*100;"
2127 "         _root.marker._x=xpos[i]+myx;"
2128 "         _root.textbar._x += 0.05;"
2129 "     }"
2130 "     if(myx+speed>0) {"
2131 "         speed=0;"
2132 "     } else if(myx+speed<-xpos[0]+1024) {"
2133 "         speed=0;"
2134 "     }"
2135 "     myx+=speed;"
2136 "     _root.textbar._x = myx;"
2137 "     _root.marker._x += speed;"
2138 "     _root.marker2._x += speed;"
2139 " }, 20);"
2140 " Mouse.addListener(mouseListener);"
2141 ;
2142 	ActionTAG* atag = swf_ActionCompile(data, 6);
2143 
2144 	tag = swf_InsertTag(tag, ST_DOACTION);
2145 	swf_ActionSet(tag, array);
2146 	swf_ActionSet(tag, atag);
2147 	swf_SetU8(tag, 0);
2148 	swf_ActionFree(atag);
2149 
2150 	tag = swf_InsertTag(tag, ST_SHOWFRAME);
2151 
2152 	free(flags);
2153 	free(xmin);
2154 	free(xmax);
2155     }
2156 
2157     tag = swf_InsertTag(tag, ST_END);
2158 
2159     swf.compressed = -1;
2160     swf_SaveSWF(&swf, filename);
2161     swf_FreeTags(&swf);
2162 }
2163