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