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