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