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