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