refined log output
[swftools.git] / lib / devices / swf.c
1 /* gfxdevice_swf.c
2
3    Part of the swftools package.
4
5    Copyright (c) 2001,2002,2003,2004,2005 Matthias Kramm <kramm@quiss.org> 
6
7    Swftools is free software; you can redistribute it and/or modify
8    it under the terms of the GNU General Public License as published by
9    the Free Software Foundation; either version 2 of the License, or
10    (at your option) any later version.
11
12    Swftools is distributed in the hope that it will be useful,
13    but WITHOUT ANY WARRANTY; without even the implied warranty of
14    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15    GNU General Public License for more details.
16
17    You should have received a copy of the GNU General Public License
18    along with swftools; if not, write to the Free Software
19    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA */
20
21 #include <stdlib.h>
22 #include <stdio.h>
23 #include <string.h>
24 #include "../../config.h"
25 #include <fcntl.h>
26 #include <unistd.h>
27 #ifdef HAVE_ASSERT_H
28 #include <assert.h>
29 #else
30 #define assert(a)
31 #endif
32 #include <math.h>
33 #include "../log.h"
34 #include "../rfxswf.h"
35 #include "../gfxdevice.h"
36 #include "../gfxtools.h"
37 #include "../art/libart.h"
38 #include "artsutils.c"
39
40 #define CHARDATAMAX 8192
41 #define CHARMIDX 0
42 #define CHARMIDY 0
43
44 typedef struct _chardata {
45     int charid;
46     int fontid; /* TODO: use a SWFFONT instead */
47     int x;
48     int y;
49     int size;
50     RGBA color;
51 } chardata_t;
52
53 typedef struct _fontlist
54 {
55     SWFFONT *swffont;
56     struct _fontlist*next;
57 } fontlist_t;
58
59 typedef long int twip;
60
61 typedef struct _swfmatrix {
62     double m11,m12,m21,m22,m31,m32;
63 } swfmatrix_t;
64
65 typedef struct _swfoutput_internal
66 {
67     gfxdevice_t*dev; // the gfxdevice object where this internal struct resides
68
69     double config_dumpfonts;
70     double config_ppmsubpixels;
71     double config_jpegsubpixels;
72     int config_opennewwindow;
73     int config_ignoredraworder;
74     int config_drawonlyshapes;
75     int config_jpegquality;
76     int config_storeallcharacters;
77     int config_generate_fake_tags;
78     int config_enablezlib;
79     int config_insertstoptag;
80     int config_flashversion;
81     int config_reordertags;
82     int config_splinemaxerror;
83     int config_fontsplinemaxerror;
84     int config_filloverlap;
85     int config_protect;
86     int config_bboxvars;
87     int config_disable_polygon_conversion;
88     RGBA config_linkcolor;
89     float config_minlinewidth;
90     double config_caplinewidth;
91     char* config_linktarget;
92     char*config_internallinkfunction;
93     char*config_externallinkfunction;
94
95     SWF* swf;
96
97     fontlist_t* fontlist;
98
99     char storefont;
100
101     MATRIX page_matrix;
102
103     TAG *tag;
104     int currentswfid;
105     int depth;
106     int startdepth;
107     int linewidth;
108     
109     SHAPE* shape;
110     int shapeid;
111     int textid;
112     
113     int fillstyleid;
114     int linestyleid;
115     int swflastx;
116     int swflasty;
117     int lastwasfill;
118     int shapeisempty;
119     char fill;
120     int min_x,max_x;
121     int min_y,max_y;
122     TAG* cliptags[128];
123     int clipshapes[128];
124     U32 clipdepths[128];
125     int clippos;
126
127     /* image cache */
128     int pic_xids[1024];
129     int pic_yids[1024];
130     int pic_ids[1024];
131     int pic_width[1024];
132     int pic_height[1024];
133     int picpos;
134
135     int frameno;
136     int lastframeno;
137     
138     char fillstylechanged;
139
140     int jpeg; //next image type
141     
142     int bboxrectpos;
143     SRECT bboxrect;
144
145     chardata_t chardata[CHARDATAMAX];
146     int chardatapos;
147     int firstpage;
148     char pagefinished;
149
150     char overflow;
151
152     MATRIX fontmatrix;
153     double fontm11,fontm12,fontm21,fontm22;
154     SWFFONT *swffont;
155     RGBA strokergb;
156     RGBA fillrgb;
157     int drawmode;
158
159     int shapeposx;
160     int shapeposy;
161
162     char* mark;
163
164 } swfoutput_internal;
165     
166 static void swf_fillbitmap(gfxdevice_t*driver, gfxline_t*line, gfximage_t*img, gfxmatrix_t*move, gfxcxform_t*cxform);
167 static int  swf_setparameter(gfxdevice_t*driver, const char*key, const char*value);
168 static void swf_drawstroke(gfxdevice_t*dev, gfxline_t*line, gfxcoord_t width, gfxcolor_t*color, gfx_capType cap_style, gfx_joinType joint_style, gfxcoord_t miterLimit);
169 static void swf_startclip(gfxdevice_t*dev, gfxline_t*line);
170 static void swf_endclip(gfxdevice_t*dev);
171 static void swf_stroke(gfxdevice_t*dev, gfxline_t*line, gfxcoord_t width, gfxcolor_t*color, gfx_capType cap_style, gfx_joinType joint_style, gfxcoord_t miterLimit);
172 static void swf_fill(gfxdevice_t*dev, gfxline_t*line, gfxcolor_t*color);
173 static void swf_fillbitmap(gfxdevice_t*dev, gfxline_t*line, gfximage_t*img, gfxmatrix_t*matrix, gfxcxform_t*cxform);
174 static void swf_fillgradient(gfxdevice_t*dev, gfxline_t*line, gfxgradient_t*gradient, gfxgradienttype_t type, gfxmatrix_t*matrix);
175 static void swf_drawchar(gfxdevice_t*dev, gfxfont_t*font, int glyph, gfxcolor_t*color, gfxmatrix_t*matrix);
176 static void swf_addfont(gfxdevice_t*dev, gfxfont_t*font);
177 static void swf_drawlink(gfxdevice_t*dev, gfxline_t*line, char*action);
178 static void swf_startframe(gfxdevice_t*dev, int width, int height);
179 static void swf_endframe(gfxdevice_t*dev);
180 static gfxresult_t* swf_finish(gfxdevice_t*driver);
181
182 static swfoutput_internal* init_internal_struct()
183 {
184     swfoutput_internal*i = (swfoutput_internal*)malloc(sizeof(swfoutput_internal));
185     memset(i, 0, sizeof(swfoutput_internal));
186
187     i->storefont = 0;
188     i->currentswfid = 0;
189     i->depth = 0;
190     i->overflow = 0;
191     i->startdepth = 0;
192     i->linewidth = 0;
193     i->shapeid = -1;
194     i->textid = -1;
195     i->frameno = 0;
196     i->lastframeno = 0;
197
198     i->mark = 0;
199
200     i->fillstyleid;
201     i->linestyleid;
202     i->swflastx=0;
203     i->swflasty=0;
204     i->lastwasfill = 0;
205     i->shapeisempty = 1;
206     i->fill = 0;
207     i->clippos = 0;
208
209     i->fillstylechanged = 0;
210
211     i->bboxrectpos = -1;
212     i->chardatapos = 0;
213     i->firstpage = 1;
214     i->pagefinished = 1;
215
216     i->config_dumpfonts=0;
217     i->config_ppmsubpixels=0;
218     i->config_jpegsubpixels=0;
219     i->config_opennewwindow=1;
220     i->config_ignoredraworder=0;
221     i->config_drawonlyshapes=0;
222     i->config_jpegquality=85;
223     i->config_storeallcharacters=0;
224     i->config_enablezlib=0;
225     i->config_generate_fake_tags=0;
226     i->config_insertstoptag=0;
227     i->config_flashversion=6;
228     i->config_splinemaxerror=1;
229     i->config_fontsplinemaxerror=1;
230     i->config_filloverlap=0;
231     i->config_protect=0;
232     i->config_bboxvars=0;
233     i->config_minlinewidth=0.05;
234     i->config_caplinewidth=1;
235     i->config_linktarget=0;
236     i->config_internallinkfunction=0;
237     i->config_externallinkfunction=0;
238     i->config_reordertags=1;
239
240     i->config_linkcolor.r = i->config_linkcolor.g = i->config_linkcolor.b = 255;
241     i->config_linkcolor.a = 0x40;
242
243     return i;
244 };
245
246 static int id_error = 0;
247
248 static U16 getNewID(gfxdevice_t* dev)
249 {
250     swfoutput_internal*i = (swfoutput_internal*)dev->internal;
251     if(i->currentswfid == 65535) {
252         if(!id_error) {
253             msg("<error> ID Table overflow");
254             msg("<error> This file is too complex to render- SWF only supports 65536 shapes at once");
255         }
256         id_error=1;
257         i->overflow = 1;
258         exit(1);
259     }
260     return ++i->currentswfid;
261 }
262 static U16 getNewDepth(gfxdevice_t* dev)
263 {
264     swfoutput_internal*i = (swfoutput_internal*)dev->internal;
265     if(i->depth == 65535) {
266         if(!id_error) {
267             msg("<error> Depth Table overflow");
268             msg("<error> This file is too complex to render- SWF only supports 65536 shapes at once");
269         }
270         id_error=1;
271         i->overflow = 1;
272         exit(1);
273     }
274     return ++i->depth;
275 }
276
277 static void startshape(gfxdevice_t* dev);
278 static void starttext(gfxdevice_t* dev);
279 static void endshape(gfxdevice_t* dev);
280 static void endtext(gfxdevice_t* dev);
281
282 typedef struct _plotxy
283 {
284     double x,y;
285 } plotxy_t;
286
287 // write a move-to command into the swf
288 static int movetoxy(gfxdevice_t*dev, TAG*tag, plotxy_t p0)
289 {
290     swfoutput_internal*i = (swfoutput_internal*)dev->internal;
291     int rx = (int)(p0.x*20);
292     int ry = (int)(p0.y*20);
293     if(rx!=i->swflastx || ry!=i->swflasty || i->fillstylechanged) {
294       swf_ShapeSetMove (tag, i->shape, rx,ry);
295       i->fillstylechanged = 0;
296       i->swflastx=rx;
297       i->swflasty=ry;
298       return 1;
299     }
300     return 0;
301 }
302 static int moveto(gfxdevice_t*dev, TAG*tag, double  x, double y)
303 {
304     swfoutput_internal*i = (swfoutput_internal*)dev->internal;
305     plotxy_t p;
306     p.x = x;
307     p.y = y;
308     return movetoxy(dev, tag, p);
309 }
310 static void addPointToBBox(gfxdevice_t*dev, int px, int py) 
311 {
312     swfoutput_internal*i = (swfoutput_internal*)dev->internal;
313
314     SPOINT p;
315     p.x = px;
316     p.y = py;
317     if(i->fill) {
318         swf_ExpandRect(&i->bboxrect, p);
319     } else {
320         swf_ExpandRect3(&i->bboxrect, p, i->linewidth*3/2);
321     }
322 }
323
324 /*static void plot(gfxdevice_t*dev, int x, int y, TAG*tag)
325 {
326     swfoutput_internal*i = (swfoutput_internal*)dev->internal;
327     int width = i->linewidth/4;
328     if(width > 5)
329         width = 5;
330     ////square
331     //swf_ShapeSetLine(tag, i->shape,-width,-width);
332     //swf_ShapeSetLine(tag, i->shape,width*2,0);
333     //swf_ShapeSetLine(tag, i->shape,0,width*2);
334     //swf_ShapeSetLine(tag, i->shape,-width*2,0);
335     //swf_ShapeSetLine(tag, i->shape,0,-width*2);
336     //swf_ShapeSetLine(tag, i->shape,width,width);
337    
338     // diamond
339     swf_ShapeSetLine(tag, i->shape,-width,0);
340     swf_ShapeSetLine(tag, i->shape,width,-width);
341     swf_ShapeSetLine(tag, i->shape,width,width);
342     swf_ShapeSetLine(tag, i->shape,-width,width);
343     swf_ShapeSetLine(tag, i->shape,-width,-width);
344     swf_ShapeSetLine(tag, i->shape,width,0);
345
346     addPointToBBox(dev, x-width ,y-width);
347     addPointToBBox(dev, x+width ,y+width);
348 }*/
349
350 // write a line-to command into the swf
351 static void linetoxy(gfxdevice_t*dev, TAG*tag, plotxy_t p0)
352 {
353     swfoutput_internal*i = (swfoutput_internal*)dev->internal;
354     int px = (int)(p0.x*20);
355     int py = (int)(p0.y*20);
356     int rx = (px-i->swflastx);
357     int ry = (py-i->swflasty);
358     if(rx|ry) {
359         swf_ShapeSetLine (tag, i->shape, rx,ry);
360         addPointToBBox(dev, i->swflastx,i->swflasty);
361         addPointToBBox(dev, px,py);
362     }/* else if(!i->fill) {
363        // treat lines of length 0 as plots, making them
364        // at least 1 twip wide so Flash will display them
365         plot(dev, i->swflastx, i->swflasty, tag);
366     }*/
367
368     i->shapeisempty = 0;
369     i->swflastx+=rx;
370     i->swflasty+=ry;
371 }
372 static void lineto(gfxdevice_t*dev, TAG*tag, double x, double y)
373 {
374     plotxy_t p;
375     p.x = x;
376     p.y = y;
377     linetoxy(dev,tag, p);
378 }
379
380 // write a spline-to command into the swf
381 static void splineto(gfxdevice_t*dev, TAG*tag, plotxy_t control,plotxy_t end)
382 {
383     swfoutput_internal*i = (swfoutput_internal*)dev->internal;
384     int lastlastx = i->swflastx;
385     int lastlasty = i->swflasty;
386
387     int cx = ((int)(control.x*20)-i->swflastx);
388     int cy = ((int)(control.y*20)-i->swflasty);
389     i->swflastx += cx;
390     i->swflasty += cy;
391     int ex = ((int)(end.x*20)-i->swflastx);
392     int ey = ((int)(end.y*20)-i->swflasty);
393     i->swflastx += ex;
394     i->swflasty += ey;
395     
396     if((cx || cy) && (ex || ey)) {
397         swf_ShapeSetCurve(tag, i->shape, cx,cy,ex,ey);
398         addPointToBBox(dev, lastlastx   ,lastlasty   );
399         addPointToBBox(dev, lastlastx+cx,lastlasty+cy);
400         addPointToBBox(dev, lastlastx+cx+ex,lastlasty+cy+ey);
401     } else if(cx || cy || ex || ey) {
402         swf_ShapeSetLine(tag, i->shape, cx+ex,cy+ey);
403         addPointToBBox(dev, lastlastx   ,lastlasty   );
404         addPointToBBox(dev, lastlastx+cx,lastlasty+cy);
405         addPointToBBox(dev, lastlastx+cx+ex,lastlasty+cy+ey);
406     }
407
408     i->shapeisempty = 0;
409 }
410
411 /* write a line, given two points and the transformation
412    matrix. */
413 /*static void line(gfxdevice_t*dev, TAG*tag, plotxy_t p0, plotxy_t p1)
414 {
415     moveto(dev, tag, p0);
416     lineto(dev, tag, p1);
417 }*/
418
419 void resetdrawer(gfxdevice_t*dev)
420 {
421     swfoutput_internal*i = (swfoutput_internal*)dev->internal;
422     i->swflastx = 0;
423     i->swflasty = 0;
424 }
425
426 static void stopFill(gfxdevice_t*dev)
427 {
428     swfoutput_internal*i = (swfoutput_internal*)dev->internal;
429     if(i->lastwasfill!=0)
430     {
431         swf_ShapeSetStyle(i->tag,i->shape,i->linestyleid,0x8000,0);
432         i->fillstylechanged = 1;
433         i->lastwasfill = 0;
434     }
435 }
436 static void startFill(gfxdevice_t*dev)
437 {
438     swfoutput_internal*i = (swfoutput_internal*)dev->internal;
439     if(i->lastwasfill!=1)
440     {
441         swf_ShapeSetStyle(i->tag,i->shape,0x8000,i->fillstyleid,0);
442         i->fillstylechanged = 1;
443         i->lastwasfill = 1;
444     }
445 }
446
447 static inline int colorcompare(gfxdevice_t*dev, RGBA*a,RGBA*b)
448 {
449
450     if(a->r!=b->r ||
451        a->g!=b->g ||
452        a->b!=b->b ||
453        a->a!=b->a) {
454         return 0;
455     }
456     return 1;
457 }
458
459 static SRECT getcharacterbbox(gfxdevice_t*dev, SWFFONT*font, MATRIX* m)
460 {
461     swfoutput_internal*i = (swfoutput_internal*)dev->internal;
462     SRECT r;
463     char debug = 0;
464     memset(&r, 0, sizeof(r));
465
466     int t;
467     if(debug) printf("\n");
468     for(t=0;t<i->chardatapos;t++)
469     {
470         if(i->chardata[t].fontid != font->id) {
471             msg("<error> Internal error: fontid %d != fontid %d", i->chardata[t].fontid, font->id);
472             exit(1);
473         }
474         SRECT b = font->layout->bounds[i->chardata[t].charid];
475         b.xmin *= i->chardata[t].size;
476         b.ymin *= i->chardata[t].size;
477         b.xmax *= i->chardata[t].size;
478         b.ymax *= i->chardata[t].size;
479
480         /* divide by 1024, rounding xmax/ymax up */
481         b.xmax += 1023; b.ymax += 1023; b.xmin /= 1024; b.ymin /= 1024; b.xmax /= 1024; b.ymax /= 1024;
482
483         b.xmin += i->chardata[t].x;
484         b.ymin += i->chardata[t].y;
485         b.xmax += i->chardata[t].x;
486         b.ymax += i->chardata[t].y;
487
488         /* until we solve the INTERNAL_SCALING problem (see below)
489            make sure the bounding box is big enough */
490         b.xmin -= 20;
491         b.ymin -= 20;
492         b.xmax += 20;
493         b.ymax += 20;
494
495         b = swf_TurnRect(b, m);
496
497         if(debug) printf("(%f,%f,%f,%f) -> (%f,%f,%f,%f) [font %d/%d, char %d]\n",
498                 font->layout->bounds[i->chardata[t].charid].xmin/20.0,
499                 font->layout->bounds[i->chardata[t].charid].ymin/20.0,
500                 font->layout->bounds[i->chardata[t].charid].xmax/20.0,
501                 font->layout->bounds[i->chardata[t].charid].ymax/20.0,
502                 b.xmin/20.0,
503                 b.ymin/20.0,
504                 b.xmax/20.0,
505                 b.ymax/20.0,
506                 i->chardata[t].fontid,
507                 font->id,
508                 i->chardata[t].charid
509                 );
510         swf_ExpandRect2(&r, &b);
511     }
512     if(debug) printf("-----> (%f,%f,%f,%f)\n",
513             r.xmin/20.0,
514             r.ymin/20.0,
515             r.xmax/20.0,
516             r.ymax/20.0);
517     return r;
518 }
519
520 static void putcharacters(gfxdevice_t*dev, TAG*tag)
521 {
522     swfoutput_internal*i = (swfoutput_internal*)dev->internal;
523     int t;
524     SWFFONT font;
525     RGBA color;
526     color.r = i->chardata[0].color.r^255;
527     color.g = 0;
528     color.b = 0;
529     color.a = 0;
530     int lastfontid;
531     int lastx;
532     int lasty;
533     int lastsize;
534     int charids[128];
535     int charadvance[128];
536     int charstorepos;
537     int pass;
538     int glyphbits=1; //TODO: can this be zero?
539     int advancebits=1;
540
541     if(tag->id != ST_DEFINETEXT &&
542         tag->id != ST_DEFINETEXT2) {
543         msg("<error> internal error: putcharacters needs an text tag, not %d\n",tag->id);
544         exit(1);
545     }
546     if(!i->chardatapos) {
547         msg("<warning> putcharacters called with zero characters");
548     }
549
550     for(pass = 0; pass < 2; pass++)
551     {
552         charstorepos = 0;
553         lastfontid = -1;
554         lastx = CHARMIDX;
555         lasty = CHARMIDY;
556         lastsize = -1;
557
558         if(pass==1)
559         {
560             advancebits++; // add sign bit
561             swf_SetU8(tag, glyphbits);
562             swf_SetU8(tag, advancebits);
563         }
564
565         for(t=0;t<=i->chardatapos;t++)
566         {
567             if(lastfontid != i->chardata[t].fontid || 
568                     lastx!=i->chardata[t].x ||
569                     lasty!=i->chardata[t].y ||
570                     !colorcompare(dev,&color, &i->chardata[t].color) ||
571                     charstorepos==127 ||
572                     lastsize != i->chardata[t].size ||
573                     t == i->chardatapos)
574             {
575                 if(charstorepos && pass==0)
576                 {
577                     int s;
578                     for(s=0;s<charstorepos;s++)
579                     {
580                         while(charids[s]>=(1<<glyphbits))
581                             glyphbits++;
582                         while(charadvance[s]>=(1<<advancebits))
583                             advancebits++;
584                     }
585                 }
586                 if(charstorepos && pass==1)
587                 {
588                     tag->writeBit = 0; // Q&D
589                     swf_SetBits(tag, 0, 1); // GLYPH Record
590                     swf_SetBits(tag, charstorepos, 7); // number of glyphs
591                     int s;
592                     for(s=0;s<charstorepos;s++)
593                     {
594                         swf_SetBits(tag, charids[s], glyphbits);
595                         swf_SetBits(tag, charadvance[s], advancebits);
596                     }
597                 }
598                 charstorepos = 0;
599
600                 if(pass == 1 && t<i->chardatapos)
601                 {
602                     RGBA*newcolor=0;
603                     SWFFONT*newfont=0;
604                     int newx = 0;
605                     int newy = 0;
606                     if(lastx != i->chardata[t].x ||
607                        lasty != i->chardata[t].y)
608                     {
609                         newx = i->chardata[t].x;
610                         newy = i->chardata[t].y;
611                         if(newx == 0)
612                             newx = SET_TO_ZERO;
613                         if(newy == 0)
614                             newy = SET_TO_ZERO;
615                     }
616                     if(!colorcompare(dev,&color, &i->chardata[t].color)) 
617                     {
618                         color = i->chardata[t].color;
619                         newcolor = &color;
620                     }
621                     font.id = i->chardata[t].fontid;
622                     if(lastfontid != i->chardata[t].fontid || lastsize != i->chardata[t].size)
623                         newfont = &font;
624
625                     tag->writeBit = 0; // Q&D
626                     swf_TextSetInfoRecord(tag, newfont, i->chardata[t].size, newcolor, newx,newy);
627                 }
628
629                 lastfontid = i->chardata[t].fontid;
630                 lastx = i->chardata[t].x;
631                 lasty = i->chardata[t].y;
632                 lastsize = i->chardata[t].size;
633             }
634
635             if(t==i->chardatapos)
636                     break;
637
638             int advance;
639             int nextt = t==i->chardatapos-1?t:t+1;
640             int rel = i->chardata[nextt].x-i->chardata[t].x;
641             if(rel>=0 && (rel<(1<<(advancebits-1)) || pass==0)) {
642                advance = rel;
643                lastx=i->chardata[nextt].x;
644             }
645             else {
646                advance = 0;
647                lastx=i->chardata[t].x;
648             }
649             charids[charstorepos] = i->chardata[t].charid;
650             charadvance[charstorepos] = advance;
651             charstorepos ++;
652         }
653     }
654     i->chardatapos = 0;
655 }
656
657 static void putcharacter(gfxdevice_t*dev, int fontid, int charid, int x,int y, int size, RGBA color)
658 {
659     swfoutput_internal*i = (swfoutput_internal*)dev->internal;
660     if(i->chardatapos == CHARDATAMAX)
661     {
662         msg("<warning> Character buffer too small. SWF will be slightly bigger");
663         endtext(dev);
664         starttext(dev);
665     }
666     i->chardata[i->chardatapos].fontid = fontid;
667     i->chardata[i->chardatapos].charid = charid;
668     i->chardata[i->chardatapos].x = x;
669     i->chardata[i->chardatapos].y = y;
670     i->chardata[i->chardatapos].color = color;
671     i->chardata[i->chardatapos].size = size;
672     i->chardatapos++;
673 }
674
675 /* Notice: we can only put chars in the range -1639,1638 (-32768/20,32768/20).
676    So if we set this value to high, the char coordinates will overflow.
677    If we set it to low, however, the char positions will be inaccurate */
678 #define FONT_INTERNAL_SIZE 4
679
680 /* process a character. */
681 static int drawchar(gfxdevice_t*dev, SWFFONT *swffont, int charid, swfmatrix_t*m, gfxcolor_t*col)
682 {
683     swfoutput_internal*i = (swfoutput_internal*)dev->internal;
684     if(!swffont) {
685         msg("<warning> Font is NULL");
686         return 0;
687     }
688
689     if(charid<0 || charid>=swffont->numchars) {
690         msg("<warning> No character %d in font %s ", charid, FIXNULL((char*)swffont->name));
691         return 0;
692     }
693     /*if(swffont->glyph[charid].shape->bitlen <= 16) {
694         msg("<warning> Glyph %d in current charset (%s, %d characters) is empty", 
695                 charid, FIXNULL((char*)swffont->name), swffont->numchars);
696         return 1;
697     }*/
698
699     if(i->shapeid>=0)
700         endshape(dev);
701     if(i->textid<0)
702         starttext(dev);
703
704     float x = m->m31;
705     float y = m->m32;
706     float det = ((m->m11*m->m22)-(m->m21*m->m12));
707     if(fabs(det) < 0.0005) { 
708         /* x direction equals y direction- the text is invisible */
709         return 1;
710     }
711     det = 20*FONT_INTERNAL_SIZE / det;
712
713     SPOINT p;
714     p.x = (SCOORD)((  x * m->m22 - y * m->m12)*det);
715     p.y = (SCOORD)((- x * m->m21 + y * m->m11)*det);
716
717     RGBA rgba = *(RGBA*)col;
718     putcharacter(dev, swffont->id, charid,p.x,p.y,FONT_INTERNAL_SIZE, rgba);
719     swf_FontUseGlyph(swffont, charid);
720     return 1;
721 }
722
723 static void endtext(gfxdevice_t*dev)
724 {
725     swfoutput_internal*i = (swfoutput_internal*)dev->internal;
726     if(i->textid<0)
727         return;
728
729     i->tag = swf_InsertTag(i->tag,ST_DEFINETEXT);
730     swf_SetU16(i->tag, i->textid);
731
732     SRECT r;
733     r = getcharacterbbox(dev, i->swffont, &i->fontmatrix);
734     
735     swf_SetRect(i->tag,&r);
736
737     swf_SetMatrix(i->tag,&i->fontmatrix);
738
739     msg("<trace> Placing text (%d characters) as ID %d", i->chardatapos, i->textid);
740
741     putcharacters(dev, i->tag);
742     swf_SetU8(i->tag,0);
743
744     if(i->swf->fileVersion >= 8) {
745         i->tag = swf_InsertTag(i->tag, ST_CSMTEXTSETTINGS);
746         swf_SetU16(i->tag, i->textid);
747         //swf_SetU8(i->tag, /*subpixel grid*/(2<<3)|/*flashtype*/0x40);
748         //swf_SetU8(i->tag, /*grid*/(1<<3)|/*flashtype*/0x40);
749         //swf_SetU8(i->tag, /*grid*/(0<<3)|/*flashtype*/0x40);
750         swf_SetU8(i->tag, /*grid*/(1<<3)|/*no flashtype*/0x00);
751         swf_SetU32(i->tag, 0);//thickness
752         swf_SetU32(i->tag, 0);//sharpness
753         swf_SetU8(i->tag, 0);//reserved
754     }
755     i->tag = swf_InsertTag(i->tag,ST_PLACEOBJECT2);
756
757     swf_ObjectPlace(i->tag,i->textid,getNewDepth(dev),&i->page_matrix,NULL,NULL);
758     i->textid = -1;
759 }
760
761 /* set's the matrix which is to be applied to characters drawn by swfoutput_drawchar() */
762 static void swfoutput_setfontmatrix(gfxdevice_t*dev,double m11,double m21,
763                                                   double m12,double m22)
764 {
765     m11 *= 1024;
766     m12 *= 1024;
767     m21 *= 1024;
768     m22 *= 1024;
769     swfoutput_internal*i = (swfoutput_internal*)dev->internal;
770     if(i->fontm11 == m11 &&
771        i->fontm12 == m12 &&
772        i->fontm21 == m21 &&
773        i->fontm22 == m22)
774         return;
775    if(i->textid>=0)
776         endtext(dev);
777     i->fontm11 = m11;
778     i->fontm12 = m12;
779     i->fontm21 = m21;
780     i->fontm22 = m22;
781     
782     MATRIX m;
783     m.sx = (U32)(((i->fontm11)*65536)/FONT_INTERNAL_SIZE); m.r1 = (U32)(((i->fontm12)*65536)/FONT_INTERNAL_SIZE);
784     m.r0 = (U32)(((i->fontm21)*65536)/FONT_INTERNAL_SIZE); m.sy = (U32)(((i->fontm22)*65536)/FONT_INTERNAL_SIZE); 
785     m.tx = 0;
786     m.ty = 0;
787     i->fontmatrix = m;
788 }
789
790 static void endpage(gfxdevice_t*dev)
791 {
792     swfoutput_internal*i = (swfoutput_internal*)dev->internal;
793     if(i->shapeid>=0)
794       endshape(dev);
795     if(i->textid>=0)
796       endtext(dev);
797     while(i->clippos)
798         dev->endclip(dev);
799     i->pagefinished = 1;
800 }
801
802 void swf_startframe(gfxdevice_t*dev, int width, int height)
803 {
804     swfoutput_internal*i = (swfoutput_internal*)dev->internal;
805     if(!i->firstpage && !i->pagefinished)
806         endpage(dev);
807     msg("<verbose> Starting new SWF page of size %dx%d", width, height);
808
809     swf_GetMatrix(0, &i->page_matrix);
810     i->page_matrix.tx = 0;
811     i->page_matrix.ty = 0;
812     i->min_x = 0;
813     i->min_y = 0;
814     i->max_x = width;
815     i->max_y = height;
816
817     /* set clipping/background rectangle */
818     /* TODO: this should all be done in SWFOutputDev */
819     //setBackground(dev, x1, y1, x2, y2);
820
821     /* increase SWF's bounding box */
822     SRECT r;
823     r.xmin = 0;
824     r.ymin = 0;
825     r.xmax = width*20;
826     r.ymax = height*20;
827     swf_ExpandRect2(&i->swf->movieSize, &r);
828
829     i->lastframeno = i->frameno;
830     i->firstpage = 0;
831     i->pagefinished = 0;
832 }
833
834 void swf_endframe(gfxdevice_t*dev)
835 {
836     swfoutput_internal*i = (swfoutput_internal*)dev->internal;
837     
838     if(!i->pagefinished)
839         endpage(dev);
840
841     if(i->config_insertstoptag) {
842         ActionTAG*atag=0;
843         atag = action_Stop(atag);
844         atag = action_End(atag);
845         i->tag = swf_InsertTag(i->tag,ST_DOACTION);
846         swf_ActionSet(i->tag,atag);
847     }
848     i->tag = swf_InsertTag(i->tag,ST_SHOWFRAME);
849     i->frameno ++;
850     
851     for(i->depth;i->depth>i->startdepth;i->depth--) {
852         i->tag = swf_InsertTag(i->tag,ST_REMOVEOBJECT2);
853         swf_SetU16(i->tag,i->depth);
854     }
855     i->depth = i->startdepth;
856 }
857
858 static void setBackground(gfxdevice_t*dev, int x1, int y1, int x2, int y2)
859 {
860     swfoutput_internal*i = (swfoutput_internal*)dev->internal;
861     RGBA rgb;
862     rgb.a = rgb.r = rgb.g = rgb.b = 0xff;
863     SRECT r;
864     SHAPE* s;
865     int ls1=0,fs1=0;
866     int shapeid = getNewID(dev);
867     r.xmin = x1;
868     r.ymin = y1;
869     r.xmax = x2;
870     r.ymax = y2;
871     i->tag = swf_InsertTag(i->tag, ST_DEFINESHAPE);
872     swf_ShapeNew(&s);
873     fs1 = swf_ShapeAddSolidFillStyle(s, &rgb);
874     swf_SetU16(i->tag,shapeid);
875     swf_SetRect(i->tag,&r);
876     swf_SetShapeHeader(i->tag,s);
877     swf_ShapeSetAll(i->tag,s,x1,y1,ls1,fs1,0);
878     swf_ShapeSetLine(i->tag,s,(x2-x1),0);
879     swf_ShapeSetLine(i->tag,s,0,(y2-y1));
880     swf_ShapeSetLine(i->tag,s,(x1-x2),0);
881     swf_ShapeSetLine(i->tag,s,0,(y1-y2));
882     swf_ShapeSetEnd(i->tag);
883     swf_ShapeFree(s);
884     i->tag = swf_InsertTag(i->tag, ST_PLACEOBJECT2);
885     swf_ObjectPlace(i->tag,shapeid,getNewDepth(dev),0,0,0);
886     i->tag = swf_InsertTag(i->tag, ST_PLACEOBJECT2);
887     swf_ObjectPlaceClip(i->tag,shapeid,getNewDepth(dev),0,0,0,65535);
888 }
889
890 /* initialize the swf writer */
891 void gfxdevice_swf_init(gfxdevice_t* dev)
892 {
893     memset(dev, 0, sizeof(gfxdevice_t));
894     
895     dev->name = "swf";
896
897     dev->internal = init_internal_struct();
898
899     dev->startpage = swf_startframe;
900     dev->endpage = swf_endframe;
901     dev->finish = swf_finish;
902     dev->fillbitmap = swf_fillbitmap;
903     dev->setparameter = swf_setparameter;
904     dev->stroke = swf_stroke;
905     dev->startclip = swf_startclip;
906     dev->endclip = swf_endclip;
907     dev->fill = swf_fill;
908     dev->fillbitmap = swf_fillbitmap;
909     dev->fillgradient = swf_fillgradient;
910     dev->addfont = swf_addfont;
911     dev->drawchar = swf_drawchar;
912     dev->drawlink = swf_drawlink;
913
914     swfoutput_internal*i = (swfoutput_internal*)dev->internal;
915     i->dev = dev;
916
917     SRECT r;
918     RGBA rgb;
919
920     msg("<verbose> initializing swf output\n", i->max_x,i->max_y);
921
922     i->swffont = 0;
923    
924     i->swf = (SWF*)rfx_calloc(sizeof(SWF));
925     i->swf->fileVersion    = i->config_flashversion;
926     i->swf->frameRate      = 0x0040; // 1 frame per 4 seconds
927     i->swf->movieSize.xmin = 0;
928     i->swf->movieSize.ymin = 0;
929     i->swf->movieSize.xmax = 0;
930     i->swf->movieSize.ymax = 0;
931     
932     i->swf->firstTag = swf_InsertTag(NULL,ST_SETBACKGROUNDCOLOR);
933     i->tag = i->swf->firstTag;
934     rgb.a = rgb.r = rgb.g = rgb.b = 0xff;
935     swf_SetRGB(i->tag,&rgb);
936
937     i->startdepth = i->depth = 0;
938     
939     if(i->config_protect)
940       i->tag = swf_InsertTag(i->tag, ST_PROTECT);
941 }
942
943 static void startshape(gfxdevice_t*dev)
944 {
945     swfoutput_internal*i = (swfoutput_internal*)dev->internal;
946     SRECT r;
947
948     if(i->shapeid>=0)
949         return;
950
951     if(i->textid>=0)
952         endtext(dev);
953
954     i->tag = swf_InsertTag(i->tag,ST_DEFINESHAPE3);
955
956     swf_ShapeNew(&i->shape);
957     i->linestyleid = swf_ShapeAddLineStyle(i->shape,i->linewidth,&i->strokergb);
958     i->fillstyleid = swf_ShapeAddSolidFillStyle(i->shape,&i->fillrgb);
959     if(i->mark) {
960         RGBA markcol = {0,i->mark[0],i->mark[1],i->mark[2]};
961         swf_ShapeAddSolidFillStyle(i->shape,&markcol);
962     }
963
964     i->shapeid = getNewID(dev);
965     
966     msg("<debug> Using shape id %d", i->shapeid);
967
968     swf_SetU16(i->tag,i->shapeid);  // ID
969
970     i->bboxrectpos = i->tag->len;
971     /* changed later */
972     r.xmin = 0;
973     r.ymin = 0;
974     r.xmax = 20*i->max_x;
975     r.ymax = 20*i->max_y;
976     swf_SetRect(i->tag,&r);
977    
978     memset(&i->bboxrect, 0, sizeof(i->bboxrect));
979
980     swf_SetShapeStyles(i->tag,i->shape);
981     swf_ShapeCountBits(i->shape,NULL,NULL);
982     swf_SetShapeBits(i->tag,i->shape);
983
984     /* TODO: do we really need this? */
985     //swf_ShapeSetAll(i->tag,i->shape,/*x*/0,/*y*/0,i->linestyleid,0,0);
986     //swf_ShapeSetAll(i->tag,i->shape,/*x*/UNDEFINED_COORD,/*y*/UNDEFINED_COORD,i->linestyleid,0,0);
987     i->swflastx=i->swflasty=UNDEFINED_COORD;
988     i->lastwasfill = -1;
989     i->shapeisempty = 1;
990 }
991
992 static void starttext(gfxdevice_t*dev)
993 {
994     swfoutput_internal*i = (swfoutput_internal*)dev->internal;
995     if(i->shapeid>=0)
996         endshape(dev);
997       
998     i->textid = getNewID(dev);
999
1000     i->swflastx=i->swflasty=0;
1001 }
1002             
1003
1004 /* TODO: move to ../lib/rfxswf */
1005 void changeRect(gfxdevice_t*dev, TAG*tag, int pos, SRECT*newrect)
1006 {
1007     swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1008     /* determine length of old rect */
1009     tag->pos = pos;
1010     tag->readBit = 0;
1011     SRECT old;
1012     swf_GetRect(tag, &old);
1013     swf_ResetReadBits(tag);
1014     int pos_end = tag->pos;
1015
1016     int len = tag->len - pos_end;
1017     U8*data = (U8*)malloc(len);
1018     memcpy(data, &tag->data[pos_end], len);
1019     tag->writeBit = 0;
1020     tag->len = pos;
1021     swf_SetRect(tag, newrect);
1022     swf_SetBlock(tag, data, len);
1023     free(data);
1024     tag->pos = tag->readBit = 0;
1025 }
1026
1027 void cancelshape(gfxdevice_t*dev)
1028 {
1029     swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1030     /* delete old shape tag */
1031     TAG*todel = i->tag;
1032     i->tag = i->tag->prev;
1033     swf_DeleteTag(todel);
1034     if(i->shape) {swf_ShapeFree(i->shape);i->shape=0;}
1035     i->shapeid = -1;
1036     i->bboxrectpos = -1;
1037
1038 //    i->currentswfid--; // this was an *exceptionally* bad idea
1039 }
1040
1041 void fixAreas(gfxdevice_t*dev)
1042 {
1043     swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1044     if(!i->shapeisempty && i->fill &&
1045        (i->bboxrect.xmin == i->bboxrect.xmax ||
1046         i->bboxrect.ymin == i->bboxrect.ymax) &&
1047         i->config_minlinewidth >= 0.001
1048        ) {
1049         msg("<debug> Shape has size 0: width=%.2f height=%.2f",
1050                 (i->bboxrect.xmax-i->bboxrect.xmin)/20.0,
1051                 (i->bboxrect.ymax-i->bboxrect.ymin)/20.0
1052                 );
1053     
1054         SRECT r = i->bboxrect;
1055         
1056         if(r.xmin == r.xmax && r.ymin == r.ymax) {
1057             /* this thing comes down to a single dot- nothing to fix here */
1058             return;
1059         }
1060
1061         cancelshape(dev);
1062
1063         RGBA save_col = i->strokergb;
1064         int  save_width = i->linewidth;
1065
1066         i->strokergb = i->fillrgb;
1067         i->linewidth = (int)(i->config_minlinewidth*20);
1068         if(i->linewidth==0) i->linewidth = 1;
1069         
1070         startshape(dev);
1071         stopFill(dev);
1072
1073         moveto(dev, i->tag, r.xmin/20.0,r.ymin/20.0);
1074         lineto(dev, i->tag, r.xmax/20.0,r.ymax/20.0);
1075
1076         i->strokergb = save_col;
1077         i->linewidth = save_width;
1078     }
1079     
1080 }
1081
1082 static void endshape_noput(gfxdevice_t*dev)
1083 {
1084     swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1085     if(i->shapeid<0) 
1086         return;
1087     //changeRect(dev, i->tag, i->bboxrectpos, &i->bboxrect);
1088     i->shapeid = -1;
1089     if(i->shape) {
1090         swf_ShapeFree(i->shape);
1091         i->shape=0;
1092     }
1093     i->fill=0;
1094     i->shapeposx=0;
1095     i->shapeposy=0;
1096 }
1097
1098 static void endshape(gfxdevice_t*dev)
1099 {
1100     swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1101     if(i->shapeid<0) 
1102         return;
1103
1104     fixAreas(dev);
1105         
1106     if(i->shapeisempty ||
1107        /*bbox empty?*/
1108        (i->bboxrect.xmin == i->bboxrect.xmax && 
1109         i->bboxrect.ymin == i->bboxrect.ymax))
1110     {
1111         // delete the shape again, we didn't do anything
1112         msg("<debug> cancelling shape: bbox is (%f,%f,%f,%f)",
1113                 i->bboxrect.xmin /20.0,
1114                 i->bboxrect.ymin /20.0,
1115                 i->bboxrect.xmax /20.0,
1116                 i->bboxrect.ymax /20.0
1117                 );
1118         cancelshape(dev);
1119         return;
1120     }
1121     
1122     swf_ShapeSetEnd(i->tag);
1123
1124     changeRect(dev, i->tag, i->bboxrectpos, &i->bboxrect);
1125
1126     msg("<trace> Placing shape ID %d", i->shapeid);
1127
1128     i->tag = swf_InsertTag(i->tag,ST_PLACEOBJECT2);
1129     MATRIX m = i->page_matrix;
1130     m.tx += i->shapeposx;
1131     m.ty += i->shapeposy;
1132     swf_ObjectPlace(i->tag,i->shapeid,getNewDepth(dev),&m,NULL,NULL);
1133
1134     swf_ShapeFree(i->shape);
1135     i->shape = 0;
1136     i->shapeid = -1;
1137     i->bboxrectpos = -1;
1138
1139     i->fill=0;
1140     i->shapeposx=0;
1141     i->shapeposy=0;
1142 }
1143
1144 void wipeSWF(SWF*swf)
1145 {
1146     TAG*tag = swf->firstTag;
1147     while(tag) {
1148         TAG*next = tag->next;
1149         if(tag->id != ST_SETBACKGROUNDCOLOR &&
1150            tag->id != ST_END &&
1151            tag->id != ST_DOACTION &&
1152            tag->id != ST_SHOWFRAME) {
1153             swf_DeleteTag(tag);
1154         }
1155         tag = next;
1156     }
1157 }
1158
1159
1160 void swfoutput_finalize(gfxdevice_t*dev)
1161 {
1162     swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1163
1164     if(i->tag && i->tag->id == ST_END)
1165         return; //already done
1166
1167     if(i->config_bboxvars) {
1168         TAG* tag = swf_InsertTag(i->swf->firstTag, ST_DOACTION);
1169         ActionTAG*a = 0;
1170         a = action_PushString(a, "xmin");
1171         a = action_PushFloat(a, i->swf->movieSize.xmin / 20.0);
1172         a = action_SetVariable(a);
1173         a = action_PushString(a, "ymin");
1174         a = action_PushFloat(a, i->swf->movieSize.ymin / 20.0);
1175         a = action_SetVariable(a);
1176         a = action_PushString(a, "xmax");
1177         a = action_PushFloat(a, i->swf->movieSize.xmax / 20.0);
1178         a = action_SetVariable(a);
1179         a = action_PushString(a, "ymax");
1180         a = action_PushFloat(a, i->swf->movieSize.ymax / 20.0);
1181         a = action_SetVariable(a);
1182         a = action_PushString(a, "width");
1183         a = action_PushFloat(a, (i->swf->movieSize.xmax - i->swf->movieSize.xmin) / 20.0);
1184         a = action_SetVariable(a);
1185         a = action_PushString(a, "height");
1186         a = action_PushFloat(a, (i->swf->movieSize.ymax - i->swf->movieSize.ymin) / 20.0);
1187         a = action_SetVariable(a);
1188         a = action_End(a);
1189         swf_ActionSet(tag, a);
1190         swf_ActionFree(a);
1191     }
1192
1193     if(i->frameno == i->lastframeno) // fix: add missing pagefeed
1194         dev->endpage(dev);
1195
1196     if(i->mark) {
1197         free(i->mark);i->mark = 0;
1198     }
1199
1200     endpage(dev);
1201     fontlist_t *tmp,*iterator = i->fontlist;
1202     while(iterator) {
1203         TAG*mtag = i->swf->firstTag;
1204         if(iterator->swffont) {
1205             if(!i->config_storeallcharacters) {
1206                 msg("<debug> Reducing font %s", iterator->swffont->name);
1207                 swf_FontReduce(iterator->swffont);
1208             }
1209             int used = iterator->swffont->use && iterator->swffont->use->used_glyphs;
1210             if(i->config_storeallcharacters || used) {
1211                 mtag = swf_InsertTag(mtag, ST_DEFINEFONT2);
1212                 swf_FontSetDefine2(mtag, iterator->swffont);
1213             }
1214         }
1215
1216         iterator = iterator->next;
1217     }
1218     i->tag = swf_InsertTag(i->tag,ST_END);
1219     TAG* tag = i->tag->prev;
1220
1221     /* remove the removeobject2 tags between the last ST_SHOWFRAME
1222        and the ST_END- they confuse the flash player  */
1223     while(tag->id == ST_REMOVEOBJECT2) {
1224         TAG* prev = tag->prev;
1225         swf_DeleteTag(tag);
1226         tag = prev;
1227     }
1228     
1229     if(i->overflow) {
1230         wipeSWF(i->swf);
1231     }
1232     if(i->config_enablezlib || i->config_flashversion>=6) {
1233         i->swf->compressed = 1;
1234     }
1235
1236     if(i->config_reordertags)
1237         swf_Optimize(i->swf);
1238 }
1239
1240 int swfresult_save(gfxresult_t*gfx, char*filename)
1241 {
1242     SWF*swf = (SWF*)gfx->internal;
1243     int fi;
1244     if(filename)
1245      fi = open(filename, O_BINARY|O_CREAT|O_TRUNC|O_WRONLY, 0777);
1246     else
1247      fi = 1; // stdout
1248     
1249     if(fi<=0) {
1250         msg("<fatal> Could not create \"%s\". ", FIXNULL(filename));
1251         return -1;
1252     }
1253     
1254     if(swf->compressed) {
1255         if FAILED(swf_WriteSWC(fi,swf)) 
1256             msg("<error> WriteSWC() failed.\n");
1257     } else {
1258         if FAILED(swf_WriteSWF(fi,swf)) 
1259             msg("<error> WriteSWF() failed.\n");
1260     }
1261
1262     if(filename)
1263      close(fi);
1264     return 0;
1265 }
1266 void* swfresult_get(gfxresult_t*gfx, char*name)
1267 {
1268     SWF*swf = (SWF*)gfx->internal;
1269     if(!strcmp(name, "swf")) {
1270         return (void*)swf_CopySWF(swf);
1271     } else if(!strcmp(name, "xmin")) {
1272         return (void*)(swf->movieSize.xmin/20);
1273     } else if(!strcmp(name, "ymin")) {
1274         return (void*)(swf->movieSize.ymin/20);
1275     } else if(!strcmp(name, "xmax")) {
1276         return (void*)(swf->movieSize.xmax/20);
1277     } else if(!strcmp(name, "ymax")) {
1278         return (void*)(swf->movieSize.ymax/20);
1279     } else if(!strcmp(name, "width")) {
1280         return (void*)((swf->movieSize.xmax - swf->movieSize.xmin)/20);
1281     } else if(!strcmp(name, "height")) {
1282         return (void*)((swf->movieSize.ymax - swf->movieSize.ymin)/20);
1283     }
1284     return 0;
1285 }
1286 void swfresult_destroy(gfxresult_t*gfx)
1287 {
1288     if(gfx->internal) {
1289         swf_FreeTags((SWF*)gfx->internal);
1290         free(gfx->internal);
1291         gfx->internal = 0;
1292     }
1293     memset(gfx, 0, sizeof(gfxresult_t));
1294     free(gfx);
1295 }
1296
1297 static void swfoutput_destroy(gfxdevice_t* dev);
1298
1299 gfxresult_t* swf_finish(gfxdevice_t* dev)
1300 {
1301     swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1302     gfxresult_t*result;
1303
1304     if(i->config_linktarget) {
1305         free(i->config_linktarget);
1306         i->config_linktarget = 0;
1307     }
1308
1309     swfoutput_finalize(dev);
1310     SWF* swf = i->swf;i->swf = 0;
1311     swfoutput_destroy(dev);
1312
1313     result = (gfxresult_t*)rfx_calloc(sizeof(gfxresult_t));
1314     result->internal = swf;
1315     result->save = swfresult_save;
1316     result->write = 0;
1317     result->get = swfresult_get;
1318     result->destroy = swfresult_destroy;
1319     return result;
1320 }
1321
1322 /* Perform cleaning up */
1323 static void swfoutput_destroy(gfxdevice_t* dev) 
1324 {
1325     swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1326     if(!i) {
1327         /* not initialized yet- nothing to destroy */
1328         return;
1329     }
1330
1331     fontlist_t *tmp,*iterator = i->fontlist;
1332     while(iterator) {
1333         if(iterator->swffont) {
1334             swf_FontFree(iterator->swffont);iterator->swffont=0;
1335         }
1336         tmp = iterator;
1337         iterator = iterator->next;
1338         free(tmp);
1339     }
1340     if(i->swf) {swf_FreeTags(i->swf);free(i->swf);i->swf = 0;}
1341
1342     free(i);i=0;
1343     memset(dev, 0, sizeof(gfxdevice_t));
1344 }
1345
1346 static void swfoutput_setfillcolor(gfxdevice_t* dev, U8 r, U8 g, U8 b, U8 a)
1347 {
1348     swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1349     if(i->fillrgb.r == r &&
1350        i->fillrgb.g == g &&
1351        i->fillrgb.b == b &&
1352        i->fillrgb.a == a) return;
1353     if(i->shapeid>=0)
1354      endshape(dev);
1355
1356     i->fillrgb.r = r;
1357     i->fillrgb.g = g;
1358     i->fillrgb.b = b;
1359     i->fillrgb.a = a;
1360 }
1361
1362 static void swfoutput_setstrokecolor(gfxdevice_t* dev, U8 r, U8 g, U8 b, U8 a)
1363 {
1364     swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1365     if(i->strokergb.r == r &&
1366        i->strokergb.g == g &&
1367        i->strokergb.b == b &&
1368        i->strokergb.a == a) return;
1369
1370     if(i->shapeid>=0)
1371      endshape(dev);
1372     i->strokergb.r = r;
1373     i->strokergb.g = g;
1374     i->strokergb.b = b;
1375     i->strokergb.a = a;
1376 }
1377
1378 //#define ROUND_UP 19
1379 //#define ROUND_UP 10
1380
1381 static void swfoutput_setlinewidth(gfxdevice_t*dev, double _linewidth)
1382 {
1383     swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1384     if(i->linewidth == (U16)(_linewidth*20+19.0/20.0))
1385         return;
1386     if(i->shapeid>=0)
1387         endshape(dev);
1388     i->linewidth = (U16)(_linewidth*20+19.0/20.0);
1389 }
1390
1391
1392 static void drawlink(gfxdevice_t*dev, ActionTAG*,ActionTAG*, gfxline_t*points, char mouseover);
1393 static void swfoutput_namedlink(gfxdevice_t*dev, char*name, gfxline_t*points);
1394 static void swfoutput_linktopage(gfxdevice_t*dev, int page, gfxline_t*points);
1395 static void swfoutput_linktourl(gfxdevice_t*dev, char*url, gfxline_t*points);
1396
1397 void swfoutput_drawlink(gfxdevice_t*dev, char*url, gfxline_t*points)
1398 {
1399     swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1400     dev->drawlink(dev, points, url);
1401 }
1402
1403 void swf_drawlink(gfxdevice_t*dev, gfxline_t*points, char*url)
1404 {
1405     swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1406
1407     if(!strncmp("http://pdf2swf:", url, 15)) {
1408         char*tmp = strdup(url);
1409         int l = strlen(tmp);
1410         if(tmp[l-1] == '/')
1411            tmp[l-1] = 0;
1412         swfoutput_namedlink(dev, tmp+15, points);
1413         free(tmp);
1414         return;
1415     } else if(!strncmp("page", url, 4)) {
1416         int t, nodigit=0;
1417         for(t=4;url[t];t++)
1418             if(url[t]<'0' || url[t]>'9')
1419                 nodigit = 1;
1420         if(!nodigit) {
1421             int page = atoi(&url[4]);
1422             if(page<0) page = 0;
1423             swfoutput_linktopage(dev, page, points);
1424         }
1425     } else {
1426         swfoutput_linktourl(dev, url, points);
1427     }
1428 }
1429 void swfoutput_linktourl(gfxdevice_t*dev, char*url, gfxline_t*points)
1430 {
1431     ActionTAG* actions = 0;
1432     swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1433     if(i->shapeid>=0)
1434         endshape(dev);
1435     if(i->textid>=0)
1436         endtext(dev);
1437     
1438     if(i->config_externallinkfunction) {
1439         actions = action_PushString(actions, url); //parameter
1440         actions = action_PushInt(actions, 1); //number of parameters (1)
1441         actions = action_PushString(actions, i->config_externallinkfunction); //function name
1442         actions = action_CallFunction(actions);
1443     } else if(!i->config_linktarget) {
1444         if(!i->config_opennewwindow)
1445           actions = action_GetUrl(actions, url, "_parent");
1446         else
1447           actions = action_GetUrl(actions, url, "_this");
1448     } else {
1449         actions = action_GetUrl(actions, url, i->config_linktarget);
1450     }
1451     actions = action_End(actions);
1452     
1453     drawlink(dev, actions, 0, points,0);
1454 }
1455 void swfoutput_linktopage(gfxdevice_t*dev, int page, gfxline_t*points)
1456 {
1457     swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1458     ActionTAG* actions = 0;
1459
1460     if(i->shapeid>=0)
1461         endshape(dev);
1462     if(i->textid>=0)
1463         endtext(dev);
1464   
1465     if(!i->config_internallinkfunction) {
1466         actions = action_GotoFrame(actions, page-1);
1467         actions = action_End(actions);
1468     } else {
1469         actions = action_PushInt(actions, page); //parameter
1470         actions = action_PushInt(actions, 1); //number of parameters (1)
1471         actions = action_PushString(actions, i->config_internallinkfunction); //function name
1472         actions = action_CallFunction(actions);
1473         actions = action_End(actions);
1474     }
1475
1476     drawlink(dev, actions, 0, points,0);
1477 }
1478
1479 /* Named Links (a.k.a. Acrobatmenu) are used to implement various gadgets
1480    of the viewer objects, like subtitles, index elements etc.
1481 */
1482 void swfoutput_namedlink(gfxdevice_t*dev, char*name, gfxline_t*points)
1483 {
1484     swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1485     ActionTAG *actions1,*actions2;
1486     char*tmp = strdup(name);
1487     char mouseover = 1;
1488
1489     if(i->shapeid>=0)
1490         endshape(dev);
1491     if(i->textid>=0)
1492         endtext(dev);
1493
1494     if(!strncmp(tmp, "call:", 5))
1495     {
1496         char*x = strchr(&tmp[5], ':');
1497         if(!x) {
1498             actions1 = action_PushInt(0, 0); //number of parameters (0)
1499             actions1 = action_PushString(actions1, &tmp[5]); //function name
1500             actions1 = action_CallFunction(actions1);
1501             actions1 = action_End(actions1);
1502         } else {
1503             *x = 0;
1504             actions1 = action_PushString(0, x+1); //parameter
1505             actions1 = action_PushInt(actions1, 1); //number of parameters (1)
1506             actions1 = action_PushString(actions1, &tmp[5]); //function name
1507             actions1 = action_CallFunction(actions1);
1508             actions1 = action_End(actions1);
1509         }
1510         actions2 = action_End(0);
1511         mouseover = 0;
1512     }
1513     else
1514     {
1515         actions1 = action_PushString(0, "/:subtitle");
1516         actions1 = action_PushString(actions1, name);
1517         actions1 = action_SetVariable(actions1);
1518         actions1 = action_End(actions1);
1519
1520         actions2 = action_PushString(0, "/:subtitle");
1521         actions2 = action_PushString(actions2, "");
1522         actions2 = action_SetVariable(actions2);
1523         actions2 = action_End(actions2);
1524     }
1525
1526     drawlink(dev, actions1, actions2, points,mouseover);
1527
1528     swf_ActionFree(actions1);
1529     swf_ActionFree(actions2);
1530     free(tmp);
1531 }
1532
1533 static void drawgfxline(gfxdevice_t*dev, gfxline_t*line)
1534 {
1535     swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1536     gfxcoord_t lastx=0,lasty=0,px=0,py=0;
1537     char lastwasmoveto;
1538     int lines= 0, splines=0;
1539     while(1) {
1540         if(!line)
1541             break;
1542         /* check whether the next segment is zero */
1543         if(line->type == gfx_moveTo) {
1544             moveto(dev, i->tag, line->x, line->y);
1545             px = lastx = line->x;
1546             py = lasty = line->y;
1547             lastwasmoveto = 1;
1548         } if(line->type == gfx_lineTo) {
1549             lineto(dev, i->tag, line->x, line->y);
1550             px = line->x;
1551             py = line->y;
1552             lastwasmoveto = 0;
1553             lines++;
1554         } else if(line->type == gfx_splineTo) {
1555             plotxy_t s,p;
1556             s.x = line->sx;p.x = line->x;
1557             s.y = line->sy;p.y = line->y;
1558             splineto(dev, i->tag, s, p);
1559             px = line->x;
1560             py = line->y;
1561             lastwasmoveto = 0;
1562             splines++;
1563         }
1564         line = line->next;
1565     }
1566     msg("<trace> drawgfxline, %d lines, %d splines", lines, splines);
1567 }
1568
1569
1570 static void drawlink(gfxdevice_t*dev, ActionTAG*actions1, ActionTAG*actions2, gfxline_t*points, char mouseover)
1571 {
1572     swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1573     RGBA rgb;
1574     SRECT r;
1575     int lsid=0;
1576     int fsid;
1577     plotxy_t p1,p2,p3,p4;
1578     int myshapeid;
1579     int myshapeid2;
1580     double posx = 0;
1581     double posy = 0;
1582     int t;
1583     int buttonid = getNewID(dev);
1584     gfxbbox_t bbox = gfxline_getbbox(points);
1585
1586     /* shape */
1587     myshapeid = getNewID(dev);
1588     i->tag = swf_InsertTag(i->tag,ST_DEFINESHAPE3);
1589     swf_ShapeNew(&i->shape);
1590     rgb.r = rgb.b = rgb.a = rgb.g = 0; 
1591     fsid = swf_ShapeAddSolidFillStyle(i->shape,&rgb);
1592     swf_SetU16(i->tag, myshapeid);
1593     r.xmin = (int)(bbox.xmin*20);
1594     r.ymin = (int)(bbox.ymin*20);
1595     r.xmax = (int)(bbox.xmax*20);
1596     r.ymax = (int)(bbox.ymax*20);
1597     swf_SetRect(i->tag,&r);
1598     swf_SetShapeStyles(i->tag,i->shape);
1599     swf_ShapeCountBits(i->shape,NULL,NULL);
1600     swf_SetShapeBits(i->tag,i->shape);
1601     swf_ShapeSetAll(i->tag,i->shape,/*x*/0,/*y*/0,0,fsid,0);
1602     i->swflastx = i->swflasty = 0;
1603     drawgfxline(dev, points);
1604     swf_ShapeSetEnd(i->tag);
1605
1606     /* shape2 */
1607     myshapeid2 = getNewID(dev);
1608     i->tag = swf_InsertTag(i->tag,ST_DEFINESHAPE3);
1609     swf_ShapeNew(&i->shape);
1610     
1611     rgb = i->config_linkcolor;
1612
1613     fsid = swf_ShapeAddSolidFillStyle(i->shape,&rgb);
1614     swf_SetU16(i->tag, myshapeid2);
1615     r.xmin = (int)(bbox.xmin*20);
1616     r.ymin = (int)(bbox.ymin*20);
1617     r.xmax = (int)(bbox.xmax*20);
1618     r.ymax = (int)(bbox.ymax*20);
1619     swf_SetRect(i->tag,&r);
1620     swf_SetShapeStyles(i->tag,i->shape);
1621     swf_ShapeCountBits(i->shape,NULL,NULL);
1622     swf_SetShapeBits(i->tag,i->shape);
1623     swf_ShapeSetAll(i->tag,i->shape,/*x*/0,/*y*/0,0,fsid,0);
1624     i->swflastx = i->swflasty = 0;
1625     drawgfxline(dev, points);
1626     swf_ShapeSetEnd(i->tag);
1627
1628     if(!mouseover)
1629     {
1630         i->tag = swf_InsertTag(i->tag,ST_DEFINEBUTTON);
1631         swf_SetU16(i->tag,buttonid); //id
1632         swf_ButtonSetFlags(i->tag, 0); //menu=no
1633         swf_ButtonSetRecord(i->tag,0x01,myshapeid,i->depth,0,0);
1634         swf_ButtonSetRecord(i->tag,0x02,myshapeid2,i->depth,0,0);
1635         swf_ButtonSetRecord(i->tag,0x04,myshapeid2,i->depth,0,0);
1636         swf_ButtonSetRecord(i->tag,0x08,myshapeid,i->depth,0,0);
1637         swf_SetU8(i->tag,0);
1638         swf_ActionSet(i->tag,actions1);
1639         swf_SetU8(i->tag,0);
1640     }
1641     else
1642     {
1643         i->tag = swf_InsertTag(i->tag,ST_DEFINEBUTTON2);
1644         swf_SetU16(i->tag,buttonid); //id
1645         swf_ButtonSetFlags(i->tag, 0); //menu=no
1646         swf_ButtonSetRecord(i->tag,0x01,myshapeid,i->depth,0,0);
1647         swf_ButtonSetRecord(i->tag,0x02,myshapeid2,i->depth,0,0);
1648         swf_ButtonSetRecord(i->tag,0x04,myshapeid2,i->depth,0,0);
1649         swf_ButtonSetRecord(i->tag,0x08,myshapeid,i->depth,0,0);
1650         swf_SetU8(i->tag,0); // end of button records
1651         swf_ButtonSetCondition(i->tag, BC_IDLE_OVERUP);
1652         swf_ActionSet(i->tag,actions1);
1653         if(actions2) {
1654             swf_ButtonSetCondition(i->tag, BC_OVERUP_IDLE);
1655             swf_ActionSet(i->tag,actions2);
1656             swf_SetU8(i->tag,0);
1657             swf_ButtonPostProcess(i->tag, 2);
1658         } else {
1659             swf_SetU8(i->tag,0);
1660             swf_ButtonPostProcess(i->tag, 1);
1661         }
1662     }
1663     
1664     msg("<trace> Placing link ID %d", buttonid);
1665     i->tag = swf_InsertTag(i->tag,ST_PLACEOBJECT2);
1666
1667     if(posx!=0 || posy!=0) {
1668         SPOINT p;
1669         p.x = (int)(posx*20);
1670         p.y = (int)(posy*20);
1671         p = swf_TurnPoint(p, &i->page_matrix);
1672         MATRIX m;
1673         m = i->page_matrix;
1674         m.tx = p.x;
1675         m.ty = p.y;
1676         swf_ObjectPlace(i->tag, buttonid, getNewDepth(dev),&m,0,0);
1677     } else {
1678         swf_ObjectPlace(i->tag, buttonid, getNewDepth(dev),&i->page_matrix,0,0);
1679     }
1680 }
1681
1682       
1683 ///////////
1684 /*
1685 for(t=0;t<picpos;t++)
1686       {
1687           if(pic_xids[t] == xid &&
1688              pic_yids[t] == yid) {
1689               width = pic_width[t];
1690               height = pic_height[t];
1691               found = t;break;
1692           }
1693       }
1694           pic_ids[picpos] = swfoutput_drawimagelosslessN(&output, pic, pal, width, height, x1,y1,x2,y2,x3,y3,x4,y4, numpalette);
1695           pic_xids[picpos] = xid;
1696           pic_yids[picpos] = yid;
1697           pic_width[picpos] = width;
1698           pic_height[picpos] = height;
1699           if(picpos<1024)
1700               picpos++;
1701             pic[width*y+x] = buf[0];
1702             xid+=x*buf[0]+1;
1703             yid+=y*buf[0]*3+1;
1704       
1705             xid += pal[1].r*3 + pal[1].g*11 + pal[1].b*17;
1706       yid += pal[1].r*7 + pal[1].g*5 + pal[1].b*23;
1707       
1708       int xid = 0;
1709       int yid = 0;
1710           xid += x*r+x*b*3+x*g*7+x*a*11;
1711           yid += y*r*3+y*b*17+y*g*19+y*a*11;
1712       int t,found = -1;
1713       for(t=0;t<picpos;t++)
1714       {
1715           if(pic_xids[t] == xid &&
1716              pic_yids[t] == yid) {
1717               found = t;break;
1718           }
1719       }
1720       if(found<0) {
1721 */
1722 ///////////
1723
1724
1725 int swf_setparameter(gfxdevice_t*dev, const char*name, const char*value)
1726 {
1727     swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1728
1729     msg("<trace> swfdevice: %s=%s", name, value);
1730     if(!strcmp(name, "jpegsubpixels")) {
1731         i->config_jpegsubpixels = atof(value);
1732     } else if(!strcmp(name, "ppmsubpixels")) {
1733         i->config_ppmsubpixels = atof(value);
1734     } else if(!strcmp(name, "subpixels")) {
1735         i->config_ppmsubpixels = i->config_jpegsubpixels = atof(value);
1736     } else if(!strcmp(name, "drawonlyshapes")) {
1737         i->config_drawonlyshapes = atoi(value);
1738     } else if(!strcmp(name, "ignoredraworder")) {
1739         i->config_ignoredraworder = atoi(value);
1740     } else if(!strcmp(name, "mark")) {
1741         if(!value || !value[0]) {
1742             if(i->mark) free(i->mark);
1743             i->mark = 0;
1744         } else {
1745             int t;
1746             i->mark = strdup("...");
1747             for(t=0;t<3;t++) if(value[t]) i->mark[t] = value[t];
1748         }
1749     } else if(!strcmp(name, "filloverlap")) {
1750         i->config_filloverlap = atoi(value);
1751     } else if(!strcmp(name, "linksopennewwindow")) {
1752         i->config_opennewwindow = atoi(value);
1753     } else if(!strcmp(name, "opennewwindow")) {
1754         i->config_opennewwindow = atoi(value);
1755     } else if(!strcmp(name, "storeallcharacters")) {
1756         i->config_storeallcharacters = atoi(value);
1757     } else if(!strcmp(name, "enablezlib")) {
1758         i->config_enablezlib = atoi(value);
1759     } else if(!strcmp(name, "bboxvars")) {
1760         i->config_bboxvars = atoi(value);
1761     } else if(!strcmp(name, "reordertags")) {
1762         i->config_reordertags = atoi(value);
1763     } else if(!strcmp(name, "internallinkfunction")) {
1764         i->config_internallinkfunction = strdup(value);
1765     } else if(!strcmp(name, "externallinkfunction")) {
1766         i->config_externallinkfunction = strdup(value);
1767     } else if(!strcmp(name, "disable_polygon_conversion")) {
1768         i->config_disable_polygon_conversion = atoi(value);
1769     } else if(!strcmp(name, "insertstop")) {
1770         i->config_insertstoptag = atoi(value);
1771     } else if(!strcmp(name, "protect")) {
1772         i->config_protect = atoi(value);
1773         if(i->config_protect && i->tag) {
1774             i->tag = swf_InsertTag(i->tag, ST_PROTECT);
1775         }
1776     } else if(!strcmp(name, "faketags")) {
1777         i->config_generate_fake_tags = atoi(value);
1778     } else if(!strcmp(name, "flashversion")) {
1779         i->config_flashversion = atoi(value);
1780         if(i->swf) {
1781             i->swf->fileVersion = i->config_flashversion;
1782         }
1783     } else if(!strcmp(name, "minlinewidth")) {
1784         i->config_minlinewidth = atof(value);
1785     } else if(!strcmp(name, "caplinewidth")) {
1786         i->config_caplinewidth = atof(value);
1787     } else if(!strcmp(name, "linktarget")) {
1788         i->config_linktarget = strdup(value);
1789     } else if(!strcmp(name, "dumpfonts")) {
1790         i->config_dumpfonts = atoi(value);
1791     } else if(!strcmp(name, "next_bitmap_is_jpeg")) {
1792         i->jpeg = 1;
1793     } else if(!strcmp(name, "jpegquality")) {
1794         int val = atoi(value);
1795         if(val<0) val=0;
1796         if(val>100) val=100;
1797         i->config_jpegquality = val;
1798     } else if(!strcmp(name, "splinequality")) {
1799         int v = atoi(value);
1800         v = 500-(v*5); // 100% = 0.25 pixel, 0% = 25 pixel
1801         if(v<1) v = 1;
1802         i->config_splinemaxerror = v;
1803     } else if(!strcmp(name, "fontquality")) {
1804         int v = atoi(value);
1805         v = 500-(v*5); // 100% = 0.25 pixel, 0% = 25 pixel
1806         if(v<1) v = 1;
1807         i->config_fontsplinemaxerror = v;
1808     } else if(!strcmp(name, "linkcolor")) {
1809         if(strlen(value)!=8) {
1810             fprintf(stderr, "Unknown format for option 'linkcolor'. (%s <-> RRGGBBAA)\n", value);
1811             return 1;
1812         }
1813 #       define NIBBLE(s) (((s)>='0' && (s)<='9')?((s)-'0'):((s)&0x0f)+9)
1814         i->config_linkcolor.r = NIBBLE(value[0])<<4 | NIBBLE(value[1]);
1815         i->config_linkcolor.g = NIBBLE(value[2])<<4 | NIBBLE(value[3]);
1816         i->config_linkcolor.b = NIBBLE(value[4])<<4 | NIBBLE(value[5]);
1817         i->config_linkcolor.a = NIBBLE(value[6])<<4 | NIBBLE(value[7]);
1818         printf("%02x%02x%02x%02x\n", 
1819             i->config_linkcolor.r,
1820             i->config_linkcolor.g,
1821             i->config_linkcolor.b,
1822             i->config_linkcolor.a);
1823
1824     } else {
1825         return 0;
1826     }
1827     return 1;
1828 }
1829
1830 // --------------------------------------------------------------------
1831
1832 static CXFORM gfxcxform_to_cxform(gfxcxform_t* c)
1833 {
1834     CXFORM cx;
1835     swf_GetCXForm(0, &cx, 1);
1836     if(!c)
1837         return cx;
1838     if(c->rg!=0 || c->rb!=0 || c->ra!=0 ||
1839        c->gr!=0 || c->gb!=0 || c->ga!=0 ||
1840        c->br!=0 || c->bg!=0 || c->ba!=0 ||
1841        c->ar!=0 || c->ag!=0 || c->ab!=0)
1842         msg("<warning> CXForm not SWF-compatible");
1843
1844     cx.a0 = (S16)(c->aa*256);
1845     cx.r0 = (S16)(c->rr*256);
1846     cx.g0 = (S16)(c->gg*256);
1847     cx.b0 = (S16)(c->bb*256);
1848     cx.a1 = c->ta;
1849     cx.r1 = c->tr;
1850     cx.g1 = c->tg;
1851     cx.b1 = c->tb;
1852     return cx;
1853 }
1854
1855 /* TODO */
1856 static int imageInCache(gfxdevice_t*dev, void*data, int width, int height)
1857 {
1858     return -1;
1859 }
1860 static void addImageToCache(gfxdevice_t*dev, void*data, int width, int height)
1861 {
1862 }
1863     
1864 static int add_image(swfoutput_internal*i, gfximage_t*img, int targetwidth, int targetheight, int* newwidth, int* newheight)
1865 {
1866     gfxdevice_t*dev = i->dev;
1867     RGBA*newpic = 0;
1868     RGBA*mem = (RGBA*)img->data;
1869     
1870     int sizex = img->width;
1871     int sizey = img->height;
1872     int is_jpeg = i->jpeg;
1873     i->jpeg = 0;
1874
1875     int newsizex=sizex, newsizey=sizey;
1876
1877     /// {
1878     if(is_jpeg && i->config_jpegsubpixels) {
1879         newsizex = (int)(targetwidth*i->config_jpegsubpixels+0.5);
1880         newsizey = (int)(targetheight*i->config_jpegsubpixels+0.5);
1881     } else if(!is_jpeg && i->config_ppmsubpixels) {
1882         newsizex = (int)(targetwidth*i->config_ppmsubpixels+0.5);
1883         newsizey = (int)(targetheight*i->config_ppmsubpixels+0.5);
1884     }
1885     /// }
1886
1887     if(sizex<=0 || sizey<=0 || newsizex<=0 || newsizey<=0)
1888         return -1;
1889
1890     /* TODO: cache images */
1891     
1892     if(newsizex<sizex || newsizey<sizey) {
1893         msg("<verbose> Scaling %dx%d image to %dx%d", sizex, sizey, newsizex, newsizey);
1894         newpic = swf_ImageScale(mem, sizex, sizey, newsizex, newsizey);
1895         *newwidth = sizex = newsizex;
1896         *newheight  = sizey = newsizey;
1897         mem = newpic;
1898     } else {
1899         *newwidth = newsizex = sizex;
1900         *newheight = newsizey  = sizey;
1901     }
1902
1903     int num_colors = swf_ImageGetNumberOfPaletteEntries(mem,sizex,sizey,0);
1904     int has_alpha = swf_ImageHasAlpha(mem,sizex,sizey);
1905     
1906     msg("<verbose> Drawing %dx%d %s%simage (id %d) at size %dx%d (%dx%d), %s%d colors",
1907             sizex, sizey, 
1908             has_alpha?(has_alpha==2?"semi-transparent ":"transparent "):"", 
1909             is_jpeg?"jpeg-":"", i->currentswfid+1,
1910             newsizex, newsizey,
1911             targetwidth, targetheight,
1912             /*newsizex, newsizey,*/
1913             num_colors>256?">":"", num_colors>256?256:num_colors);
1914
1915     /*RGBA* pal = (RGBA*)rfx_alloc(sizeof(RGBA)*num_colors);
1916     swf_ImageGetNumberOfPaletteEntries(mem,sizex,sizey,pal);
1917     int t;
1918     for(t=0;t<num_colors;t++) {
1919         printf("%02x%02x%02x%02x ",
1920                 pal[t].r, pal[t].g, pal[t].b, pal[t].a);
1921         if((t&7)==7)
1922             printf("\n");
1923     }
1924     printf("\n");*/
1925
1926     int bitid = -1;
1927     int cacheid = imageInCache(dev, mem, sizex, sizey);
1928
1929     if(cacheid<=0) {
1930         bitid = getNewID(dev);
1931         i->tag = swf_AddImage(i->tag, bitid, mem, sizex, sizey, i->config_jpegquality);
1932         addImageToCache(dev, mem, sizex, sizey);
1933     } else {
1934         bitid = cacheid;
1935     }
1936
1937     if(newpic)
1938         free(newpic);
1939     return bitid;
1940 }
1941
1942 static SRECT gfxline_getSWFbbox(gfxline_t*line)
1943 {
1944     gfxbbox_t bbox = gfxline_getbbox(line);
1945     SRECT r;
1946     r.xmin = (int)(bbox.xmin*20);
1947     r.ymin = (int)(bbox.ymin*20);
1948     r.xmax = (int)(bbox.xmax*20);
1949     r.ymax = (int)(bbox.ymax*20);
1950     return r;
1951 }
1952
1953 int line_is_empty(gfxline_t*line)
1954 {
1955     while(line) {
1956         if(line->type != gfx_moveTo)
1957             return 0;
1958         line = line->next;
1959     }
1960     return 1;
1961 }
1962
1963 static void swf_fillbitmap(gfxdevice_t*dev, gfxline_t*line, gfximage_t*img, gfxmatrix_t*matrix, gfxcxform_t*cxform)
1964 {
1965     swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1966     
1967     if(line_is_empty(line))
1968         return;
1969
1970     endshape(dev);
1971     endtext(dev);
1972
1973     int targetx = (int)(sqrt(matrix->m00*matrix->m00 + matrix->m01*matrix->m01)*img->width);
1974     int targety = (int)(sqrt(matrix->m10*matrix->m10 + matrix->m11*matrix->m11)*img->height);
1975
1976     int newwidth=0,newheight=0;
1977     int bitid = add_image(i, img, targetx, targety, &newwidth, &newheight);
1978     if(bitid<0)
1979         return;
1980     double fx = (double)img->width / (double)newwidth;
1981     double fy = (double)img->height / (double)newheight;
1982
1983     MATRIX m;
1984     float m00,m10,tx;
1985     float m01,m11,ty;
1986     m.sx = (int)(65536*20*matrix->m00*fx); m.r1 = (int)(65536*20*matrix->m10*fy);
1987     m.r0 = (int)(65536*20*matrix->m01*fx); m.sy = (int)(65536*20*matrix->m11*fy);
1988     m.tx = (int)(matrix->tx*20);
1989     m.ty = (int)(matrix->ty*20);
1990   
1991     /* shape */
1992     int myshapeid = getNewID(dev);
1993     i->tag = swf_InsertTag(i->tag,ST_DEFINESHAPE);
1994     SHAPE*shape;
1995     swf_ShapeNew(&shape);
1996     int fsid = swf_ShapeAddBitmapFillStyle(shape,&m,bitid,1);
1997     swf_SetU16(i->tag, myshapeid);
1998     SRECT r = gfxline_getSWFbbox(line);
1999     swf_SetRect(i->tag,&r);
2000     swf_SetShapeStyles(i->tag,shape);
2001     swf_ShapeCountBits(shape,NULL,NULL);
2002     swf_SetShapeBits(i->tag,shape);
2003     swf_ShapeSetAll(i->tag,shape,UNDEFINED_COORD,UNDEFINED_COORD,0,fsid,0);
2004     i->swflastx = i->swflasty = UNDEFINED_COORD;
2005     drawgfxline(dev, line);
2006     swf_ShapeSetEnd(i->tag);
2007     swf_ShapeFree(shape);
2008
2009     msg("<trace> Placing bitmap ID %d", myshapeid);
2010     i->tag = swf_InsertTag(i->tag,ST_PLACEOBJECT2);
2011     CXFORM cxform2 = gfxcxform_to_cxform(cxform);
2012     swf_ObjectPlace(i->tag,myshapeid,getNewDepth(dev),&i->page_matrix,&cxform2,NULL);
2013 }
2014
2015 static void swf_startclip(gfxdevice_t*dev, gfxline_t*line)
2016 {
2017     swfoutput_internal*i = (swfoutput_internal*)dev->internal;
2018
2019     endtext(dev);
2020     endshape(dev);
2021
2022     if(i->clippos >= 127)
2023     {
2024         msg("<warning> Too many clip levels.");
2025         i->clippos --;
2026     } 
2027
2028     int myshapeid = getNewID(dev);
2029     i->tag = swf_InsertTag(i->tag,ST_DEFINESHAPE3);
2030     RGBA col;
2031     memset(&col, 0, sizeof(RGBA));
2032     col.a = 255;
2033     SHAPE*shape;
2034     swf_ShapeNew(&shape);
2035     int fsid = swf_ShapeAddSolidFillStyle(shape,&col);
2036     if(i->mark) {
2037         RGBA markcol = {0,i->mark[0],i->mark[1],i->mark[2]};
2038         swf_ShapeAddSolidFillStyle(shape,&markcol);
2039     }
2040     swf_SetU16(i->tag,myshapeid);
2041     SRECT r = gfxline_getSWFbbox(line);
2042     swf_SetRect(i->tag,&r);
2043     swf_SetShapeStyles(i->tag,shape);
2044     swf_ShapeCountBits(shape,NULL,NULL);
2045     swf_SetShapeBits(i->tag,shape);
2046     swf_ShapeSetAll(i->tag,shape,UNDEFINED_COORD,UNDEFINED_COORD,0,fsid,0);
2047     i->swflastx = i->swflasty = UNDEFINED_COORD;
2048     drawgfxline(dev, line);
2049     swf_ShapeSetEnd(i->tag);
2050     swf_ShapeFree(shape);
2051
2052     /* TODO: remember the bbox, and check all shapes against it */
2053     
2054     msg("<trace> Placing clip ID %d", myshapeid);
2055     i->tag = swf_InsertTag(i->tag,ST_PLACEOBJECT2);
2056     i->cliptags[i->clippos] = i->tag;
2057     i->clipshapes[i->clippos] = myshapeid;
2058     i->clipdepths[i->clippos] = getNewDepth(dev);
2059     i->clippos++;
2060 }
2061
2062 static void swf_endclip(gfxdevice_t*dev)
2063 {
2064     swfoutput_internal*i = (swfoutput_internal*)dev->internal;
2065     if(i->textid>=0)
2066         endtext(dev);
2067     if(i->shapeid>=0)
2068         endshape(dev);
2069
2070     if(!i->clippos) {
2071         msg("<error> Invalid end of clipping region");
2072         return;
2073     }
2074     i->clippos--;
2075     /*swf_ObjectPlaceClip(i->cliptags[i->clippos],i->clipshapes[i->clippos],i->clipdepths[i->clippos],&i->page_matrix,NULL,NULL,
2076             / * clip to depth: * / i->depth <= i->clipdepths[i->clippos]? i->depth : i->depth - 1);
2077     i->depth ++;*/
2078     swf_ObjectPlaceClip(i->cliptags[i->clippos],i->clipshapes[i->clippos],i->clipdepths[i->clippos],&i->page_matrix,NULL,NULL,i->depth);
2079 }
2080 static int gfxline_type(gfxline_t*line)
2081 {
2082     int tmplines=0;
2083     int tmpsplines=0;
2084     int lines=0;
2085     int splines=0;
2086     int haszerosegments=0;
2087     int length=0;
2088     while(line) {
2089         if(line->type == gfx_moveTo) {
2090             tmplines=0;
2091             tmpsplines=0;
2092         } else if(line->type == gfx_lineTo) {
2093             tmplines++;
2094             if(tmplines>lines)
2095                 lines=tmplines;
2096         } else if(line->type == gfx_splineTo) {
2097             tmpsplines++;
2098             if(tmpsplines>lines)
2099                 splines=tmpsplines;
2100         }
2101         length++;
2102         line = line->next;
2103     }
2104     if(length>400)
2105         return 5;
2106     if(lines==0 && splines==0) return 0;
2107     else if(lines==1 && splines==0) return 1;
2108     else if(lines==0 && splines==1) return 2;
2109     else if(splines==0) return 3;
2110     else return 4;
2111 }
2112
2113 static int gfxline_has_dots(gfxline_t*line)
2114 {
2115     int tmplines=0;
2116     double x=0,y=0;
2117     double dist = 0;
2118     int isline = 0;
2119     int short_gap = 0;
2120     while(line) {
2121         if(line->type == gfx_moveTo) {
2122             /* test the length of the preceding line, and assume it is a dot if
2123                it's length is less than 1.0. But *only* if there's a noticable 
2124                gap between the previous line and the next moveTo. (I've come
2125                across a PDF where thousands of "dots" were stringed together,
2126                forming a line) */
2127             int last_short_gap = short_gap;
2128             if((fabs(line->x - x) + fabs(line->y - y)) < 1.0) {
2129                 short_gap = 1;
2130             } else {
2131                 short_gap = 0;
2132             }
2133             if(isline && dist < 1 && !short_gap && !last_short_gap) {
2134                 return 1;
2135             }
2136             dist = 0;
2137             isline = 0;
2138         } else if(line->type == gfx_lineTo) {
2139             dist += fabs(line->x - x) + fabs(line->y - y);
2140             isline = 1;
2141         } else if(line->type == gfx_splineTo) {
2142             dist += fabs(line->sx - x) + fabs(line->sy - y) + 
2143                     fabs(line->x - line->sx) + fabs(line->y - line->sy);
2144             isline = 1;
2145         }
2146         x = line->x;
2147         y = line->y;
2148         line = line->next;
2149     }
2150     if(isline && dist < 1 && !short_gap) {
2151         return 1;
2152     }
2153     return 0;
2154 }
2155
2156 static int gfxline_fix_short_edges(gfxline_t*line)
2157 {
2158     double x,y;
2159     while(line) {
2160         if(line->type == gfx_lineTo) {
2161             if(fabs(line->x - x) + fabs(line->y - y) < 0.01) {
2162                 line->x += 0.01;
2163             }
2164         } else if(line->type == gfx_splineTo) {
2165             if(fabs(line->sx - x) + fabs(line->sy - y) + 
2166                fabs(line->x - line->sx) + fabs(line->y - line->sy) < 0.01) {
2167                 line->x += 0.01;
2168             }
2169         }
2170         x = line->x;
2171         y = line->y;
2172         line = line->next;
2173     }
2174     return 0;
2175 }
2176
2177 static char is_inside_page(gfxdevice_t*dev, gfxcoord_t x, gfxcoord_t y)
2178 {
2179     swfoutput_internal*i = (swfoutput_internal*)dev->internal;
2180     if(x<i->min_x || x>i->max_x) return 0;
2181     if(y<i->min_y || y>i->max_y) return 0;
2182     return 1;
2183 }
2184
2185 gfxline_t* gfxline_move(gfxline_t*line, double x, double y)
2186 {
2187     gfxline_t*l = line = gfxline_clone(line);
2188
2189     while(l) {
2190         l->x += x;
2191         l->y += y;
2192         l->sx += x;
2193         l->sy += y;
2194         l = l->next;
2195     }
2196     return line;
2197 }
2198
2199 //#define NORMALIZE_POLYGON_POSITIONS
2200
2201 static void swf_stroke(gfxdevice_t*dev, gfxline_t*line, gfxcoord_t width, gfxcolor_t*color, gfx_capType cap_style, gfx_joinType joint_style, gfxcoord_t miterLimit)
2202 {
2203     swfoutput_internal*i = (swfoutput_internal*)dev->internal;
2204     if(line_is_empty(line))
2205         return;
2206     int type = gfxline_type(line);
2207     int has_dots = gfxline_has_dots(line);
2208     gfxbbox_t r = gfxline_getbbox(line);
2209     int is_outside_page = !is_inside_page(dev, r.xmin, r.ymin) || !is_inside_page(dev, r.xmax, r.ymax);
2210
2211     /* TODO: * split line into segments, and perform this check for all segments */
2212
2213     if(i->config_disable_polygon_conversion || type>=5 ||
2214        (!has_dots &&
2215         (width <= i->config_caplinewidth 
2216         || (cap_style == gfx_capRound && joint_style == gfx_joinRound)
2217         || (cap_style == gfx_capRound && type<=2)))) {} else 
2218     {
2219         /* convert line to polygon */
2220         msg("<trace> draw as polygon, type=%d dots=%d", type, has_dots);
2221         if(has_dots)
2222             gfxline_fix_short_edges(line);
2223         /* we need to convert the line into a polygon */
2224         ArtSVP* svp = gfxstrokeToSVP(line, width, cap_style, joint_style, miterLimit);
2225         gfxline_t*gfxline = SVPtogfxline(svp);
2226         dev->fill(dev, gfxline, color);
2227         free(gfxline);
2228         art_svp_free(svp);
2229         return;
2230     }
2231
2232     msg("<trace> draw as stroke, type=%d dots=%d", type, has_dots);
2233     endtext(dev);
2234
2235 #ifdef NORMALIZE_POLYGON_POSITIONS
2236     endshape(dev);
2237     double startx = 0, starty = 0;
2238     if(line && line->type == gfx_moveTo) {
2239         startx = line->x;
2240         starty = line->y;
2241     }
2242     line = gfxline_move(line, -startx, -starty);
2243     i->shapeposx = (int)(startx*20);
2244     i->shapeposy = (int)(starty*20);
2245 #endif
2246
2247     swfoutput_setstrokecolor(dev, color->r, color->g, color->b, color->a);
2248     swfoutput_setlinewidth(dev, width);
2249     startshape(dev);
2250     stopFill(dev);
2251     drawgfxline(dev, line);
2252
2253 #ifdef NORMALIZE_POLYGON_POSITIONS
2254     free(line); //account for _move
2255 #endif
2256
2257 }
2258 static void swf_fill(gfxdevice_t*dev, gfxline_t*line, gfxcolor_t*color)
2259 {
2260     swfoutput_internal*i = (swfoutput_internal*)dev->internal;
2261     if(line_is_empty(line))
2262         return;
2263     if(!color->a)
2264         return;
2265     gfxbbox_t r = gfxline_getbbox(line);
2266     int is_outside_page = !is_inside_page(dev, r.xmin, r.ymin) || !is_inside_page(dev, r.xmax, r.ymax);
2267
2268     endtext(dev);
2269     if(!i->config_ignoredraworder)
2270         endshape(dev);
2271
2272 #ifdef NORMALIZE_POLYGON_POSITIONS
2273     endshape(dev);
2274     double startx = 0, starty = 0;
2275     if(line && line->type == gfx_moveTo) {
2276         startx = line->x;
2277         starty = line->y;
2278     }
2279     line = gfxline_move(line, -startx, -starty);
2280     i->shapeposx = (int)(startx*20);
2281     i->shapeposy = (int)(starty*20);
2282 #endif
2283
2284     swfoutput_setfillcolor(dev, color->r, color->g, color->b, color->a);
2285     startshape(dev);
2286     startFill(dev);
2287     i->fill=1;
2288     drawgfxline(dev, line);
2289     msg("<trace> end of swf_fill (shapeid=%d)", i->shapeid);
2290
2291 #ifdef NORMALIZE_POLYGON_POSITIONS
2292     free(line); //account for _move
2293 #endif
2294 }
2295 static void swf_fillgradient(gfxdevice_t*dev, gfxline_t*line, gfxgradient_t*gradient, gfxgradienttype_t type, gfxmatrix_t*matrix)
2296 {
2297     if(line_is_empty(line))
2298         return;
2299     msg("<error> Gradient filling not implemented yet");
2300 }
2301
2302 static SWFFONT* gfxfont_to_swffont(gfxfont_t*font, char* id)
2303 {
2304     SWFFONT*swffont = (SWFFONT*)rfx_calloc(sizeof(SWFFONT));
2305     int t;
2306     SRECT bounds = {0,0,0,0};
2307     swffont->id = -1;
2308     swffont->version = 2;
2309     swffont->name = (U8*)strdup(id);
2310     swffont->layout = (SWFLAYOUT*)rfx_calloc(sizeof(SWFLAYOUT));
2311     swffont->layout->ascent = 0; /* ? */
2312     swffont->layout->descent = 0;
2313     swffont->layout->leading = 0;
2314     swffont->layout->bounds = (SRECT*)rfx_calloc(sizeof(SRECT)*font->num_glyphs);
2315     swffont->encoding = FONT_ENCODING_UNICODE;
2316     swffont->numchars = font->num_glyphs;
2317     swffont->maxascii = font->max_unicode;
2318     swffont->ascii2glyph = (int*)rfx_calloc(sizeof(int)*swffont->maxascii);
2319     swffont->glyph2ascii = (U16*)rfx_calloc(sizeof(U16)*swffont->numchars);
2320     swffont->glyph = (SWFGLYPH*)rfx_calloc(sizeof(SWFGLYPH)*swffont->numchars);
2321     swffont->glyphnames = (char**)rfx_calloc(sizeof(char*)*swffont->numchars);
2322     for(t=0;t<font->max_unicode;t++) {
2323         swffont->ascii2glyph[t] = font->unicode2glyph[t];
2324     }
2325     for(t=0;t<font->num_glyphs;t++) {
2326         drawer_t draw;
2327         gfxline_t*line;
2328         int advance = 0;
2329         swffont->glyph2ascii[t] = font->glyphs[t].unicode;
2330         if(font->glyphs[t].name) {
2331             swffont->glyphnames[t] = strdup(font->glyphs[t].name);
2332         } else {
2333             swffont->glyphnames[t] = 0;
2334         }
2335         advance = (int)(font->glyphs[t].advance);
2336
2337         swf_Shape01DrawerInit(&draw, 0);
2338         line = font->glyphs[t].line;
2339         while(line) {
2340             FPOINT c,to;
2341             c.x = line->sx; c.y = line->sy;
2342             to.x = line->x; to.y = line->y;
2343             if(line->type == gfx_moveTo) {
2344                 draw.moveTo(&draw, &to);
2345             } else if(line->type == gfx_lineTo) {
2346                 draw.lineTo(&draw, &to);
2347             } else if(line->type == gfx_splineTo) {
2348                 draw.splineTo(&draw, &c, &to);
2349             }
2350             line = line->next;
2351         }
2352         draw.finish(&draw);
2353         swffont->glyph[t].shape = swf_ShapeDrawerToShape(&draw);
2354         swffont->layout->bounds[t] = swf_ShapeDrawerGetBBox(&draw);
2355
2356         if(swffont->layout->bounds[t].xmax && swffont->layout->bounds[t].xmax*2 < advance) {
2357             printf("fix bad advance value: bbox=%d, advance=%d (%f)\n", swffont->layout->bounds[t].xmax, advance, font->glyphs[t].advance);
2358             advance = swffont->layout->bounds[t].xmax;
2359         }
2360             
2361         if(advance<32768) {
2362             swffont->glyph[t].advance = advance;
2363         } else {
2364             swffont->glyph[t].advance = 32767;
2365         }
2366
2367         draw.dealloc(&draw);
2368
2369         swf_ExpandRect2(&bounds, &swffont->layout->bounds[t]);
2370     }
2371     if(bounds.ymin < 0 && bounds.ymax > 0) {
2372         swffont->layout->ascent = -bounds.ymin;
2373         swffont->layout->descent = bounds.ymax;
2374         swffont->layout->leading = bounds.ymax - bounds.ymin;
2375     } else {
2376         swffont->layout->ascent = (bounds.ymax - bounds.ymin)/2;
2377         swffont->layout->descent = (bounds.ymax - bounds.ymin)/2;
2378         swffont->layout->leading = bounds.ymax - bounds.ymin;
2379     }
2380
2381     return swffont;
2382 }
2383
2384 static void swf_addfont(gfxdevice_t*dev, gfxfont_t*font)
2385 {
2386     swfoutput_internal*i = (swfoutput_internal*)dev->internal;
2387
2388     if(i->swffont && i->swffont->name && !strcmp((char*)i->swffont->name,font->id))
2389         return; // the requested font is the current font
2390     
2391     fontlist_t*last=0,*l = i->fontlist;
2392     while(l) {
2393         last = l;
2394         if(!strcmp((char*)l->swffont->name, font->id)) {
2395             return; // we already know this font
2396         }
2397         l = l->next;
2398     }
2399     l = (fontlist_t*)rfx_calloc(sizeof(fontlist_t));
2400     l->swffont = gfxfont_to_swffont(font, font->id);
2401     l->next = 0;
2402     if(last) {
2403         last->next = l;
2404     } else {
2405         i->fontlist = l;
2406     }
2407     swf_FontSetID(l->swffont, getNewID(i->dev));
2408
2409     if(getScreenLogLevel() >= LOGLEVEL_DEBUG)  {
2410         int iii;
2411         // print font information
2412         msg("<debug> Font %s",font->id);
2413         msg("<debug> |   ID: %d", l->swffont->id);
2414         msg("<debug> |   Version: %d", l->swffont->version);
2415         msg("<debug> |   Name: %s", l->swffont->name);
2416         msg("<debug> |   Numchars: %d", l->swffont->numchars);
2417         msg("<debug> |   Maxascii: %d", l->swffont->maxascii);
2418         msg("<debug> |   Style: %d", l->swffont->style);
2419         msg("<debug> |   Encoding: %d", l->swffont->encoding);
2420         for(iii=0; iii<l->swffont->numchars;iii++) {
2421             msg("<debug> |   Glyph %d) name=%s, unicode=%d size=%d bbox=(%.2f,%.2f,%.2f,%.2f)\n", iii, l->swffont->glyphnames?l->swffont->glyphnames[iii]:"<nonames>", l->swffont->glyph2ascii[iii], l->swffont->glyph[iii].shape->bitlen, 
2422                     l->swffont->layout->bounds[iii].xmin/20.0,
2423                     l->swffont->layout->bounds[iii].ymin/20.0,
2424                     l->swffont->layout->bounds[iii].xmax/20.0,
2425                     l->swffont->layout->bounds[iii].ymax/20.0
2426                     );
2427             int t;
2428             for(t=0;t<l->swffont->maxascii;t++) {
2429                 if(l->swffont->ascii2glyph[t] == iii)
2430                     msg("<debug> | - maps to %d",t);
2431             }
2432         }
2433     }
2434 }
2435
2436 static void swf_switchfont(gfxdevice_t*dev, char*fontid)
2437 {
2438     swfoutput_internal*i = (swfoutput_internal*)dev->internal;
2439
2440     if(i->swffont && i->swffont->name && !strcmp((char*)i->swffont->name,fontid))
2441         return; // the requested font is the current font
2442     
2443     fontlist_t*l = i->fontlist;
2444     while(l) {
2445         if(!strcmp((char*)l->swffont->name, fontid)) {
2446             i->swffont = l->swffont;
2447             return; //done!
2448         }
2449         l = l->next;
2450     }
2451     msg("<error> Unknown font id: %s", fontid);
2452     return;
2453 }
2454
2455 static void swf_drawchar(gfxdevice_t*dev, gfxfont_t*font, int glyph, gfxcolor_t*color, gfxmatrix_t*matrix)
2456 {
2457     swfoutput_internal*i = (swfoutput_internal*)dev->internal;
2458         
2459     if(!i->swffont || !i->swffont->name || strcmp((char*)i->swffont->name,font->id)) // not equal to current font
2460     {
2461         /* TODO: remove the need for this (enhance getcharacterbbox so that it can cope
2462                  with multiple fonts */
2463         endtext(dev);
2464
2465         swf_switchfont(dev, font->id); // set the current font
2466     }
2467     swfoutput_setfontmatrix(dev, matrix->m00, matrix->m01, matrix->m10, matrix->m11);
2468    
2469     swfmatrix_t m;
2470     m.m11 = i->fontm11;
2471     m.m12 = i->fontm12;
2472     m.m21 = i->fontm21;
2473     m.m22 = i->fontm22;
2474     m.m31 = matrix->tx;
2475     m.m32 = matrix->ty;
2476   
2477 /*    printf("%f %f\n", m.m31,  m.m32);
2478     {
2479     static int xpos = 40;
2480     static int ypos = 200;
2481     m.m31 = xpos;
2482     m.m32 = ypos;
2483     xpos += 10;
2484     }*/
2485
2486
2487     drawchar(dev, i->swffont, glyph, &m, color);
2488 }