moved all static variables into a structure.
[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 }
39
40 #define CHARDATAMAX 8192
41 #define CHARMIDX 0
42 #define CHARMIDY 0
43
44 typedef struct _chardata {
45     int charid;
46     int fontid; /* TODO: use a SWFFONT instead */
47     int x;
48     int y;
49     int size;
50     RGBA color;
51 } chardata_t;
52
53 struct fontlist_t 
54 {
55     SWFFONT *swffont;
56     fontlist_t*next;
57 };
58
59 static struct _config
60 {
61     int opennewwindow;
62     int ignoredraworder;
63     int drawonlyshapes; 
64     int jpegquality;
65     int storeallcharacters;
66     int enablezlib;
67     int insertstoptag;
68     int flashversion;
69     int splinemaxerror;
70     int fontsplinemaxerror;
71     int filloverlap;
72     int protect;
73     float minlinewidth;
74 } config;
75
76 typedef struct _swfoutput_internal
77 {
78     
79     fontlist_t* fontlist;
80
81     char storefont;
82     int flag_protected;
83     
84     char* filename;
85     SWF swf;
86     TAG *tag;
87     int currentswfid;
88     int depth;
89     int startdepth;
90     int linewidth;
91     SRECT lastpagesize;
92     
93     SHAPE* shape;
94     int shapeid;
95     int textid;
96     
97     int fillstyleid;
98     int linestyleid;
99     int swflastx;
100     int swflasty;
101     int lastwasfill;
102     int shapeisempty;
103     char fill;
104     int sizex;
105     int sizey;
106     TAG* cliptags[128];
107     int clipshapes[128];
108     U32 clipdepths[128];
109     int clippos;
110     
111     char fillstylechanged;
112     
113     int bboxrectpos;
114     SRECT bboxrect;
115  
116     chardata_t chardata[CHARDATAMAX];
117     int chardatapos;
118     int firstpage;
119 } swfoutput_internal;
120
121 static int global_init;
122
123 static swfoutput_internal* init_internal_struct()
124 {
125     swfoutput_internal*i = (swfoutput_internal*)malloc(sizeof(swfoutput_internal));
126     memset(i, 0, sizeof(swfoutput_internal));
127    
128     if(!global_init) {
129         config.opennewwindow=0;
130         config.ignoredraworder=0;
131         config.drawonlyshapes=0;
132         config.jpegquality=85;
133         config.storeallcharacters=0;
134         config.enablezlib=0;
135         config.insertstoptag=0;
136         config.flashversion=5;
137         config.splinemaxerror=1;
138         config.fontsplinemaxerror=1;
139         config.filloverlap=0;
140         config.minlinewidth=0.05;
141     }
142
143     i->storefont = 0;
144     i->flag_protected = 0;
145     i->filename = 0;
146     i->currentswfid = 0;
147     i->depth = 1;
148     i->startdepth = 1;
149     i->linewidth = 0;
150     i->shapeid = -1;
151     i->textid = -1;
152
153     i->fillstyleid;
154     i->linestyleid;
155     i->swflastx=0;
156     i->swflasty=0;
157     i->lastwasfill = 0;
158     i->shapeisempty = 1;
159     i->fill = 0;
160     i->clippos = 0;
161
162     i->fillstylechanged = 0;
163
164     i->bboxrectpos = -1;
165     i->chardatapos = 0;
166     i->firstpage = 1;
167
168     return i;
169 };
170
171 static void startshape(struct swfoutput* obj);
172 static void starttext(struct swfoutput* obj);
173 static void endshape(struct swfoutput* obj,int clip);
174 static void endtext(struct swfoutput* obj);
175
176 // matrix multiplication. changes p0
177 static void transform (plotxy*p0,struct swfmatrix*m)
178 {
179     double x,y;
180     x = m->m11*p0->x+m->m12*p0->y;
181     y = m->m21*p0->x+m->m22*p0->y;
182     p0->x = x + m->m13;
183     p0->y = y + m->m23;
184 }
185
186 // write a move-to command into the swf
187 static int moveto(struct swfoutput*obj, TAG*tag, plotxy p0)
188 {
189     swfoutput_internal*i = (swfoutput_internal*)obj->internal;
190     int rx = (int)(p0.x*20);
191     int ry = (int)(p0.y*20);
192     if(rx!=i->swflastx || ry!=i->swflasty || i->fillstylechanged) {
193       swf_ShapeSetMove (tag, i->shape, rx,ry);
194       i->fillstylechanged = 0;
195       i->swflastx=rx;
196       i->swflasty=ry;
197       return 1;
198     }
199     return 0;
200 }
201 static int moveto(struct swfoutput*obj, TAG*tag, float x, float y)
202 {
203     swfoutput_internal*i = (swfoutput_internal*)obj->internal;
204     plotxy p;
205     p.x = x;
206     p.y = y;
207     return moveto(obj, tag, p);
208 }
209 static void addPointToBBox(struct swfoutput*obj, int px, int py) 
210 {
211     swfoutput_internal*i = (swfoutput_internal*)obj->internal;
212
213     SPOINT p;
214     p.x = px;
215     p.y = py;
216     if(i->fill) {
217         swf_ExpandRect(&i->bboxrect, p);
218     } else {
219         swf_ExpandRect3(&i->bboxrect, p, i->linewidth*3/2);
220     }
221 }
222
223 // write a line-to command into the swf
224 static void lineto(struct swfoutput*obj, TAG*tag, plotxy p0)
225 {
226     swfoutput_internal*i = (swfoutput_internal*)obj->internal;
227     int px = (int)(p0.x*20);
228     int py = (int)(p0.y*20);
229     int rx = (px-i->swflastx);
230     int ry = (py-i->swflasty);
231     /* we can't skip this for rx=0,ry=0, those
232        are plots */
233     swf_ShapeSetLine (tag, i->shape, rx,ry);
234
235     addPointToBBox(obj, i->swflastx,i->swflasty);
236     addPointToBBox(obj, px,py);
237
238     i->shapeisempty = 0;
239     i->swflastx+=rx;
240     i->swflasty+=ry;
241 }
242 static void lineto(struct swfoutput*obj, TAG*tag, double x, double y)
243 {
244     plotxy p;
245     p.x = x;
246     p.y = y;
247     lineto(obj,tag, p);
248 }
249
250 // write a spline-to command into the swf
251 static void splineto(struct swfoutput*obj, TAG*tag, plotxy control,plotxy end)
252 {
253     swfoutput_internal*i = (swfoutput_internal*)obj->internal;
254     int lastlastx = i->swflastx;
255     int lastlasty = i->swflasty;
256
257     int cx = ((int)(control.x*20)-i->swflastx);
258     int cy = ((int)(control.y*20)-i->swflasty);
259     i->swflastx += cx;
260     i->swflasty += cy;
261     int ex = ((int)(end.x*20)-i->swflastx);
262     int ey = ((int)(end.y*20)-i->swflasty);
263     i->swflastx += ex;
264     i->swflasty += ey;
265     
266     if(cx || cy || ex || ey) {
267         swf_ShapeSetCurve(tag, i->shape, cx,cy,ex,ey);
268         addPointToBBox(obj, lastlastx   ,lastlasty   );
269         addPointToBBox(obj, lastlastx+cx,lastlasty+cy);
270         addPointToBBox(obj, lastlastx+cx+ex,lastlasty+cy+ey);
271     }
272     i->shapeisempty = 0;
273 }
274
275 /* write a line, given two points and the transformation
276    matrix. */
277 static void line(struct swfoutput*obj, TAG*tag, plotxy p0, plotxy p1, struct swfmatrix*m)
278 {
279     transform(&p0,m);
280     transform(&p1,m);
281     moveto(obj, tag, p0);
282     lineto(obj, tag, p1);
283 }
284
285 /* write a cubic (!) spline. This involves calling the approximate()
286    function out of spline.cc to convert it to a quadratic spline.  */
287 static void spline(struct swfoutput*obj, TAG*tag,plotxy p0,plotxy p1,plotxy p2,plotxy p3,struct swfmatrix*m)
288 {
289     swfoutput_internal*i = (swfoutput_internal*)obj->internal;
290     double d;
291     struct qspline q[128];
292     int num;
293     int t;
294     transform(&p0,m);
295     transform(&p1,m);
296     transform(&p2,m);
297     transform(&p3,m);
298     cspline c;
299     c.start = p3;
300     c.control1 = p2;
301     c.control2 = p1;
302     c.end = p0;
303
304     if(i->storefont) {
305         /* fonts use a different approximation than shapes */
306         num = cspline_approximate(&c, q, config.fontsplinemaxerror/20.0, APPROXIMATE_RECURSIVE_BINARY);
307         //num = cspline_approximate(&c, q, 10.0, APPROXIMATE_INFLECTION);
308     } else {
309         num = cspline_approximate(&c, q, config.splinemaxerror/20.0, APPROXIMATE_RECURSIVE_BINARY);
310     }
311     for(t=0;t<num;t++) {
312         if(!t) 
313             moveto(obj, tag,q[t].start);
314         splineto(obj, tag,q[t].control, q[t].end);
315     }
316 }
317
318 void resetdrawer(struct swfoutput*obj)
319 {
320     swfoutput_internal*i = (swfoutput_internal*)obj->internal;
321     i->swflastx = 0;
322     i->swflasty = 0;
323 }
324
325 static void stopFill(struct swfoutput*obj)
326 {
327     swfoutput_internal*i = (swfoutput_internal*)obj->internal;
328     if(i->lastwasfill)
329     {
330         swf_ShapeSetStyle(i->tag,i->shape,i->linestyleid,0x8000,0);
331         i->fillstylechanged = 1;
332         i->lastwasfill = 0;
333     }
334 }
335 static void startFill(struct swfoutput*obj)
336 {
337     swfoutput_internal*i = (swfoutput_internal*)obj->internal;
338     if(!i->lastwasfill)
339     {
340         swf_ShapeSetStyle(i->tag,i->shape,0x8000,i->fillstyleid,0);
341         i->fillstylechanged = 1;
342         i->lastwasfill = 1;
343     }
344 }
345
346 /* draw an outline. These are generated by pdf2swf and by t1lib
347    (representing characters). */
348 void drawpath(struct swfoutput*obj, SWF_OUTLINE*outline, struct swfmatrix*m, int log)
349 {
350     swfoutput_internal*i = (swfoutput_internal*)obj->internal;
351     if( i->tag->id != ST_DEFINESHAPE &&
352         i->tag->id != ST_DEFINESHAPE2 &&
353         i->tag->id != ST_DEFINESHAPE3)
354     {
355         msg("<error> internal error: drawpath needs a shape tag, not %d\n",i->tag->id);
356         exit(1);
357     }
358     double x=0,y=0;
359     double lastx=0,lasty=0;
360     double firstx=0,firsty=0;
361     int init=1;
362
363     while (outline)
364     {
365         x += (outline->dest.x/(float)0xffff);
366         y += (outline->dest.y/(float)0xffff);
367         if(outline->type == SWF_PATHTYPE_MOVE)
368         {
369             //if(!init && fill && obj->drawmode != DRAWMODE_EOFILL && !ignoredraworder) {
370             if(config.filloverlap && !init && i->fill && obj->drawmode != DRAWMODE_EOFILL) {
371                 /* drawmode=FILL (not EOFILL) means that
372                    seperate shapes do not cancel each other out.
373                    On SWF side, we need to start a new shape for each
374                    closed polygon, because SWF only knows EOFILL.
375                 */
376                 endshape(obj,0);
377                 startshape(obj);
378                 startFill(obj);
379             }
380
381             if(((int)(lastx*20) != (int)(firstx*20) ||
382                 (int)(lasty*20) != (int)(firsty*20)) &&
383                      i->fill && !init)
384             {
385                 plotxy p0;
386                 plotxy p1;
387                 p0.x=lastx;
388                 p0.y=lasty;
389                 p1.x=firstx;
390                 p1.y=firsty;
391                 if(log) printf("fix: %f,%f -> %f,%f\n",p0.x,p0.y,p1.x,p1.y);
392                 line(obj,i->tag, p0, p1, m);
393             }
394             firstx=x;
395             firsty=y;
396             init = 0;
397         }
398         else if(outline->type == SWF_PATHTYPE_LINE) 
399         {
400             plotxy p0;
401             plotxy p1;
402             p0.x=lastx;
403             p0.y=lasty;
404             p1.x=x;
405             p1.y=y;
406             if(log) printf("line: %f,%f -> %f,%f\n",p0.x,p0.y,p1.x,p1.y);
407             line(obj,i->tag, p0,p1,m);
408         }
409         else if(outline->type == SWF_PATHTYPE_BEZIER)
410         {
411             plotxy p0;
412             plotxy p1;
413             plotxy p2;
414             plotxy p3;
415             SWF_BEZIERSEGMENT*o2 = (SWF_BEZIERSEGMENT*)outline;
416             p0.x=x; 
417             p0.y=y;
418             p1.x=o2->C.x/(float)0xffff+lastx;
419             p1.y=o2->C.y/(float)0xffff+lasty;
420             p2.x=o2->B.x/(float)0xffff+lastx;
421             p2.y=o2->B.y/(float)0xffff+lasty;
422             p3.x=lastx;
423             p3.y=lasty;
424             if(log) printf("spline: %f,%f -> %f,%f\n",p3.x,p3.y,p0.x,p0.y);
425             spline(obj,i->tag,p0,p1,p2,p3,m);
426         } 
427         else {
428             msg("<error> drawpath: unknown outline type:%d\n", outline->type);
429         }
430         lastx=x;
431         lasty=y;
432         outline = outline->link;
433     }
434     if(((int)(lastx*20) != (int)(firstx*20) ||
435         (int)(lasty*20) != (int)(firsty*20)) &&
436              i->fill)
437     {
438         plotxy p0;
439         plotxy p1;
440         p0.x=lastx;
441         p0.y=lasty;
442         p1.x=firstx;
443         p1.y=firsty;
444         if(log) printf("fix: %f,%f -> %f,%f\n",p0.x,p0.y,p1.x,p1.y);
445         line(obj, i->tag, p0, p1, m);
446     }
447 }
448
449 plotxy getPivot(struct swfoutput*obj, SWF_OUTLINE*outline, int dir, double line_width, int end, int trytwo)
450 {
451     swfoutput_internal*i = (swfoutput_internal*)obj->internal;
452     SWF_PATHPOINT next, next2;
453     double xv=0,yv=0, xv2=0, yv2=0;
454     plotxy p;
455     int two = 0;
456
457     if(!end) {
458         if(outline->type == SWF_PATHTYPE_LINE) {
459             next = outline->dest;
460         } else {
461             next = ((SWF_BEZIERSEGMENT*)outline)->B;
462             if(next.x==0 && next.y==0) {
463                 next = ((SWF_BEZIERSEGMENT*)outline)->C;
464             }
465             if(next.x==0 && next.y==0) {
466                 next = ((SWF_BEZIERSEGMENT*)outline)->dest;
467             }
468         }
469         next2 = next;
470         if(trytwo && outline->last && outline->last->type != SWF_PATHTYPE_MOVE) {
471             if(outline->type == SWF_PATHTYPE_LINE) {
472                 next2 = outline->last->dest;
473             } else {
474                 SWF_PATHPOINT c = ((SWF_BEZIERSEGMENT*)(outline->last))->C;
475                 SWF_PATHPOINT b = ((SWF_BEZIERSEGMENT*)(outline->last))->B;
476                 next2.x = outline->last->dest.x - c.x;
477                 next2.y = outline->last->dest.y - c.y;
478                 if(next2.x==0 && next2.y==0) {
479                     next2.x = outline->last->dest.x - b.x;
480                     next2.y = outline->last->dest.y - b.y;
481                 }
482                 if(next2.x==0 && next2.y==0) {
483                     next2.x = outline->last->dest.x;
484                     next2.y = outline->last->dest.y;
485                 }
486             }
487             two = 1;
488         }
489     } else {
490         if(outline->type == SWF_PATHTYPE_LINE) {
491             next = outline->dest;
492         } else {
493             SWF_PATHPOINT c = ((SWF_BEZIERSEGMENT*)outline)->C;
494             SWF_PATHPOINT b = ((SWF_BEZIERSEGMENT*)outline)->B;
495             next.x = outline->dest.x - c.x;
496             next.y = outline->dest.y - c.y;
497             if(next.x==0 && next.y==0) {
498                 next.x = outline->dest.x - b.x;
499                 next.y = outline->dest.y - b.y;
500             }
501             if(next.x==0 && next.y==0) {
502                 next.x = outline->dest.x;
503                 next.y = outline->dest.y;
504             }
505         }
506         next2 = next;
507         if(trytwo && outline->link && outline->link->type != SWF_PATHTYPE_MOVE) {
508             if(outline->type == SWF_PATHTYPE_LINE) {
509                 next2 = outline->link->dest;
510             } else {
511                 next2 = ((SWF_BEZIERSEGMENT*)(outline->link))->B;
512                 if(next2.x==0 && next2.y==0) {
513                     next2 = ((SWF_BEZIERSEGMENT*)outline->link)->C;
514                 }
515                 if(next2.x==0 && next2.y==0) {
516                     next2 = ((SWF_BEZIERSEGMENT*)outline->link)->dest;
517                 }
518             }
519             two = 1;
520         }
521     }
522
523     if(dir) {
524         xv =  next.y/(float)0xffff;
525         yv = -next.x/(float)0xffff;
526     } else {
527         xv = -next.y/(float)0xffff;
528         yv =  next.x/(float)0xffff;
529     }
530
531     double r = (line_width/2)/sqrt(xv*xv+yv*yv);
532     xv*=r;
533     yv*=r;
534
535     if(two) {
536         if(dir) {
537             xv2 =  next2.y/(float)0xffff;
538             yv2 = -next2.x/(float)0xffff;
539         } else {
540             xv2 = -next2.y/(float)0xffff;
541             yv2 =  next2.x/(float)0xffff;
542         }
543
544         double r2 = (line_width/2)/sqrt(xv2*xv2+yv2*yv2);
545         xv2*=r2;
546         yv2*=r2;
547         xv = (xv+xv2)/2;
548         yv = (yv+yv2)/2;
549         double r3 = (line_width/2)/sqrt(xv*xv+yv*yv);
550         xv *= r3;
551         yv *= r3;
552     }
553
554     p.x = xv;
555     p.y = yv;
556     return p;
557 }
558
559 void drawShortPath(struct swfoutput*obj, double x, double y, struct swfmatrix* m, SWF_OUTLINE*outline)
560 {
561     swfoutput_internal*i = (swfoutput_internal*)obj->internal;
562     double lastx=x, lasty=y;
563     while (outline && outline->type != SWF_PATHTYPE_MOVE)
564     {
565         x += (outline->dest.x/(float)0xffff);
566         y += (outline->dest.y/(float)0xffff);
567
568         if(outline->type == SWF_PATHTYPE_LINE)
569         {
570             plotxy p0, p1;
571             p0.x=lastx;
572             p0.y=lasty;
573             p1.x= x; 
574             p1.y= y;
575             line(obj, i->tag, p0, p1, m);
576         }
577         else if(outline->type == SWF_PATHTYPE_BEZIER)
578         {
579             plotxy p0,p1,p2,p3;
580             SWF_BEZIERSEGMENT*o2 = (SWF_BEZIERSEGMENT*)outline;
581             p3.x=lastx;
582             p3.y=lasty;
583             p1.x=o2->C.x/(float)0xffff+lastx;
584             p1.y=o2->C.y/(float)0xffff+lasty;
585             p2.x=o2->B.x/(float)0xffff+lastx;
586             p2.y=o2->B.y/(float)0xffff+lasty;
587             p0.x=x; 
588             p0.y=y;
589             spline(obj, i->tag,p0,p1,p2,p3,m);
590         } 
591         lastx=x;
592         lasty=y;
593         outline = outline->link;
594     }
595 }
596
597 void drawShortPathWithEnds(struct swfoutput*obj, double x, double y, struct swfmatrix* m, SWF_OUTLINE*outline, int num, int line_cap, int line_join, double line_width)
598 {
599     swfoutput_internal*i = (swfoutput_internal*)obj->internal;
600     plotxy d,d2;
601     int back = 0;
602
603     if(line_cap == LINE_CAP_BUTT || line_cap == LINE_CAP_SQUARE) {
604         endshape(obj,0);
605         startshape(obj);
606         SWF_OUTLINE *last, *tmp=outline;
607         plotxy s,e,p0,p1,p2,p3,m0,m1,m2,m3;
608         double x2 = x;
609         double y2 = y;
610         double lx=x,ly=y;
611         double ee = 1.0;
612         int nr;
613         while(tmp && tmp->type != SWF_PATHTYPE_MOVE) {
614             last = tmp;
615             lx += (tmp->dest.x/(float)0xffff);
616             ly += (tmp->dest.y/(float)0xffff);
617             tmp = tmp->link;
618         }
619         s = getPivot(obj, outline, 0, line_width, 0, 0);
620         e = getPivot(obj, last, 0, line_width, 1, 0);
621
622         if(line_cap == LINE_CAP_BUTT) {
623             /* make the clipping rectangle slighly bigger
624                than the line ending, so that it get's clipped
625                propertly */
626             //ee = 1.01;
627             ee=1.0;
628         }
629
630         p0.x = x2 + s.x*ee; 
631         p0.y = y2 + s.y*ee;
632         p1.x = x2 - s.x*ee; 
633         p1.y = y2 - s.y*ee;
634         p2.x = x2 - s.y - s.x*ee; 
635         p2.y = y2 + s.x - s.y*ee;
636         p3.x = x2 - s.y + s.x*ee; 
637         p3.y = y2 + s.x + s.y*ee;
638         m0.x = lx + e.x*ee; 
639         m0.y = ly + e.y*ee;
640         m1.x = lx - e.x*ee; 
641         m1.y = ly - e.y*ee;
642         m2.x = lx + e.y - e.x*ee; 
643         m2.y = ly - e.x - e.y*ee;
644         m3.x = lx + e.y + e.x*ee; 
645         m3.y = ly - e.x + e.y*ee;
646
647         for(nr=0;nr<2;nr++) {
648             int dir=0;
649             struct plotxy q0,q1,q2,q3,q4,q5;
650
651             startFill(obj);
652             if(line_cap == LINE_CAP_BUTT) {
653                 if(dir) {
654                     /* FIXME: box should be smaller */
655                     q0.x = 0; q0.y = 0;
656                     q1.x = i->sizex; q1.y = 0;
657                     q2.x = i->sizex; q2.y = i->sizey;
658                     q3.x = 0; q3.y = i->sizey;
659                 } else {
660                     /* FIXME: box should be smaller */
661                     q0.x = i->sizex; q0.y = i->sizey;
662                     q1.x = 0; q1.y = i->sizey;
663                     q2.x = 0; q2.y = 0;
664                     q3.x = i->sizex; q3.y = 0;
665                 }
666                 q4.x = p0.x; 
667                 q4.y = p0.y;
668                 moveto(obj, i->tag, q0);
669                 lineto(obj, i->tag, q1);
670                 lineto(obj, i->tag, q2);
671                 lineto(obj, i->tag, q3);
672                 lineto(obj, i->tag, q0);
673
674                 transform(&q4,m);
675                 lineto(obj, i->tag, q4);
676             }
677
678             line(obj, i->tag, p0, p1, m);
679             line(obj, i->tag, p1, p2, m);
680             line(obj, i->tag, p2, p3, m);
681             line(obj, i->tag, p3, p0, m);
682
683             if(line_cap == LINE_CAP_BUTT) {
684                 lineto(obj, i->tag, q0);
685                 endshape(obj, i->depth+2-nr);
686                 startshape(obj);
687             }
688             p0 = m0;
689             p1 = m1;
690             p2 = m2;
691             p3 = m3;
692         }
693
694         stopFill(obj);
695     }
696
697     drawShortPath(obj,x,y,m,outline);
698
699     if(line_cap == LINE_CAP_BUTT) {
700         endshape(obj,0);
701         startshape(obj);
702     }
703 }
704
705 void drawT1toRect(struct swfoutput*obj, double x, double y, struct swfmatrix* m, SWF_OUTLINE*outline, int num, int line_cap, int line_join, double line_width)
706 {
707     swfoutput_internal*i = (swfoutput_internal*)obj->internal;
708     plotxy d1,d2,p1,p2,p3,p4;
709
710     d1.x = (outline->dest.x/(float)0xffff);
711     d1.y = (outline->dest.y/(float)0xffff);
712     d2 = getPivot(obj, outline, 0, line_width, 0, 0);
713
714     assert(line_cap != LINE_CAP_ROUND);
715     if(line_cap == LINE_CAP_SQUARE) {
716         x -= +d2.y;
717         y -= -d2.x;
718         d1.x += +2*d2.y;
719         d1.y += -2*d2.x;
720     }
721
722     p1.x = x + d2.x;
723     p1.y = y + d2.y;
724     p2.x = x + d2.x + d1.x;
725     p2.y = y + d2.y + d1.y;
726     p3.x = x - d2.x + d1.x;
727     p3.y = y - d2.y + d1.y;
728     p4.x = x - d2.x;
729     p4.y = y - d2.y;
730
731     line(obj, i->tag, p1,p2, m);
732     line(obj, i->tag, p2,p3, m);
733     line(obj, i->tag, p3,p4, m);
734     line(obj, i->tag, p4,p1, m);
735 }
736
737 void drawShortPathWithStraightEnds(struct swfoutput*obj, double x, double y, struct swfmatrix* m, SWF_OUTLINE*outline, int num, int line_cap, int line_join, double line_width)
738 {
739     swfoutput_internal*i = (swfoutput_internal*)obj->internal;
740     SWF_OUTLINE*tmp=outline;
741     double xx=x,yy=y;
742     int stop=0;
743     assert(i->shapeid>=0);
744
745     startFill(obj);
746     drawT1toRect(obj, x, y, m,outline, num, line_cap, line_join, line_width);
747
748     while(tmp->link && tmp->link->type!=SWF_PATHTYPE_MOVE) {
749         xx += (tmp->dest.x/(float)0xffff);
750         yy += (tmp->dest.y/(float)0xffff);
751         tmp = tmp->link;
752     }
753     
754     assert(tmp->type == SWF_PATHTYPE_LINE);
755     assert(outline->type == SWF_PATHTYPE_LINE);
756     
757     if(tmp!=outline) {
758    
759         if(outline->link == tmp) {
760             /* the two straight line segments (which are everything we
761                need to draw) are very likely to overlap. To avoid that
762                they cancel each other out at the end points, start a new
763                shape for the second one */
764             endshape(obj,0);startshape(obj);
765             startFill(obj);
766         }
767
768         drawT1toRect(obj, xx, yy, m, tmp, num, line_cap, line_join, line_width);
769
770         if(outline->link != tmp)
771         {
772             stopFill(obj);stop=1;
773             int save= tmp->type;
774             tmp->type = SWF_PATHTYPE_MOVE;
775             x += (outline->dest.x/(float)0xffff);
776             y += (outline->dest.y/(float)0xffff);
777             outline = outline->link;
778             drawShortPath(obj, x, y, m, outline);
779             tmp->type = save;
780         }
781     }
782     if(!stop)
783         stopFill(obj);
784 }
785
786 static int t1len(struct swfoutput*obj, SWF_OUTLINE*line)
787 {
788     swfoutput_internal*i = (swfoutput_internal*)obj->internal;
789     int num=0;
790     while(line && line->type != SWF_PATHTYPE_MOVE) {
791         num++;
792         line = line->link;
793     }
794     return num;
795 }
796
797 static float t1linelen(struct swfoutput*obj, SWF_OUTLINE*line)
798 {
799     swfoutput_internal*i = (swfoutput_internal*)obj->internal;
800     float x,y;
801     x = (line->dest.x/(float)0xffff);
802     y = (line->dest.y/(float)0xffff);
803     return sqrt(x*x+y*y);
804 }
805
806 void drawpath2poly(struct swfoutput *obj, SWF_OUTLINE*outline, struct swfmatrix*m, int log, int line_join, int line_cap, double line_width, double miter_limit)
807 {
808     swfoutput_internal*i = (swfoutput_internal*)obj->internal;
809     if( i->tag->id != ST_DEFINESHAPE &&
810         i->tag->id != ST_DEFINESHAPE2 &&
811         i->tag->id != ST_DEFINESHAPE3) {
812         msg("<error> internal error: drawpath needs a shape tag, not %d\n",i->tag->id);
813         exit(1);
814     }
815     assert(i->shapeid>=0);
816     double x=0,y=0;
817     double lastx=0,lasty=0;
818     int valid = 0;
819     int lastwasline = 0;
820     SWF_OUTLINE*tmp = outline, *last = 0;
821     tmp->last = 0;
822
823     while(1) {
824         if(tmp) {
825             x += (tmp->dest.x/(float)0xffff);
826             y += (tmp->dest.y/(float)0xffff);
827         }
828         if(!tmp || tmp->type == SWF_PATHTYPE_MOVE) {
829             if(valid && last) {
830                 if(last->type == SWF_PATHTYPE_LINE && t1linelen(obj,last)>line_width*2 &&
831                    lastwasline && line_cap != LINE_CAP_ROUND)
832                     drawShortPathWithStraightEnds(obj, lastx, lasty, m, last, valid, line_cap, line_join, line_width);
833                 else
834                     drawShortPathWithEnds(obj, lastx, lasty, m, last, valid, line_cap, line_join, line_width);
835             }
836             if(!tmp)
837                 break;
838             valid = 0;
839             last = 0;
840             lastx = x;
841             lasty = y;
842         } else {
843             if(!last)
844                 last = tmp;
845             valid++;
846         }
847
848         if(tmp && tmp->type == SWF_PATHTYPE_LINE && t1linelen(obj,tmp)>line_width*2)
849             lastwasline = 1;
850         else
851             lastwasline = 0;
852
853         if(tmp->link)
854             tmp->link->last = tmp; // make sure list is properly linked in both directions
855         tmp = tmp->link;
856     }
857 }
858
859 static inline int colorcompare(struct swfoutput*obj, RGBA*a,RGBA*b)
860 {
861
862     if(a->r!=b->r ||
863        a->g!=b->g ||
864        a->b!=b->b ||
865        a->a!=b->a) {
866         return 0;
867     }
868     return 1;
869 }
870
871 static SRECT getcharacterbbox(struct swfoutput*obj, SWFFONT*font)
872 {
873     swfoutput_internal*i = (swfoutput_internal*)obj->internal;
874     SRECT r;
875     char debug = 0;
876     memset(&r, 0, sizeof(r));
877
878     int t;
879     if(debug) printf("\n");
880     for(t=0;t<i->chardatapos;t++)
881     {
882         if(i->chardata[t].fontid != font->id) {
883             msg("<error> Internal error: fontid %d != fontid %d", i->chardata[t].fontid, font->id);
884             exit(1);
885         }
886         SRECT b = font->layout->bounds[i->chardata[t].charid];
887         b.xmin *= i->chardata[t].size;
888         b.ymin *= i->chardata[t].size;
889         b.xmax *= i->chardata[t].size;
890         b.ymax *= i->chardata[t].size;
891         b.xmin /= 1024;
892         b.ymin /= 1024;
893         b.xmax /= 1024;
894         b.ymax /= 1024;
895         b.xmin += i->chardata[t].x;
896         b.ymin += i->chardata[t].y;
897         b.xmax += i->chardata[t].x;
898         b.ymax += i->chardata[t].y;
899
900         /* until we solve the INTERNAL_SCALING problem (see below)
901            make sure the bounding box is big enough */
902         b.xmin -= 20;
903         b.ymin -= 20;
904         b.xmax += 20;
905         b.ymax += 20;
906
907         if(debug) printf("(%f,%f,%f,%f) -> (%f,%f,%f,%f) [font %d/%d, char %d]\n",
908                 font->layout->bounds[i->chardata[t].charid].xmin/20.0,
909                 font->layout->bounds[i->chardata[t].charid].ymin/20.0,
910                 font->layout->bounds[i->chardata[t].charid].xmax/20.0,
911                 font->layout->bounds[i->chardata[t].charid].ymax/20.0,
912                 b.xmin/20.0,
913                 b.ymin/20.0,
914                 b.xmax/20.0,
915                 b.ymax/20.0,
916                 i->chardata[t].fontid,
917                 font->id,
918                 i->chardata[t].charid
919                 );
920         swf_ExpandRect2(&r, &b);
921     }
922     if(debug) printf("-----> (%f,%f,%f,%f)\n",
923             r.xmin/20.0,
924             r.ymin/20.0,
925             r.xmax/20.0,
926             r.ymax/20.0);
927     return r;
928 }
929
930 static void putcharacters(struct swfoutput*obj, TAG*tag)
931 {
932     swfoutput_internal*i = (swfoutput_internal*)obj->internal;
933     int t;
934     SWFFONT font;
935     RGBA color;
936     color.r = i->chardata[0].color.r^255;
937     color.g = 0;
938     color.b = 0;
939     color.a = 0;
940     int lastfontid;
941     int lastx;
942     int lasty;
943     int lastsize;
944     int charids[128];
945     int charadvance[128];
946     int charstorepos;
947     int pass;
948     int glyphbits=1; //TODO: can this be zero?
949     int advancebits=1;
950
951     if(tag->id != ST_DEFINETEXT &&
952         tag->id != ST_DEFINETEXT2) {
953         msg("<error> internal error: putcharacters needs an text tag, not %d\n",tag->id);
954         exit(1);
955     }
956     if(!i->chardatapos) {
957         msg("<warning> putcharacters called with zero characters");
958     }
959
960     for(pass = 0; pass < 2; pass++)
961     {
962         charstorepos = 0;
963         lastfontid = -1;
964         lastx = CHARMIDX;
965         lasty = CHARMIDY;
966         lastsize = -1;
967
968         if(pass==1)
969         {
970             advancebits++; // add sign bit
971             swf_SetU8(tag, glyphbits);
972             swf_SetU8(tag, advancebits);
973         }
974
975         for(t=0;t<=i->chardatapos;t++)
976         {
977             if(lastfontid != i->chardata[t].fontid || 
978                     lastx!=i->chardata[t].x ||
979                     lasty!=i->chardata[t].y ||
980                     !colorcompare(obj,&color, &i->chardata[t].color) ||
981                     charstorepos==127 ||
982                     lastsize != i->chardata[t].size ||
983                     t == i->chardatapos)
984             {
985                 if(charstorepos && pass==0)
986                 {
987                     int s;
988                     for(s=0;s<charstorepos;s++)
989                     {
990                         while(charids[s]>=(1<<glyphbits))
991                             glyphbits++;
992                         while(charadvance[s]>=(1<<advancebits))
993                             advancebits++;
994                     }
995                 }
996                 if(charstorepos && pass==1)
997                 {
998                     tag->writeBit = 0; // Q&D
999                     swf_SetBits(tag, 0, 1); // GLYPH Record
1000                     swf_SetBits(tag, charstorepos, 7); // number of glyphs
1001                     int s;
1002                     for(s=0;s<charstorepos;s++)
1003                     {
1004                         swf_SetBits(tag, charids[s], glyphbits);
1005                         swf_SetBits(tag, charadvance[s], advancebits);
1006                     }
1007                 }
1008                 charstorepos = 0;
1009
1010                 if(pass == 1 && t<i->chardatapos)
1011                 {
1012                     RGBA*newcolor=0;
1013                     SWFFONT*newfont=0;
1014                     int newx = 0;
1015                     int newy = 0;
1016                     if(lastx != i->chardata[t].x ||
1017                        lasty != i->chardata[t].y)
1018                     {
1019                         newx = i->chardata[t].x;
1020                         newy = i->chardata[t].y;
1021                         if(newx == 0)
1022                             newx = SET_TO_ZERO;
1023                         if(newy == 0)
1024                             newy = SET_TO_ZERO;
1025                     }
1026                     if(!colorcompare(obj,&color, &i->chardata[t].color)) 
1027                     {
1028                         color = i->chardata[t].color;
1029                         newcolor = &color;
1030                     }
1031                     font.id = i->chardata[t].fontid;
1032                     if(lastfontid != i->chardata[t].fontid || lastsize != i->chardata[t].size)
1033                         newfont = &font;
1034
1035                     tag->writeBit = 0; // Q&D
1036                     swf_TextSetInfoRecord(tag, newfont, i->chardata[t].size, newcolor, newx,newy);
1037                 }
1038
1039                 lastfontid = i->chardata[t].fontid;
1040                 lastx = i->chardata[t].x;
1041                 lasty = i->chardata[t].y;
1042                 lastsize = i->chardata[t].size;
1043             }
1044
1045             if(t==i->chardatapos)
1046                     break;
1047
1048             int advance;
1049             int nextt = t==i->chardatapos-1?t:t+1;
1050             int rel = i->chardata[nextt].x-i->chardata[t].x;
1051             if(rel>=0 && (rel<(1<<(advancebits-1)) || pass==0)) {
1052                advance = rel;
1053                lastx=i->chardata[nextt].x;
1054             }
1055             else {
1056                advance = 0;
1057                lastx=i->chardata[t].x;
1058             }
1059             charids[charstorepos] = i->chardata[t].charid;
1060             charadvance[charstorepos] = advance;
1061             charstorepos ++;
1062         }
1063     }
1064     i->chardatapos = 0;
1065 }
1066
1067 static void putcharacter(struct swfoutput*obj, int fontid, int charid, 
1068                     int x,int y, int size)
1069 {
1070     swfoutput_internal*i = (swfoutput_internal*)obj->internal;
1071     if(i->chardatapos == CHARDATAMAX)
1072     {
1073         msg("<warning> Character buffer too small. SWF will be slightly bigger");
1074         endtext(obj);
1075         starttext(obj);
1076     }
1077     i->chardata[i->chardatapos].fontid = fontid;
1078     i->chardata[i->chardatapos].charid = charid;
1079     i->chardata[i->chardatapos].x = x;
1080     i->chardata[i->chardatapos].y = y;
1081     i->chardata[i->chardatapos].color = obj->fillrgb;
1082     i->chardata[i->chardatapos].size = size;
1083     i->chardatapos++;
1084 }
1085
1086 /* Notice: we can only put chars in the range -1639,1638 (-32768/20,32768/20).
1087    So if we set this value to high, the char coordinates will overflow.
1088    If we set it to low, however, the char positions will be inaccurate */
1089 #define FONT_INTERNAL_SIZE 4
1090
1091 /* process a character. */
1092 static int drawchar(struct swfoutput*obj, SWFFONT *swffont, char*character, int charnr, int u, swfmatrix*m)
1093 {
1094     swfoutput_internal*i = (swfoutput_internal*)obj->internal;
1095     if(!swffont) {
1096         msg("<warning> Font is NULL");
1097         return 0;
1098     }
1099
1100     int charid = getCharID(swffont, charnr, character, u); 
1101     
1102     if(charid<0) {
1103         msg("<warning> Didn't find character '%s' (c=%d,u=%d) in current charset (%s, %d characters)", 
1104                 FIXNULL(character),charnr, u, FIXNULL((char*)swffont->name), swffont->numchars);
1105         return 0;
1106     }
1107
1108     if(i->shapeid>=0)
1109         endshape(obj,0);
1110     if(i->textid<0)
1111         starttext(obj);
1112
1113     float x = m->m13;
1114     float y = m->m23;
1115     float det = ((m->m11*m->m22)-(m->m21*m->m12));
1116     if(fabs(det) < 0.0005) { 
1117         /* x direction equals y direction- the text is invisible */
1118         return 1;
1119     }
1120     det = 20*FONT_INTERNAL_SIZE / det;
1121
1122     SPOINT p;
1123     p.x = (SCOORD)((  x * m->m22 - y * m->m12)*det);
1124     p.y = (SCOORD)((- x * m->m21 + y * m->m11)*det);
1125
1126     putcharacter(obj, swffont->id, charid,p.x,p.y,FONT_INTERNAL_SIZE);
1127     swf_FontUseGlyph(swffont, charid);
1128     return 1;
1129
1130     /*else
1131     {
1132         SWF_OUTLINE*outline = font->getOutline(character, charnr);
1133         char* charname = character;
1134
1135         if(!outline) {
1136          msg("<warning> Didn't find character '%s' (%d) in current charset (%s)", 
1137                  FIXNULL(character),charnr,FIXNULL(font->getName()));
1138          return;
1139         }
1140         
1141         swfmatrix m2=*m;    
1142         m2.m11/=100;
1143         m2.m21/=100;
1144         m2.m12/=100;
1145         m2.m22/=100;
1146
1147         if(textid>=0)
1148             endtext(obj);
1149         if(shapeid<0)
1150             startshape(obj);
1151
1152         startFill(obj);
1153
1154         int lf = fill;
1155         fill = 1;
1156         drawpath(tag, outline, &m2, 0);
1157         fill = lf;
1158     }*/
1159 }
1160
1161 static void endtext(swfoutput*obj)
1162 {
1163     swfoutput_internal*i = (swfoutput_internal*)obj->internal;
1164     if(i->textid<0)
1165         return;
1166
1167     i->tag = swf_InsertTag(i->tag,ST_DEFINETEXT);
1168     swf_SetU16(i->tag, i->textid);
1169
1170     SRECT r;
1171     r = getcharacterbbox(obj, obj->swffont);
1172     
1173     swf_SetRect(i->tag,&r);
1174
1175     MATRIX m;
1176     swf_GetMatrix(0, &m);
1177     swf_SetMatrix(i->tag,&m);
1178
1179     putcharacters(obj, i->tag);
1180     swf_SetU8(i->tag,0);
1181     i->tag = swf_InsertTag(i->tag,ST_PLACEOBJECT2);
1182     swf_ObjectPlace(i->tag,i->textid,/*depth*/i->depth++,&obj->fontmatrix,NULL,NULL);
1183     i->textid = -1;
1184 }
1185
1186
1187 /* draw a curved polygon. */
1188 void swfoutput_drawpath(swfoutput*obj, SWF_OUTLINE*outline, 
1189                             struct swfmatrix*m)
1190 {
1191     swfoutput_internal*i = (swfoutput_internal*)obj->internal;
1192     if(i->textid>=0)
1193         endtext(obj);
1194
1195     /* Multiple polygons in one shape don't overlap correctly, 
1196        so we better start a new shape here if the polygon is filled
1197      */
1198     if(i->shapeid>=0 && i->fill && !config.ignoredraworder) {
1199         endshape(obj,0);
1200     }
1201
1202     if(i->shapeid<0)
1203         startshape(obj);
1204
1205     if(!i->fill)
1206         stopFill(obj);
1207     else
1208         startFill(obj);
1209
1210     drawpath(obj, outline,m, 0); 
1211 }
1212
1213 void swfoutput_drawpath2poly(struct swfoutput*obj, SWF_OUTLINE*outline, struct swfmatrix*m, int line_join, int line_cap, double line_width, double miter_limit)
1214 {
1215     swfoutput_internal*i = (swfoutput_internal*)obj->internal;
1216     if(i->textid>=0)
1217         endtext(obj);
1218     if(i->shapeid>=0)
1219         endshape(obj,0);
1220     assert(i->shapeid<0);
1221     startshape(obj);
1222     stopFill(obj);
1223
1224     drawpath2poly(obj, outline, m, 0, line_join, line_cap, line_width, miter_limit); 
1225 }
1226
1227 int getCharID(SWFFONT *font, int charnr, char *charname, int u)
1228 {
1229     int t;
1230     if(charname && font->glyphnames) {
1231         for(t=0;t<font->numchars;t++) {
1232             if(font->glyphnames[t] && !strcmp(font->glyphnames[t],charname)) {
1233                 msg("<debug> Char [%d,>%s<,%d] maps to %d\n", charnr, charname, u, t);
1234                 return t;
1235             }
1236         }
1237         /* if we didn't find the character, maybe
1238            we can find the capitalized version */
1239         for(t=0;t<font->numchars;t++) {
1240             if(font->glyphnames[t] && !strcasecmp(font->glyphnames[t],charname)) {
1241                 msg("<debug> Char [%d,>>%s<<,%d] maps to %d\n", charnr, charname, u, t);
1242                 return t;
1243             }
1244         }
1245     }
1246
1247     if(u>0) {
1248         /* try to use the unicode id */
1249         if(u>=0 && u<font->maxascii && font->ascii2glyph[u]>=0) {
1250             msg("<debug> Char [%d,%s,>%d<] maps to %d\n", charnr, charname, u, font->ascii2glyph[u]);
1251             return font->ascii2glyph[u];
1252         }
1253     }
1254
1255     if(font->encoding != FONT_ENCODING_UNICODE) {
1256         /* the following only works if the font encoding
1257            is US-ASCII based. It's needed for fonts which return broken unicode
1258            indices */
1259         if(charnr>=0 && charnr<font->maxascii && font->ascii2glyph[charnr]>=0) {
1260             msg("<debug> Char [>%d<,%s,%d] maps to %d\n", charnr, charname, u, font->ascii2glyph[charnr]);
1261             return font->ascii2glyph[charnr];
1262         }
1263     } 
1264     
1265     if(charnr>=0 && charnr<font->numchars) {
1266         msg("<debug> Char [>%d<,%s,%d] maps to %d\n", charnr, charname, u, charnr);
1267         return charnr;
1268     }
1269     
1270     return -1;
1271 }
1272
1273
1274 /* set's the t1 font index of the font to use for swfoutput_drawchar(). */
1275 void swfoutput_setfont(struct swfoutput*obj, char*fontid, char*filename)
1276 {
1277     swfoutput_internal*i = (swfoutput_internal*)obj->internal;
1278     fontlist_t*last=0,*iterator;
1279     if(!fontid) {
1280         msg("<error> No fontid");
1281         return;
1282     }
1283
1284     if(obj->swffont && obj->swffont->name && !strcmp((char*)obj->swffont->name,fontid))
1285         return;
1286
1287     /* TODO: remove the need for this (enhance getcharacterbbox so that it can cope
1288              with multiple fonts */
1289     endtext(obj);
1290
1291     iterator = i->fontlist;
1292     while(iterator) {
1293         if(!strcmp((char*)iterator->swffont->name,fontid)) {
1294             obj->swffont = iterator->swffont; 
1295             return;
1296         }
1297         last = iterator;
1298         iterator = iterator->next;
1299     }
1300
1301     if(!filename) {
1302         msg("<error> No filename given for font- internal error?");
1303         return;
1304     }
1305
1306     swf_SetLoadFontParameters(64,/*skip unused*/0,/*full unicode*/1);
1307     SWFFONT*swffont = swf_LoadFont(filename);
1308
1309     if(swffont == 0) {
1310         msg("<warning> Couldn't load font %s (%s)", fontid, filename);
1311         swffont = swf_LoadFont(0);
1312     }
1313
1314     swf_FontSetID(swffont, ++i->currentswfid);
1315     
1316     if(screenloglevel >= LOGLEVEL_DEBUG)  {
1317         // print font information
1318         msg("<debug> Font %s (%s)",swffont->name, filename);
1319         msg("<debug> |   ID: %d", swffont->id);
1320         msg("<debug> |   Version: %d", swffont->version);
1321         msg("<debug> |   Name: %s", fontid);
1322         msg("<debug> |   Numchars: %d", swffont->numchars);
1323         msg("<debug> |   Maxascii: %d", swffont->maxascii);
1324         msg("<debug> |   Style: %d", swffont->style);
1325         msg("<debug> |   Encoding: %d", swffont->encoding);
1326         for(int iii=0; iii<swffont->numchars;iii++) {
1327             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, 
1328                     swffont->layout->bounds[iii].xmin/20.0,
1329                     swffont->layout->bounds[iii].ymin/20.0,
1330                     swffont->layout->bounds[iii].xmax/20.0,
1331                     swffont->layout->bounds[iii].ymax/20.0
1332                     );
1333             int t;
1334             for(t=0;t<swffont->maxascii;t++) {
1335                 if(swffont->ascii2glyph[t] == iii)
1336                     msg("<debug> | - maps to %d",t);
1337             }
1338         }
1339     }
1340
1341     /* set the font name to the ID we use here */
1342     if(swffont->name) free(swffont->name);
1343     swffont->name = (U8*)strdup(fontid);
1344
1345     iterator = new fontlist_t;
1346     iterator->swffont = swffont;
1347     iterator->next = 0;
1348
1349     if(last) 
1350         last->next = iterator;
1351     else 
1352         i->fontlist = iterator;
1353
1354     obj->swffont = swffont; 
1355 }
1356
1357 int swfoutput_queryfont(struct swfoutput*obj, char*fontid)
1358 {
1359     swfoutput_internal*i = (swfoutput_internal*)obj->internal;
1360     fontlist_t *iterator = i->fontlist;
1361     while(iterator) {
1362         if(!strcmp((char*)iterator->swffont->name,fontid))
1363             return 1;
1364         iterator = iterator->next;
1365     }
1366     return 0;
1367 }
1368
1369 /* set's the matrix which is to be applied to characters drawn by
1370    swfoutput_drawchar() */
1371 void swfoutput_setfontmatrix(struct swfoutput*obj,double m11,double m12,
1372                                                   double m21,double m22)
1373 {
1374     swfoutput_internal*i = (swfoutput_internal*)obj->internal;
1375     if(obj->fontm11 == m11 &&
1376        obj->fontm12 == m12 &&
1377        obj->fontm21 == m21 &&
1378        obj->fontm22 == m22)
1379         return;
1380    if(i->textid>=0)
1381         endtext(obj);
1382     obj->fontm11 = m11;
1383     obj->fontm12 = m12;
1384     obj->fontm21 = m21;
1385     obj->fontm22 = m22;
1386     
1387     MATRIX m;
1388     m.sx = (U32)(((obj->fontm11)*65536)/FONT_INTERNAL_SIZE); m.r1 = (U32)(((obj->fontm12)*65536)/FONT_INTERNAL_SIZE);
1389     m.r0 = (U32)(((obj->fontm21)*65536)/FONT_INTERNAL_SIZE); m.sy = (U32)(((obj->fontm22)*65536)/FONT_INTERNAL_SIZE); 
1390     m.tx = 0;
1391     m.ty = 0;
1392     obj->fontmatrix = m;
1393 }
1394
1395 /* draws a character at x,y. */
1396 int swfoutput_drawchar(struct swfoutput* obj,double x,double y,char*character, int charnr, int u) 
1397 {
1398     swfoutput_internal*i = (swfoutput_internal*)obj->internal;
1399     swfmatrix m;
1400     m.m11 = obj->fontm11;
1401     m.m12 = obj->fontm12;
1402     m.m21 = obj->fontm21;
1403     m.m22 = obj->fontm22;
1404     m.m13 = x;
1405     m.m23 = y;
1406     return drawchar(obj, obj->swffont, character, charnr, u, &m);
1407 }
1408
1409 static void endpage(struct swfoutput*obj)
1410 {
1411     swfoutput_internal*i = (swfoutput_internal*)obj->internal;
1412     if(i->shapeid>=0)
1413       endshape(obj,0);
1414     if(i->textid>=0)
1415       endtext(obj);
1416     while(i->clippos)
1417         swfoutput_endclip(obj);
1418
1419     if(config.insertstoptag) {
1420         ActionTAG*atag=0;
1421         atag = action_Stop(atag);
1422         atag = action_End(atag);
1423         i->tag = swf_InsertTag(i->tag,ST_DOACTION);
1424         swf_ActionSet(i->tag,atag);
1425     }
1426     i->tag = swf_InsertTag(i->tag,ST_SHOWFRAME);
1427 }
1428
1429 void swfoutput_newpage(struct swfoutput*obj, int pageNum, int x1, int y1, int x2, int y2)
1430 {
1431     swfoutput_internal*i = (swfoutput_internal*)obj->internal;
1432     if(!i->firstpage)
1433         endpage(obj);
1434
1435     for(i->depth--;i->depth>=i->startdepth;i->depth--) {
1436         i->tag = swf_InsertTag(i->tag,ST_REMOVEOBJECT2);
1437         swf_SetU16(i->tag,i->depth);
1438     }
1439     i->depth = i->startdepth = 3; /* leave room for clip and background rectangle */
1440
1441     i->sizex = x2;
1442     i->sizey = y2;
1443     x1*=20;y1*=20;x2*=20;y2*=20;
1444
1445     if(i->lastpagesize.xmin != x1 ||
1446        i->lastpagesize.xmax != x2 ||
1447        i->lastpagesize.ymin != y1 ||
1448        i->lastpagesize.ymax != y2)
1449     {/* add white clipping rectangle */
1450         msg("<notice> processing page %d (%dx%d)", pageNum,i->sizex,i->sizey);
1451
1452         if(!i->firstpage) {
1453             msg("<notice> Page has a different size than previous ones");
1454             i->tag = swf_InsertTag(i->tag,ST_REMOVEOBJECT2);
1455             swf_SetU16(i->tag,1);
1456             i->tag = swf_InsertTag(i->tag,ST_REMOVEOBJECT2);
1457             swf_SetU16(i->tag,2);
1458         }
1459
1460         RGBA rgb;
1461         rgb.a = rgb.r = rgb.g = rgb.b = 0xff;
1462         SRECT r;
1463         SHAPE* s;
1464         int ls1=0,fs1=0;
1465         int shapeid = ++i->currentswfid;
1466         r.xmin = x1;
1467         r.ymin = y1;
1468         r.xmax = x2;
1469         r.ymax = y2;
1470         i->tag = swf_InsertTag(i->tag, ST_DEFINESHAPE);
1471         swf_ShapeNew(&s);
1472         fs1 = swf_ShapeAddSolidFillStyle(s, &rgb);
1473         swf_SetU16(i->tag,shapeid);
1474         swf_SetRect(i->tag,&r);
1475         swf_SetShapeHeader(i->tag,s);
1476         swf_ShapeSetAll(i->tag,s,x1,y1,ls1,fs1,0);
1477         swf_ShapeSetLine(i->tag,s,(x2-x1),0);
1478         swf_ShapeSetLine(i->tag,s,0,(y2-y1));
1479         swf_ShapeSetLine(i->tag,s,(x1-x2),0);
1480         swf_ShapeSetLine(i->tag,s,0,(y1-y2));
1481         swf_ShapeSetEnd(i->tag);
1482         swf_ShapeFree(s);
1483         i->tag = swf_InsertTag(i->tag, ST_PLACEOBJECT2);
1484         swf_ObjectPlace(i->tag,shapeid,/*depth*/1,0,0,0);
1485         i->tag = swf_InsertTag(i->tag, ST_PLACEOBJECT2);
1486         swf_ObjectPlaceClip(i->tag,shapeid,/*depth*/2,0,0,0,65535);
1487     } else {
1488         msg("<notice> processing page %d", pageNum);
1489     }
1490
1491     i->lastpagesize.xmin = x1;
1492     i->lastpagesize.xmax = x2;
1493     i->lastpagesize.ymin = y1;
1494     i->lastpagesize.ymax = y2;
1495     swf_ExpandRect2(&i->swf.movieSize, &i->lastpagesize);
1496
1497     i->firstpage = 0;
1498 }
1499
1500 /* initialize the swf writer */
1501 void swfoutput_init(struct swfoutput* obj, char*_filename)
1502 {
1503     memset(obj, 0, sizeof(struct swfoutput));
1504     obj->internal = init_internal_struct();
1505
1506     swfoutput_internal*i = (swfoutput_internal*)obj->internal;
1507
1508     SRECT r;
1509     RGBA rgb;
1510     i->filename = _filename;
1511
1512     msg("<verbose> initializing swf output for size %d*%d\n", i->sizex,i->sizey);
1513
1514     obj->swffont = 0;
1515     obj->drawmode = -1;
1516     
1517     memset(&i->swf,0x00,sizeof(SWF));
1518     memset(&i->lastpagesize,0x00,sizeof(SRECT));
1519
1520     i->swf.fileVersion    = config.flashversion;
1521     i->swf.frameRate      = 0x0040; // 1 frame per 4 seconds
1522     i->swf.movieSize.xmin = 0;
1523     i->swf.movieSize.ymin = 0;
1524     i->swf.movieSize.xmax = 0;
1525     i->swf.movieSize.ymax = 0;
1526     
1527     i->swf.firstTag = swf_InsertTag(NULL,ST_SETBACKGROUNDCOLOR);
1528     i->tag = i->swf.firstTag;
1529     rgb.a = rgb.r = rgb.g = rgb.b = 0xff;
1530     swf_SetRGB(i->tag,&rgb);
1531
1532     i->startdepth = i->depth = 0;
1533 }
1534
1535 void swfoutput_setprotected(struct swfoutput*obj) //write PROTECT tag
1536 {
1537     swfoutput_internal*i = (swfoutput_internal*)obj->internal;
1538     if(!i->flag_protected)
1539       i->tag = swf_InsertTag(i->tag, ST_PROTECT);
1540     i->flag_protected = 1;
1541 }
1542
1543 static void startshape(struct swfoutput*obj)
1544 {
1545     swfoutput_internal*i = (swfoutput_internal*)obj->internal;
1546     RGBA rgb;
1547     SRECT r;
1548
1549     if(i->textid>=0)
1550         endtext(obj);
1551
1552     i->tag = swf_InsertTag(i->tag,ST_DEFINESHAPE);
1553
1554     swf_ShapeNew(&i->shape);
1555     i->linestyleid = swf_ShapeAddLineStyle(i->shape,i->linewidth,&obj->strokergb);
1556     rgb.r = obj->fillrgb.r;
1557     rgb.g = obj->fillrgb.g;
1558     rgb.b = obj->fillrgb.b;
1559     i->fillstyleid = swf_ShapeAddSolidFillStyle(i->shape,&obj->fillrgb);
1560
1561     i->shapeid = ++i->currentswfid;
1562     swf_SetU16(i->tag,i->shapeid);  // ID
1563
1564     i->bboxrectpos = i->tag->len;
1565     r.xmin = 0;
1566     r.ymin = 0;
1567     r.xmax = 20*i->sizex;
1568     r.ymax = 20*i->sizey;
1569     swf_SetRect(i->tag,&r);
1570    
1571     memset(&i->bboxrect, 0, sizeof(i->bboxrect));
1572
1573     swf_SetShapeStyles(i->tag,i->shape);
1574     swf_ShapeCountBits(i->shape,NULL,NULL);
1575     swf_SetShapeBits(i->tag,i->shape);
1576
1577     /* TODO: do we really need this? */
1578     swf_ShapeSetAll(i->tag,i->shape,/*x*/0,/*y*/0,i->linestyleid,0,0);
1579     i->swflastx=i->swflasty=0;
1580     i->lastwasfill = 0;
1581     i->shapeisempty = 1;
1582 }
1583
1584 static void starttext(struct swfoutput*obj)
1585 {
1586     swfoutput_internal*i = (swfoutput_internal*)obj->internal;
1587     if(i->shapeid>=0)
1588         endshape(obj,0);
1589       
1590     i->textid = ++i->currentswfid;
1591
1592     i->swflastx=i->swflasty=0;
1593 }
1594             
1595
1596 /* TODO: move to ../lib/rfxswf */
1597 void changeRect(struct swfoutput*obj, TAG*tag, int pos, SRECT*newrect)
1598 {
1599     swfoutput_internal*i = (swfoutput_internal*)obj->internal;
1600     /* determine length of old rect */
1601     tag->pos = pos;
1602     tag->readBit = 0;
1603     SRECT old;
1604     swf_GetRect(tag, &old);
1605     swf_ResetReadBits(tag);
1606     int pos_end = tag->pos;
1607
1608     int len = tag->len - pos_end;
1609     U8*data = (U8*)malloc(len);
1610     memcpy(data, &tag->data[pos_end], len);
1611     tag->writeBit = 0;
1612     tag->len = pos;
1613     swf_SetRect(tag, newrect);
1614     swf_SetBlock(tag, data, len);
1615     free(data);
1616     tag->pos = tag->readBit = 0;
1617 }
1618
1619 void cancelshape(swfoutput*obj)
1620 {
1621     swfoutput_internal*i = (swfoutput_internal*)obj->internal;
1622     /* delete old shape tag */
1623     TAG*todel = i->tag;
1624     i->tag = i->tag->prev;
1625     swf_DeleteTag(todel);
1626     i->shapeid = -1;
1627     i->bboxrectpos = -1;
1628 }
1629
1630 void fixAreas(swfoutput*obj)
1631 {
1632     swfoutput_internal*i = (swfoutput_internal*)obj->internal;
1633     if(!i->shapeisempty && i->fill &&
1634        (i->bboxrect.xmin == i->bboxrect.xmax ||
1635         i->bboxrect.ymin == i->bboxrect.ymax) &&
1636         config.minlinewidth >= 0.001
1637        ) {
1638         msg("<debug> Shape has size 0: width=%.2f height=%.2f",
1639                 (i->bboxrect.xmax-i->bboxrect.xmin)/20.0,
1640                 (i->bboxrect.ymax-i->bboxrect.ymin)/20.0
1641                 );
1642     
1643         SRECT r = i->bboxrect;
1644         
1645         if(r.xmin == r.xmax && r.ymin == r.ymax) {
1646             /* this thing comes down to a single dot- nothing to fix here */
1647             return;
1648         }
1649
1650         cancelshape(obj);
1651
1652         RGBA save_col = obj->strokergb;
1653         int  save_width = i->linewidth;
1654
1655         obj->strokergb = obj->fillrgb;
1656         i->linewidth = (int)(config.minlinewidth*20);
1657         if(i->linewidth==0) i->linewidth = 1;
1658         
1659         startshape(obj);
1660
1661         moveto(obj, i->tag, r.xmin/20.0,r.ymin/20.0);
1662         lineto(obj, i->tag, r.xmax/20.0,r.ymax/20.0);
1663
1664         obj->strokergb = save_col;
1665         i->linewidth = save_width;
1666     }
1667     
1668 }
1669
1670 static void endshape(swfoutput*obj, int clipdepth)
1671 {
1672     swfoutput_internal*i = (swfoutput_internal*)obj->internal;
1673     if(i->shapeid<0) 
1674         return;
1675
1676     if(!clipdepth)
1677         fixAreas(obj);
1678         
1679     if(i->shapeisempty ||
1680        (i->bboxrect.xmin == i->bboxrect.xmax && 
1681         i->bboxrect.ymin == i->bboxrect.ymax)) 
1682     {
1683         // delete the shape again, we didn't do anything
1684         cancelshape(obj);
1685         return;
1686     }
1687     
1688     swf_ShapeSetEnd(i->tag);
1689
1690     changeRect(obj, i->tag, i->bboxrectpos, &i->bboxrect);
1691
1692     i->tag = swf_InsertTag(i->tag,ST_PLACEOBJECT2);
1693     if(clipdepth)
1694         swf_ObjectPlaceClip(i->tag,i->shapeid,i->depth++,NULL,NULL,NULL,clipdepth);
1695     else
1696         swf_ObjectPlace(i->tag,i->shapeid,/*depth*/i->depth++,NULL,NULL,NULL);
1697
1698     i->shapeid = -1;
1699     i->bboxrectpos = -1;
1700 }
1701
1702 void swfoutput_save(struct swfoutput* obj) 
1703 {
1704     swfoutput_internal*i = (swfoutput_internal*)obj->internal;
1705     endpage(obj);
1706     fontlist_t *tmp,*iterator = i->fontlist;
1707     while(iterator) {
1708         TAG*mtag = i->swf.firstTag;
1709         if(iterator->swffont) {
1710             mtag = swf_InsertTag(mtag, ST_DEFINEFONT2);
1711             /*if(!storeallcharacters)
1712                 swf_FontReduce(iterator->swffont);*/
1713             swf_FontSetDefine2(mtag, iterator->swffont);
1714         }
1715
1716         iterator = iterator->next;
1717     }
1718     int fi;
1719
1720     if(!i->filename) 
1721         return;
1722     if(i->filename)
1723      fi = open(i->filename, O_BINARY|O_CREAT|O_TRUNC|O_WRONLY, 0777);
1724     else
1725      fi = 1; // stdout
1726     
1727     if(fi<=0) {
1728      msg("<fatal> Could not create \"%s\". ", FIXNULL(i->filename));
1729      exit(1);
1730     }
1731  
1732     i->tag = swf_InsertTag(i->tag,ST_END);
1733
1734     if(config.enablezlib || config.flashversion>=6) {
1735       if FAILED(swf_WriteSWC(fi,&i->swf)) 
1736        msg("<error> WriteSWC() failed.\n");
1737     } else {
1738       if FAILED(swf_WriteSWF(fi,&i->swf)) 
1739        msg("<error> WriteSWF() failed.\n");
1740     }
1741
1742     if(i->filename)
1743      close(fi);
1744     msg("<notice> SWF written\n");
1745 }
1746
1747 /* Perform cleaning up, complete the swf, and write it out. */
1748 void swfoutput_destroy(struct swfoutput* obj) 
1749 {
1750     swfoutput_internal*i = (swfoutput_internal*)obj->internal;
1751
1752     swfoutput_save(obj);
1753
1754     fontlist_t *tmp,*iterator = i->fontlist;
1755     while(iterator) {
1756         if(iterator->swffont) {
1757             swf_FontFree(iterator->swffont);
1758         }
1759         tmp = iterator;
1760         iterator = iterator->next;
1761         delete tmp;
1762     }
1763     free(i);
1764     memset(obj, 0, sizeof(swfoutput));
1765 }
1766
1767 void swfoutput_setdrawmode(swfoutput* obj, int mode)
1768 {
1769     swfoutput_internal*i = (swfoutput_internal*)obj->internal;
1770     obj->drawmode = mode;
1771     if(mode == DRAWMODE_FILL)
1772      i->fill = 1;
1773     else if(mode == DRAWMODE_EOFILL)
1774      i->fill = 1;
1775     else if(mode == DRAWMODE_STROKE)
1776      i->fill = 0;
1777     else if(mode == DRAWMODE_CLIP)
1778      i->fill = 1;
1779     else if(mode == DRAWMODE_EOCLIP)
1780      i->fill = 1;
1781 }
1782
1783 void swfoutput_setfillcolor(swfoutput* obj, U8 r, U8 g, U8 b, U8 a)
1784 {
1785     swfoutput_internal*i = (swfoutput_internal*)obj->internal;
1786     if(obj->fillrgb.r == r &&
1787        obj->fillrgb.g == g &&
1788        obj->fillrgb.b == b &&
1789        obj->fillrgb.a == a) return;
1790     if(i->shapeid>=0)
1791      endshape(obj,0);
1792
1793     obj->fillrgb.r = r;
1794     obj->fillrgb.g = g;
1795     obj->fillrgb.b = b;
1796     obj->fillrgb.a = a;
1797 }
1798
1799 void swfoutput_setstrokecolor(swfoutput* obj, U8 r, U8 g, U8 b, U8 a)
1800 {
1801     swfoutput_internal*i = (swfoutput_internal*)obj->internal;
1802     if(obj->strokergb.r == r &&
1803        obj->strokergb.g == g &&
1804        obj->strokergb.b == b &&
1805        obj->strokergb.a == a) return;
1806
1807     if(i->shapeid>=0)
1808      endshape(obj,0);
1809     obj->strokergb.r = r;
1810     obj->strokergb.g = g;
1811     obj->strokergb.b = b;
1812     obj->strokergb.a = a;
1813 }
1814
1815 void swfoutput_setlinewidth(struct swfoutput*obj, double _linewidth)
1816 {
1817     swfoutput_internal*i = (swfoutput_internal*)obj->internal;
1818     if(i->linewidth == (U16)(_linewidth*20))
1819         return;
1820
1821     if(i->shapeid>=0)
1822         endshape(obj,0);
1823     i->linewidth = (U16)(_linewidth*20);
1824 }
1825
1826
1827 void swfoutput_startclip(swfoutput*obj, SWF_OUTLINE*outline, struct swfmatrix*m)
1828 {
1829     swfoutput_internal*i = (swfoutput_internal*)obj->internal;
1830     if(i->textid>=0)
1831      endtext(obj);
1832     if(i->shapeid>=0)
1833      endshape(obj,0);
1834
1835     if(i->clippos >= 127)
1836     {
1837         msg("<warning> Too many clip levels.");
1838         i->clippos --;
1839     } 
1840     
1841     startshape(obj);
1842     int olddrawmode = obj->drawmode;
1843     swfoutput_setdrawmode(obj, DRAWMODE_CLIP);
1844     swfoutput_drawpath(obj, outline, m);
1845     swf_ShapeSetEnd(i->tag);
1846     swfoutput_setdrawmode(obj, olddrawmode);
1847
1848     i->tag = swf_InsertTag(i->tag,ST_PLACEOBJECT2);
1849     i->cliptags[i->clippos] = i->tag;
1850     i->clipshapes[i->clippos] = i->shapeid;
1851     i->clipdepths[i->clippos] = i->depth++;
1852     i->clippos++;
1853     i->shapeid = -1;
1854 }
1855
1856 void swfoutput_endclip(swfoutput*obj)
1857 {
1858     swfoutput_internal*i = (swfoutput_internal*)obj->internal;
1859     if(i->textid>=0)
1860         endtext(obj);
1861     if(i->shapeid>=0)
1862         endshape(obj,0);
1863
1864     if(!i->clippos) {
1865         msg("<error> Invalid end of clipping region");
1866         return;
1867     }
1868     i->clippos--;
1869     swf_ObjectPlaceClip(i->cliptags[i->clippos],i->clipshapes[i->clippos],i->clipdepths[i->clippos],NULL,NULL,NULL,i->depth++);
1870 }
1871
1872 static void drawlink(struct swfoutput*obj, ActionTAG*,ActionTAG*, swfcoord*points, char mouseover);
1873
1874 void swfoutput_linktourl(struct swfoutput*obj, char*url, swfcoord*points)
1875 {
1876     swfoutput_internal*i = (swfoutput_internal*)obj->internal;
1877     ActionTAG* actions;
1878     if(!strncmp("http://pdf2swf:", url, 15)) {
1879      char*tmp = strdup(url);
1880      int l = strlen(tmp);
1881      if(tmp[l-1] == '/')
1882         tmp[l-1] = 0;
1883      swfoutput_namedlink(obj, tmp+15, points);
1884      free(tmp);
1885      return;
1886     }
1887     
1888     if(i->shapeid>=0)
1889         endshape(obj,0);
1890     if(i->textid>=0)
1891         endtext(obj);
1892     
1893     if(config.opennewwindow)
1894       actions = action_GetUrl(0, url, "_parent");
1895     else
1896       actions = action_GetUrl(0, url, "_this");
1897     actions = action_End(actions);
1898     
1899     drawlink(obj, actions, 0, points,0);
1900 }
1901 void swfoutput_linktopage(struct swfoutput*obj, int page, swfcoord*points)
1902 {
1903     swfoutput_internal*i = (swfoutput_internal*)obj->internal;
1904     ActionTAG* actions;
1905
1906     if(i->shapeid>=0)
1907         endshape(obj,0);
1908     if(i->textid>=0)
1909         endtext(obj);
1910    
1911       actions = action_GotoFrame(0, page);
1912       actions = action_End(actions);
1913
1914     drawlink(obj, actions, 0, points,0);
1915 }
1916
1917 /* Named Links (a.k.a. Acrobatmenu) are used to implement various gadgets
1918    of the viewer objects, like subtitles, index elements etc.
1919 */
1920 void swfoutput_namedlink(struct swfoutput*obj, char*name, swfcoord*points)
1921 {
1922     swfoutput_internal*i = (swfoutput_internal*)obj->internal;
1923     ActionTAG *actions1,*actions2;
1924     char*tmp = strdup(name);
1925     char mouseover = 1;
1926
1927     if(i->shapeid>=0)
1928         endshape(obj,0);
1929     if(i->textid>=0)
1930         endtext(obj);
1931
1932     if(!strncmp(tmp, "call:", 5))
1933     {
1934         char*x = strchr(&tmp[5], ':');
1935         if(!x) {
1936             actions1 = action_PushInt(0, 0); //number of parameters (0)
1937             actions1 = action_PushString(actions1, &tmp[5]); //function name
1938             actions1 = action_CallFunction(actions1);
1939         } else {
1940             *x = 0;
1941             actions1 = action_PushString(0, x+1); //parameter
1942             actions1 = action_PushInt(actions1, 1); //number of parameters (1)
1943             actions1 = action_PushString(actions1, &tmp[5]); //function name
1944             actions1 = action_CallFunction(actions1);
1945         }
1946         actions2 = action_End(0);
1947         mouseover = 0;
1948     }
1949     else
1950     {
1951         actions1 = action_PushString(0, "/:subtitle");
1952         actions1 = action_PushString(actions1, name);
1953         actions1 = action_SetVariable(actions1);
1954         actions1 = action_End(actions1);
1955
1956         actions2 = action_PushString(0, "/:subtitle");
1957         actions2 = action_PushString(actions2, "");
1958         actions2 = action_SetVariable(actions2);
1959         actions2 = action_End(actions2);
1960     }
1961
1962     drawlink(obj, actions1, actions2, points,mouseover);
1963
1964     swf_ActionFree(actions1);
1965     swf_ActionFree(actions2);
1966     free(tmp);
1967 }
1968
1969 static void drawlink(struct swfoutput*obj, ActionTAG*actions1, ActionTAG*actions2, swfcoord*points, char mouseover)
1970 {
1971     swfoutput_internal*i = (swfoutput_internal*)obj->internal;
1972     RGBA rgb;
1973     SRECT r;
1974     int lsid=0;
1975     int fsid;
1976     struct plotxy p1,p2,p3,p4;
1977     int myshapeid;
1978     int myshapeid2;
1979     double xmin,ymin;
1980     double xmax=xmin=points[0].x,ymax=ymin=points[0].y;
1981     double posx = 0;
1982     double posy = 0;
1983     int t;
1984     int buttonid = ++i->currentswfid;
1985     for(t=1;t<4;t++)
1986     {
1987         if(points[t].x>xmax) xmax=points[t].x;
1988         if(points[t].y>ymax) ymax=points[t].y;
1989         if(points[t].x<xmin) xmin=points[t].x;
1990         if(points[t].y<ymin) ymin=points[t].y;
1991     }
1992    
1993     p1.x=points[0].x; p1.y=points[0].y; p2.x=points[1].x; p2.y=points[1].y; 
1994     p3.x=points[2].x; p3.y=points[2].y; p4.x=points[3].x; p4.y=points[3].y;
1995    
1996     /* the following code subtracts the upper left edge from all coordinates,
1997        and set's posx,posy so that ST_PLACEOBJECT is used with a matrix.
1998        Necessary for preprocessing with swfcombine. */
1999     posx = xmin; posy = ymin;
2000     p1.x-=posx;p2.x-=posx;p3.x-=posx;p4.x-=posx;
2001     p1.y-=posy;p2.y-=posy;p3.y-=posy;p4.y-=posy;
2002     xmin -= posx; ymin -= posy;
2003     xmax -= posx; ymax -= posy;
2004     
2005     /* shape */
2006     myshapeid = ++i->currentswfid;
2007     i->tag = swf_InsertTag(i->tag,ST_DEFINESHAPE3);
2008     swf_ShapeNew(&i->shape);
2009     rgb.r = rgb.b = rgb.a = rgb.g = 0; 
2010     fsid = swf_ShapeAddSolidFillStyle(i->shape,&rgb);
2011     swf_SetU16(i->tag, myshapeid);
2012     r.xmin = (int)(xmin*20);
2013     r.ymin = (int)(ymin*20);
2014     r.xmax = (int)(xmax*20);
2015     r.ymax = (int)(ymax*20);
2016     swf_SetRect(i->tag,&r);
2017     swf_SetShapeStyles(i->tag,i->shape);
2018     swf_ShapeCountBits(i->shape,NULL,NULL);
2019     swf_SetShapeBits(i->tag,i->shape);
2020     swf_ShapeSetAll(i->tag,i->shape,/*x*/0,/*y*/0,0,fsid,0);
2021     i->swflastx = i->swflasty = 0;
2022     moveto(obj, i->tag, p1);
2023     lineto(obj, i->tag, p2);
2024     lineto(obj, i->tag, p3);
2025     lineto(obj, i->tag, p4);
2026     lineto(obj, i->tag, p1);
2027     swf_ShapeSetEnd(i->tag);
2028
2029     /* shape2 */
2030     myshapeid2 = ++i->currentswfid;
2031     i->tag = swf_InsertTag(i->tag,ST_DEFINESHAPE3);
2032     swf_ShapeNew(&i->shape);
2033     rgb.r = rgb.b = rgb.a = rgb.g = 255;
2034     rgb.a = 40;
2035     fsid = swf_ShapeAddSolidFillStyle(i->shape,&rgb);
2036     swf_SetU16(i->tag, myshapeid2);
2037     r.xmin = (int)(xmin*20);
2038     r.ymin = (int)(ymin*20);
2039     r.xmax = (int)(xmax*20);
2040     r.ymax = (int)(ymax*20);
2041     swf_SetRect(i->tag,&r);
2042     swf_SetShapeStyles(i->tag,i->shape);
2043     swf_ShapeCountBits(i->shape,NULL,NULL);
2044     swf_SetShapeBits(i->tag,i->shape);
2045     swf_ShapeSetAll(i->tag,i->shape,/*x*/0,/*y*/0,0,fsid,0);
2046     i->swflastx = i->swflasty = 0;
2047     moveto(obj, i->tag, p1);
2048     lineto(obj, i->tag, p2);
2049     lineto(obj, i->tag, p3);
2050     lineto(obj, i->tag, p4);
2051     lineto(obj, i->tag, p1);
2052     swf_ShapeSetEnd(i->tag);
2053
2054     if(!mouseover)
2055     {
2056         i->tag = swf_InsertTag(i->tag,ST_DEFINEBUTTON);
2057         swf_SetU16(i->tag,buttonid); //id
2058         swf_ButtonSetFlags(i->tag, 0); //menu=no
2059         swf_ButtonSetRecord(i->tag,0x01,myshapeid,i->depth,0,0);
2060         swf_ButtonSetRecord(i->tag,0x02,myshapeid2,i->depth,0,0);
2061         swf_ButtonSetRecord(i->tag,0x04,myshapeid2,i->depth,0,0);
2062         swf_ButtonSetRecord(i->tag,0x08,myshapeid,i->depth,0,0);
2063         swf_SetU8(i->tag,0);
2064         swf_ActionSet(i->tag,actions1);
2065         swf_SetU8(i->tag,0);
2066     }
2067     else
2068     {
2069         i->tag = swf_InsertTag(i->tag,ST_DEFINEBUTTON2);
2070         swf_SetU16(i->tag,buttonid); //id
2071         swf_ButtonSetFlags(i->tag, 0); //menu=no
2072         swf_ButtonSetRecord(i->tag,0x01,myshapeid,i->depth,0,0);
2073         swf_ButtonSetRecord(i->tag,0x02,myshapeid2,i->depth,0,0);
2074         swf_ButtonSetRecord(i->tag,0x04,myshapeid2,i->depth,0,0);
2075         swf_ButtonSetRecord(i->tag,0x08,myshapeid,i->depth,0,0);
2076         swf_SetU8(i->tag,0); // end of button records
2077         swf_ButtonSetCondition(i->tag, BC_IDLE_OVERUP);
2078         swf_ActionSet(i->tag,actions1);
2079         if(actions2) {
2080             swf_ButtonSetCondition(i->tag, BC_OVERUP_IDLE);
2081             swf_ActionSet(i->tag,actions2);
2082             swf_SetU8(i->tag,0);
2083             swf_ButtonPostProcess(i->tag, 2);
2084         } else {
2085             swf_SetU8(i->tag,0);
2086             swf_ButtonPostProcess(i->tag, 1);
2087         }
2088     }
2089     
2090     i->tag = swf_InsertTag(i->tag,ST_PLACEOBJECT2);
2091
2092     if(posx!=0 || posy!=0) {
2093         MATRIX m;
2094         swf_GetMatrix(0,&m);
2095         m.tx = (int)(posx*20);
2096         m.ty = (int)(posy*20);
2097         swf_ObjectPlace(i->tag, buttonid, i->depth++,&m,0,0);
2098     }
2099     else {
2100         swf_ObjectPlace(i->tag, buttonid, i->depth++,0,0,0);
2101     }
2102 }
2103
2104 static void drawimage(struct swfoutput*obj, int bitid, int sizex,int sizey, 
2105         double x1,double y1,
2106         double x2,double y2,
2107         double x3,double y3,
2108         double x4,double y4)
2109 {
2110     swfoutput_internal*i = (swfoutput_internal*)obj->internal;
2111     RGBA rgb;
2112     SRECT r;
2113     int lsid=0;
2114     int fsid;
2115     struct plotxy p1,p2,p3,p4;
2116     int myshapeid;
2117     double xmax=x1,ymax=y1,xmin=x1,ymin=y1;
2118     if(x2>xmax) xmax=x2;
2119     if(y2>ymax) ymax=y2;
2120     if(x2<xmin) xmin=x2;
2121     if(y2<ymin) ymin=y2;
2122     if(x3>xmax) xmax=x3;
2123     if(y3>ymax) ymax=y3;
2124     if(x3<xmin) xmin=x3;
2125     if(y3<ymin) ymin=y3;
2126     if(x4>xmax) xmax=x4;
2127     if(y4>ymax) ymax=y4;
2128     if(x4<xmin) xmin=x4;
2129     if(y4<ymin) ymin=y4;
2130     p1.x=x1; p1.y=y1;
2131     p2.x=x2; p2.y=y2;
2132     p3.x=x3; p3.y=y3;
2133     p4.x=x4; p4.y=y4;
2134
2135     {p1.x = (int)(p1.x*20)/20.0;
2136      p1.y = (int)(p1.y*20)/20.0;
2137      p2.x = (int)(p2.x*20)/20.0;
2138      p2.y = (int)(p2.y*20)/20.0;
2139      p3.x = (int)(p3.x*20)/20.0;
2140      p3.y = (int)(p3.y*20)/20.0;
2141      p4.x = (int)(p4.x*20)/20.0;
2142      p4.y = (int)(p4.y*20)/20.0;}
2143     
2144     MATRIX m;
2145     m.sx = (int)(65536*20*(p4.x-p1.x)/sizex);
2146     m.r1 = -(int)(65536*20*(p4.y-p1.y)/sizex);
2147     m.r0 = (int)(65536*20*(p1.x-p2.x)/sizey);
2148     m.sy = -(int)(65536*20*(p1.y-p2.y)/sizey);
2149
2150     m.tx = (int)(p1.x*20);
2151     m.ty = (int)(p1.y*20);
2152   
2153     /* shape */
2154     myshapeid = ++i->currentswfid;
2155     i->tag = swf_InsertTag(i->tag,ST_DEFINESHAPE);
2156     swf_ShapeNew(&i->shape);
2157     //lsid = ShapeAddLineStyle(shape,linewidth,&obj->strokergb);
2158     //fsid = ShapeAddSolidFillStyle(shape,&obj->fillrgb);
2159     fsid = swf_ShapeAddBitmapFillStyle(i->shape,&m,bitid,1);
2160     swf_SetU16(i->tag, myshapeid);
2161     r.xmin = (int)(xmin*20);
2162     r.ymin = (int)(ymin*20);
2163     r.xmax = (int)(xmax*20);
2164     r.ymax = (int)(ymax*20);
2165     swf_SetRect(i->tag,&r);
2166     swf_SetShapeStyles(i->tag,i->shape);
2167     swf_ShapeCountBits(i->shape,NULL,NULL);
2168     swf_SetShapeBits(i->tag,i->shape);
2169     swf_ShapeSetAll(i->tag,i->shape,/*x*/0,/*y*/0,lsid,fsid,0);
2170     i->swflastx = i->swflasty = 0;
2171     moveto(obj, i->tag, p1);
2172     lineto(obj, i->tag, p2);
2173     lineto(obj, i->tag, p3);
2174     lineto(obj, i->tag, p4);
2175     lineto(obj, i->tag, p1);
2176     /*
2177     ShapeMoveTo  (tag, shape, (int)(x1*20),(int)(y1*20));
2178     ShapeSetLine (tag, shape, (int)(x1*20);
2179     ShapeSetLine (tag, shape, x*20,0);
2180     ShapeSetLine (tag, shape, 0,-y*20);
2181     ShapeSetLine (tag, shape, -x*20,0);*/
2182     swf_ShapeSetEnd(i->tag);
2183
2184     /* instance */
2185     i->tag = swf_InsertTag(i->tag,ST_PLACEOBJECT2);
2186     swf_ObjectPlace(i->tag,myshapeid,/*depth*/i->depth++,NULL,NULL,NULL);
2187 }
2188
2189 int swfoutput_drawimagejpeg_old(struct swfoutput*obj, char*filename, int sizex,int sizey, 
2190         double x1,double y1,
2191         double x2,double y2,
2192         double x3,double y3,
2193         double x4,double y4)
2194 {
2195     swfoutput_internal*i = (swfoutput_internal*)obj->internal;
2196     TAG*oldtag;
2197     if(i->shapeid>=0)
2198         endshape(obj,0);
2199     if(i->textid>=0)
2200         endtext(obj);
2201
2202     int bitid = ++i->currentswfid;
2203     oldtag = i->tag;
2204     i->tag = swf_InsertTag(i->tag,ST_DEFINEBITSJPEG2);
2205     swf_SetU16(i->tag, bitid);
2206     if(swf_SetJPEGBits(i->tag, filename, config.jpegquality)<0) {
2207         swf_DeleteTag(i->tag);
2208         i->tag = oldtag;
2209         return -1;
2210     }
2211
2212     drawimage(obj, bitid, sizex, sizey, x1,y1,x2,y2,x3,y3,x4,y4);
2213     return bitid;
2214 }
2215
2216 int swfoutput_drawimagejpeg(struct swfoutput*obj, RGBA*mem, int sizex,int sizey, 
2217         double x1,double y1,
2218         double x2,double y2,
2219         double x3,double y3,
2220         double x4,double y4)
2221 {
2222     swfoutput_internal*i = (swfoutput_internal*)obj->internal;
2223     TAG*oldtag;
2224     JPEGBITS*jpeg;
2225
2226     if(i->shapeid>=0)
2227         endshape(obj,0);
2228     if(i->textid>=0)
2229         endtext(obj);
2230
2231     int bitid = ++i->currentswfid;
2232     oldtag = i->tag;
2233     i->tag = swf_InsertTag(i->tag,ST_DEFINEBITSJPEG2);
2234     swf_SetU16(i->tag, bitid);
2235     swf_SetJPEGBits2(i->tag,sizex,sizey,mem,config.jpegquality);
2236     drawimage(obj, bitid, sizex, sizey, x1,y1,x2,y2,x3,y3,x4,y4);
2237     return bitid;
2238 }
2239
2240 int swfoutput_drawimagelossless(struct swfoutput*obj, RGBA*mem, int sizex,int sizey, 
2241         double x1,double y1,
2242         double x2,double y2,
2243         double x3,double y3,
2244         double x4,double y4)
2245 {
2246     swfoutput_internal*i = (swfoutput_internal*)obj->internal;
2247     TAG*oldtag;
2248     if(i->shapeid>=0)
2249         endshape(obj,0);
2250     if(i->textid>=0)
2251         endtext(obj);
2252
2253     int bitid = ++i->currentswfid;
2254     oldtag = i->tag;
2255     i->tag = swf_InsertTag(i->tag,ST_DEFINEBITSLOSSLESS);
2256     swf_SetU16(i->tag, bitid);
2257     if(swf_SetLosslessBits(i->tag,sizex,sizey,mem, BMF_32BIT)<0) {
2258         swf_DeleteTag(i->tag);
2259         i->tag = oldtag;
2260         return -1;
2261     }
2262     
2263     drawimage(obj, bitid, sizex, sizey, x1,y1,x2,y2,x3,y3,x4,y4);
2264     return bitid;
2265 }
2266
2267 int swfoutput_drawimagelosslessN(struct swfoutput*obj, U8*mem, RGBA*pal, int sizex,int sizey, 
2268         double x1,double y1,
2269         double x2,double y2,
2270         double x3,double y3,
2271         double x4,double y4, int n)
2272 {
2273     swfoutput_internal*i = (swfoutput_internal*)obj->internal;
2274     TAG*oldtag;
2275     U8*mem2 = 0;
2276     if(i->shapeid>=0)
2277         endshape(obj,0);
2278     if(i->textid>=0)
2279         endtext(obj);
2280
2281     if(sizex&3)
2282     { 
2283         /* SWF expects scanlines to be 4 byte aligned */
2284         int x,y;
2285         U8*ptr;
2286         mem2 = (U8*)malloc(BYTES_PER_SCANLINE(sizex)*sizey);
2287         ptr = mem2;
2288         for(y=0;y<sizey;y++)
2289         {
2290             for(x=0;x<sizex;x++)
2291                 *ptr++ = mem[y*sizex+x];
2292             ptr+= BYTES_PER_SCANLINE(sizex)-sizex;
2293         }
2294         mem = mem2;
2295     }
2296
2297     int bitid = ++i->currentswfid;
2298     oldtag = i->tag;
2299     i->tag = swf_InsertTag(i->tag,ST_DEFINEBITSLOSSLESS2);
2300     swf_SetU16(i->tag, bitid);
2301     if(swf_SetLosslessBitsIndexed(i->tag,sizex,sizey,mem, pal, n)<0) {
2302         swf_DeleteTag(i->tag);
2303         i->tag = oldtag;
2304         return -1;
2305     }
2306     if(mem2)
2307         free(mem2);
2308   
2309     drawimage(obj, bitid, sizex, sizey, x1,y1,x2,y2,x3,y3,x4,y4);
2310     return bitid;
2311 }
2312
2313 void swfoutput_drawimageagain(struct swfoutput*obj, int id, int sizex,int sizey, 
2314         double x1,double y1,
2315         double x2,double y2,
2316         double x3,double y3,
2317         double x4,double y4)
2318 {
2319     swfoutput_internal*i = (swfoutput_internal*)obj->internal;
2320     if(id<0) return;
2321     if(i->shapeid>=0)
2322         endshape(obj,0);
2323     if(i->textid>=0)
2324         endtext(obj);
2325
2326     drawimage(obj, id, sizex, sizey, x1,y1,x2,y2,x3,y3,x4,y4);
2327 }
2328
2329 void swfoutput_setparameter(char*name, char*value)
2330 {
2331     if(!strcmp(name, "drawonlyshapes")) {
2332         config.drawonlyshapes = atoi(value);
2333     } else if(!strcmp(name, "ignoredraworder")) {
2334         config.ignoredraworder = atoi(value);
2335     } else if(!strcmp(name, "filloverlap")) {
2336         config.filloverlap = atoi(value);
2337     } else if(!strcmp(name, "linksopennewwindow")) {
2338         config.opennewwindow = atoi(value);
2339     } else if(!strcmp(name, "opennewwindow")) {
2340         config.opennewwindow = atoi(value);
2341     } else if(!strcmp(name, "storeallcharacters")) {
2342         config.storeallcharacters = atoi(value);
2343     } else if(!strcmp(name, "enablezlib")) {
2344         config.enablezlib = atoi(value);
2345     } else if(!strcmp(name, "insertstop")) {
2346         config.insertstoptag = atoi(value);
2347     } else if(!strcmp(name, "protected")) {
2348         config.protect = atoi(value);
2349     } else if(!strcmp(name, "flashversion")) {
2350         config.flashversion = atoi(value);
2351     } else if(!strcmp(name, "minlinewidth")) {
2352         config.minlinewidth = atof(value);
2353     } else if(!strcmp(name, "jpegquality")) {
2354         int val = atoi(value);
2355         if(val<0) val=0;
2356         if(val>100) val=100;
2357         config.jpegquality = val;
2358     } else if(!strcmp(name, "splinequality")) {
2359         int v = atoi(value);
2360         v = 500-(v*5); // 100% = 0.25 pixel, 0% = 25 pixel
2361         if(v<1) v = 1;
2362         config.splinemaxerror = v;
2363     } else if(!strcmp(name, "fontquality")) {
2364         int v = atoi(value);
2365         v = 500-(v*5); // 100% = 0.25 pixel, 0% = 25 pixel
2366         if(v<1) v = 1;
2367         config.fontsplinemaxerror = v;
2368     } else {
2369         fprintf(stderr, "unknown parameter: %s (=%s)\n", name, value);
2370     }
2371 }
2372