fixed "triangle problem"- corrected bounding boxes are now correct
[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.1;
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(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(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(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(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(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(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 1
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(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(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(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   /* TODO: patch back */
1404   bboxrectpos = tag->len;
1405   r.xmin = 0;
1406   r.ymin = 0;
1407   r.xmax = 20*sizex;
1408   r.ymax = 20*sizey;
1409   swf_SetRect(tag,&r);
1410  
1411   memset(&bboxrect, 0, sizeof(bboxrect));
1412
1413   swf_SetShapeStyles(tag,shape);
1414   swf_ShapeCountBits(shape,NULL,NULL);
1415   swf_SetShapeBits(tag,shape);
1416
1417   /* TODO: do we really need this? */
1418   swf_ShapeSetAll(tag,shape,/*x*/0,/*y*/0,linestyleid,0,0);
1419   swflastx=swflasty=0;
1420   lastwasfill = 0;
1421   shapeisempty = 1;
1422 }
1423
1424 static void starttext(struct swfoutput*obj)
1425 {
1426   if(shapeid>=0)
1427       endshape(0);
1428     
1429   textid = ++currentswfid;
1430
1431   swflastx=swflasty=0;
1432 }
1433             
1434
1435 /* TODO: move to ../lib/rfxswf */
1436 void changeRect(TAG*tag, int pos, SRECT*newrect)
1437 {
1438     /* determine length of old rect */
1439     tag->pos = pos;
1440     tag->readBit = 0;
1441     SRECT old;
1442     swf_GetRect(tag, &old);
1443     swf_ResetReadBits(tag);
1444     int pos_end = tag->pos;
1445
1446     int len = tag->len - pos_end;
1447     U8*data = (U8*)malloc(len);
1448     memcpy(data, &tag->data[pos_end], len);
1449     tag->writeBit = 0;
1450     tag->len = pos;
1451     swf_SetRect(tag, newrect);
1452     swf_SetBlock(tag, data, len);
1453     free(data);
1454     tag->pos = tag->readBit = 0;
1455 }
1456
1457 void fixAreas()
1458 {
1459     if(!shapeisempty && fill &&
1460        (bboxrect.xmin == bboxrect.xmax ||
1461         bboxrect.ymin == bboxrect.ymax) &&
1462         minlinewidth >= 0.001
1463        ) {
1464         SRECT r = bboxrect;
1465         msg("<debug> Shape has size 0");
1466     
1467         if(r.xmin == r.xmax && r.ymin == r.ymax) {
1468             /* this thing comes down to a single dot- nothing to fix here */
1469             return;
1470         }
1471
1472         double x=0,y=0;
1473         if(r.xmin == r.xmax) {
1474             x = minlinewidth;
1475         } else {
1476             y = minlinewidth;
1477         }
1478         /* warning: doing this inside endshape() is dangerous */
1479         moveto(tag, r.xmin/20.0    , r.ymin/20.0);
1480         lineto(tag, r.xmax/20.0 + x, r.ymin/20.0);
1481         lineto(tag, r.xmax/20.0 + x, r.ymax/20.0 + y);
1482         lineto(tag, r.xmin/20.0    , r.ymax/20.0 + y);
1483         lineto(tag, r.xmin/20.0    , r.ymin/20.0);
1484     }
1485     
1486 }
1487
1488 static void endshape(int clipdepth)
1489 {
1490     if(shapeid<0) 
1491         return;
1492
1493     if(!clipdepth)
1494         fixAreas();
1495         
1496     if(shapeisempty ||
1497        (bboxrect.xmin == bboxrect.xmax && bboxrect.ymin == bboxrect.ymax)) 
1498     {
1499         // delete the tag again, we didn't do anything
1500         TAG*todel = tag;
1501         tag = tag->prev;
1502         swf_DeleteTag(todel);
1503         shapeid = -1;
1504         bboxrectpos = -1;
1505         return;
1506     }
1507     
1508     swf_ShapeSetEnd(tag);
1509
1510     changeRect(tag, bboxrectpos, &bboxrect);
1511
1512     tag = swf_InsertTag(tag,ST_PLACEOBJECT2);
1513     if(clipdepth)
1514         swf_ObjectPlaceClip(tag,shapeid,depth++,NULL,NULL,NULL,clipdepth);
1515     else
1516         swf_ObjectPlace(tag,shapeid,/*depth*/depth++,NULL,NULL,NULL);
1517
1518     shapeid = -1;
1519     bboxrectpos = -1;
1520 }
1521
1522 static void endpage(struct swfoutput*obj)
1523 {
1524     if(shapeid>=0)
1525       endshape(0);
1526     if(textid>=0)
1527       endtext(obj);
1528     while(clippos)
1529         swfoutput_endclip(obj);
1530
1531     if(insertstoptag) {
1532         ActionTAG*atag=0;
1533         atag = action_Stop(atag);
1534         atag = action_End(atag);
1535         tag = swf_InsertTag(tag,ST_DOACTION);
1536         swf_ActionSet(tag,atag);
1537     }
1538     tag = swf_InsertTag(tag,ST_SHOWFRAME);
1539 }
1540
1541 void swfoutput_newpage(struct swfoutput*obj)
1542 {
1543     endpage(obj);
1544
1545     for(depth--;depth>=startdepth;depth--) {
1546         tag = swf_InsertTag(tag,ST_REMOVEOBJECT2);
1547         swf_SetU16(tag,depth);
1548     }
1549
1550     depth = startdepth;
1551 }
1552
1553 /* Perform cleaning up, complete the swf, and write it out. */
1554 void swfoutput_destroy(struct swfoutput* obj) 
1555 {
1556     endpage(obj);
1557     fontlist_t *tmp,*iterator = fontlist;
1558     while(iterator) {
1559         TAG*mtag = swf.firstTag;
1560         if(iterator->swffont) {
1561             mtag = swf_InsertTag(mtag, ST_DEFINEFONT2);
1562             /*if(!storeallcharacters)
1563                 swf_FontReduce(iterator->swffont);*/
1564             swf_FontSetDefine2(mtag, iterator->swffont);
1565             swf_FontFree(iterator->swffont);
1566         }
1567
1568         tmp = iterator;
1569         iterator = iterator->next;
1570         delete tmp;
1571     }
1572
1573     if(!filename) 
1574         return;
1575     if(filename)
1576      fi = open(filename, O_BINARY|O_CREAT|O_TRUNC|O_WRONLY, 0777);
1577     else
1578      fi = 1; // stdout
1579     
1580     if(fi<=0) {
1581      msg("<fatal> Could not create \"%s\". ", FIXNULL(filename));
1582      exit(1);
1583     }
1584  
1585     tag = swf_InsertTag(tag,ST_END);
1586
1587     if(enablezlib || flashversion>=6) {
1588       if FAILED(swf_WriteSWC(fi,&swf)) 
1589        msg("<error> WriteSWC() failed.\n");
1590     } else {
1591       if FAILED(swf_WriteSWF(fi,&swf)) 
1592        msg("<error> WriteSWF() failed.\n");
1593     }
1594
1595     if(filename)
1596      close(fi);
1597     msg("<notice> SWF written\n");
1598 }
1599
1600 void swfoutput_setdrawmode(swfoutput* obj, int mode)
1601 {
1602     obj->drawmode = mode;
1603     if(mode == DRAWMODE_FILL)
1604      fill = 1;
1605     else if(mode == DRAWMODE_EOFILL)
1606      fill = 1;
1607     else if(mode == DRAWMODE_STROKE)
1608      fill = 0;
1609     else if(mode == DRAWMODE_CLIP)
1610      fill = 1;
1611     else if(mode == DRAWMODE_EOCLIP)
1612      fill = 1;
1613 }
1614
1615 void swfoutput_setfillcolor(swfoutput* obj, u8 r, u8 g, u8 b, u8 a)
1616 {
1617     if(obj->fillrgb.r == r &&
1618        obj->fillrgb.g == g &&
1619        obj->fillrgb.b == b &&
1620        obj->fillrgb.a == a) return;
1621     if(shapeid>=0)
1622      endshape(0);
1623
1624     obj->fillrgb.r = r;
1625     obj->fillrgb.g = g;
1626     obj->fillrgb.b = b;
1627     obj->fillrgb.a = a;
1628 }
1629
1630 void swfoutput_setstrokecolor(swfoutput* obj, u8 r, u8 g, u8 b, u8 a)
1631 {
1632     if(obj->strokergb.r == r &&
1633        obj->strokergb.g == g &&
1634        obj->strokergb.b == b &&
1635        obj->strokergb.a == a) return;
1636
1637     if(shapeid>=0)
1638      endshape(0);
1639     obj->strokergb.r = r;
1640     obj->strokergb.g = g;
1641     obj->strokergb.b = b;
1642     obj->strokergb.a = a;
1643 }
1644
1645 void swfoutput_setlinewidth(struct swfoutput*obj, double _linewidth)
1646 {
1647     if(linewidth == (u16)(_linewidth*20))
1648         return;
1649
1650     if(shapeid>=0)
1651         endshape(0);
1652     linewidth = (u16)(_linewidth*20);
1653 }
1654
1655
1656 void swfoutput_startclip(swfoutput*obj, SWF_OUTLINE*outline, struct swfmatrix*m)
1657 {
1658     if(textid>=0)
1659      endtext(obj);
1660     if(shapeid>=0)
1661      endshape(0);
1662
1663     if(clippos >= 127)
1664     {
1665         msg("<warning> Too many clip levels.");
1666         clippos --;
1667     } 
1668     
1669     startshape(obj);
1670     int olddrawmode = obj->drawmode;
1671     swfoutput_setdrawmode(obj, DRAWMODE_CLIP);
1672     swfoutput_drawpath(obj, outline, m);
1673     swf_ShapeSetEnd(tag);
1674     swfoutput_setdrawmode(obj, olddrawmode);
1675
1676     tag = swf_InsertTag(tag,ST_PLACEOBJECT2);
1677     cliptags[clippos] = tag;
1678     clipshapes[clippos] = shapeid;
1679     clipdepths[clippos] = depth++;
1680     clippos++;
1681     shapeid = -1;
1682 }
1683
1684 void swfoutput_endclip(swfoutput*obj)
1685 {
1686     if(textid>=0)
1687         endtext(obj);
1688     if(shapeid>=0)
1689         endshape(0);
1690
1691     if(!clippos) {
1692         msg("<error> Invalid end of clipping region");
1693         return;
1694     }
1695     clippos--;
1696     swf_ObjectPlaceClip(cliptags[clippos],clipshapes[clippos],clipdepths[clippos],NULL,NULL,NULL,depth++);
1697 }
1698
1699 static void drawlink(struct swfoutput*obj, ActionTAG*,ActionTAG*, swfcoord*points, char mouseover);
1700
1701 void swfoutput_linktourl(struct swfoutput*obj, char*url, swfcoord*points)
1702 {
1703     ActionTAG* actions;
1704     if(!strncmp("http://pdf2swf:", url, 15)) {
1705      char*tmp = strdup(url);
1706      int l = strlen(tmp);
1707      if(tmp[l-1] == '/')
1708         tmp[l-1] = 0;
1709      swfoutput_namedlink(obj, tmp+15, points);
1710      free(tmp);
1711      return;
1712     }
1713     
1714     if(shapeid>=0)
1715         endshape(0);
1716     if(textid>=0)
1717         endtext(obj);
1718     
1719     if(opennewwindow)
1720       actions = action_GetUrl(0, url, "_parent");
1721     else
1722       actions = action_GetUrl(0, url, "_this");
1723     actions = action_End(actions);
1724     
1725     drawlink(obj, actions, 0, points,0);
1726 }
1727 void swfoutput_linktopage(struct swfoutput*obj, int page, swfcoord*points)
1728 {
1729     ActionTAG* actions;
1730
1731     if(shapeid>=0)
1732         endshape(0);
1733     if(textid>=0)
1734         endtext(obj);
1735    
1736       actions = action_GotoFrame(0, page);
1737       actions = action_End(actions);
1738
1739     drawlink(obj, actions, 0, points,0);
1740 }
1741
1742 /* Named Links (a.k.a. Acrobatmenu) are used to implement various gadgets
1743    of the viewer objects, like subtitles, index elements etc.
1744 */
1745 void swfoutput_namedlink(struct swfoutput*obj, char*name, swfcoord*points)
1746 {
1747     ActionTAG *actions1,*actions2;
1748     char*tmp = strdup(name);
1749     char mouseover = 1;
1750
1751     if(shapeid>=0)
1752         endshape(0);
1753     if(textid>=0)
1754         endtext(obj);
1755
1756     if(!strncmp(tmp, "call:", 5))
1757     {
1758         char*x = strchr(&tmp[5], ':');
1759         if(!x) {
1760             actions1 = action_PushInt(0, 0); //number of parameters (0)
1761             actions1 = action_PushString(actions1, &tmp[5]); //function name
1762             actions1 = action_CallFunction(actions1);
1763         } else {
1764             *x = 0;
1765             actions1 = action_PushString(0, x+1); //parameter
1766             actions1 = action_PushInt(actions1, 1); //number of parameters (1)
1767             actions1 = action_PushString(actions1, &tmp[5]); //function name
1768             actions1 = action_CallFunction(actions1);
1769         }
1770         actions2 = action_End(0);
1771         mouseover = 0;
1772     }
1773     else
1774     {
1775         actions1 = action_PushString(0, "/:subtitle");
1776         actions1 = action_PushString(actions1, name);
1777         actions1 = action_SetVariable(actions1);
1778         actions1 = action_End(actions1);
1779
1780         actions2 = action_PushString(0, "/:subtitle");
1781         actions2 = action_PushString(actions2, "");
1782         actions2 = action_SetVariable(actions2);
1783         actions2 = action_End(actions2);
1784     }
1785
1786     drawlink(obj, actions1, actions2, points,mouseover);
1787
1788     swf_ActionFree(actions1);
1789     swf_ActionFree(actions2);
1790     free(tmp);
1791 }
1792
1793 static void drawlink(struct swfoutput*obj, ActionTAG*actions1, ActionTAG*actions2, swfcoord*points, char mouseover)
1794 {
1795     RGBA rgb;
1796     SRECT r;
1797     int lsid=0;
1798     int fsid;
1799     struct plotxy p1,p2,p3,p4;
1800     int myshapeid;
1801     int myshapeid2;
1802     double xmin,ymin;
1803     double xmax=xmin=points[0].x,ymax=ymin=points[0].y;
1804     double posx = 0;
1805     double posy = 0;
1806     int t;
1807     int buttonid = ++currentswfid;
1808     for(t=1;t<4;t++)
1809     {
1810         if(points[t].x>xmax) xmax=points[t].x;
1811         if(points[t].y>ymax) ymax=points[t].y;
1812         if(points[t].x<xmin) xmin=points[t].x;
1813         if(points[t].y<ymin) ymin=points[t].y;
1814     }
1815    
1816     p1.x=points[0].x; p1.y=points[0].y; p2.x=points[1].x; p2.y=points[1].y; 
1817     p3.x=points[2].x; p3.y=points[2].y; p4.x=points[3].x; p4.y=points[3].y;
1818    
1819     /* the following code subtracts the upper left edge from all coordinates,
1820        and set's posx,posy so that ST_PLACEOBJECT is used with a matrix.
1821        Necessary for preprocessing with swfcombine. */
1822     posx = xmin; posy = ymin;
1823     p1.x-=posx;p2.x-=posx;p3.x-=posx;p4.x-=posx;
1824     p1.y-=posy;p2.y-=posy;p3.y-=posy;p4.y-=posy;
1825     xmin -= posx; ymin -= posy;
1826     xmax -= posx; ymax -= posy;
1827     
1828     /* shape */
1829     myshapeid = ++currentswfid;
1830     tag = swf_InsertTag(tag,ST_DEFINESHAPE3);
1831     swf_ShapeNew(&shape);
1832     rgb.r = rgb.b = rgb.a = rgb.g = 0; 
1833     fsid = swf_ShapeAddSolidFillStyle(shape,&rgb);
1834     swf_SetU16(tag, myshapeid);
1835     r.xmin = (int)(xmin*20);
1836     r.ymin = (int)(ymin*20);
1837     r.xmax = (int)(xmax*20);
1838     r.ymax = (int)(ymax*20);
1839     swf_SetRect(tag,&r);
1840     swf_SetShapeStyles(tag,shape);
1841     swf_ShapeCountBits(shape,NULL,NULL);
1842     swf_SetShapeBits(tag,shape);
1843     swf_ShapeSetAll(tag,shape,/*x*/0,/*y*/0,0,fsid,0);
1844     swflastx = swflasty = 0;
1845     moveto(tag, p1);
1846     lineto(tag, p2);
1847     lineto(tag, p3);
1848     lineto(tag, p4);
1849     lineto(tag, p1);
1850     swf_ShapeSetEnd(tag);
1851
1852     /* shape2 */
1853     myshapeid2 = ++currentswfid;
1854     tag = swf_InsertTag(tag,ST_DEFINESHAPE3);
1855     swf_ShapeNew(&shape);
1856     rgb.r = rgb.b = rgb.a = rgb.g = 255;
1857     rgb.a = 40;
1858     fsid = swf_ShapeAddSolidFillStyle(shape,&rgb);
1859     swf_SetU16(tag, myshapeid2);
1860     r.xmin = (int)(xmin*20);
1861     r.ymin = (int)(ymin*20);
1862     r.xmax = (int)(xmax*20);
1863     r.ymax = (int)(ymax*20);
1864     swf_SetRect(tag,&r);
1865     swf_SetShapeStyles(tag,shape);
1866     swf_ShapeCountBits(shape,NULL,NULL);
1867     swf_SetShapeBits(tag,shape);
1868     swf_ShapeSetAll(tag,shape,/*x*/0,/*y*/0,0,fsid,0);
1869     swflastx = swflasty = 0;
1870     moveto(tag, p1);
1871     lineto(tag, p2);
1872     lineto(tag, p3);
1873     lineto(tag, p4);
1874     lineto(tag, p1);
1875     swf_ShapeSetEnd(tag);
1876
1877     if(!mouseover)
1878     {
1879         tag = swf_InsertTag(tag,ST_DEFINEBUTTON);
1880         swf_SetU16(tag,buttonid); //id
1881         swf_ButtonSetFlags(tag, 0); //menu=no
1882         swf_ButtonSetRecord(tag,0x01,myshapeid,depth,0,0);
1883         swf_ButtonSetRecord(tag,0x02,myshapeid2,depth,0,0);
1884         swf_ButtonSetRecord(tag,0x04,myshapeid2,depth,0,0);
1885         swf_ButtonSetRecord(tag,0x08,myshapeid,depth,0,0);
1886         swf_SetU8(tag,0);
1887         swf_ActionSet(tag,actions1);
1888         swf_SetU8(tag,0);
1889     }
1890     else
1891     {
1892         tag = swf_InsertTag(tag,ST_DEFINEBUTTON2);
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); // end of button records
1900         swf_ButtonSetCondition(tag, BC_IDLE_OVERUP);
1901         swf_ActionSet(tag,actions1);
1902         if(actions2) {
1903             swf_ButtonSetCondition(tag, BC_OVERUP_IDLE);
1904             swf_ActionSet(tag,actions2);
1905             swf_SetU8(tag,0);
1906             swf_ButtonPostProcess(tag, 2);
1907         } else {
1908             swf_SetU8(tag,0);
1909             swf_ButtonPostProcess(tag, 1);
1910         }
1911     }
1912     
1913     tag = swf_InsertTag(tag,ST_PLACEOBJECT2);
1914
1915     if(posx!=0 || posy!=0) {
1916         MATRIX m;
1917         swf_GetMatrix(0,&m);
1918         m.tx = (int)(posx*20);
1919         m.ty = (int)(posy*20);
1920         swf_ObjectPlace(tag, buttonid, depth++,&m,0,0);
1921     }
1922     else {
1923         swf_ObjectPlace(tag, buttonid, depth++,0,0,0);
1924     }
1925 }
1926
1927 static void drawimage(struct swfoutput*obj, int bitid, int sizex,int sizey, 
1928         double x1,double y1,
1929         double x2,double y2,
1930         double x3,double y3,
1931         double x4,double y4)
1932 {
1933     RGBA rgb;
1934     SRECT r;
1935     int lsid=0;
1936     int fsid;
1937     struct plotxy p1,p2,p3,p4;
1938     int myshapeid;
1939     double xmax=x1,ymax=y1,xmin=x1,ymin=y1;
1940     if(x2>xmax) xmax=x2;
1941     if(y2>ymax) ymax=y2;
1942     if(x2<xmin) xmin=x2;
1943     if(y2<ymin) ymin=y2;
1944     if(x3>xmax) xmax=x3;
1945     if(y3>ymax) ymax=y3;
1946     if(x3<xmin) xmin=x3;
1947     if(y3<ymin) ymin=y3;
1948     if(x4>xmax) xmax=x4;
1949     if(y4>ymax) ymax=y4;
1950     if(x4<xmin) xmin=x4;
1951     if(y4<ymin) ymin=y4;
1952     p1.x=x1; p1.y=y1;
1953     p2.x=x2; p2.y=y2;
1954     p3.x=x3; p3.y=y3;
1955     p4.x=x4; p4.y=y4;
1956
1957     {p1.x = (int)(p1.x*20)/20.0;
1958      p1.y = (int)(p1.y*20)/20.0;
1959      p2.x = (int)(p2.x*20)/20.0;
1960      p2.y = (int)(p2.y*20)/20.0;
1961      p3.x = (int)(p3.x*20)/20.0;
1962      p3.y = (int)(p3.y*20)/20.0;
1963      p4.x = (int)(p4.x*20)/20.0;
1964      p4.y = (int)(p4.y*20)/20.0;}
1965     
1966     MATRIX m;
1967     m.sx = (int)(65536*20*(p4.x-p1.x)/sizex);
1968     m.r1 = -(int)(65536*20*(p4.y-p1.y)/sizex);
1969     m.r0 = (int)(65536*20*(p1.x-p2.x)/sizey);
1970     m.sy = -(int)(65536*20*(p1.y-p2.y)/sizey);
1971
1972     m.tx = (int)(p1.x*20);
1973     m.ty = (int)(p1.y*20);
1974   
1975     /* shape */
1976     myshapeid = ++currentswfid;
1977     tag = swf_InsertTag(tag,ST_DEFINESHAPE);
1978     swf_ShapeNew(&shape);
1979     //lsid = ShapeAddLineStyle(shape,linewidth,&obj->strokergb);
1980     //fsid = ShapeAddSolidFillStyle(shape,&obj->fillrgb);
1981     fsid = swf_ShapeAddBitmapFillStyle(shape,&m,bitid,1);
1982     swf_SetU16(tag, myshapeid);
1983     r.xmin = (int)(xmin*20);
1984     r.ymin = (int)(ymin*20);
1985     r.xmax = (int)(xmax*20);
1986     r.ymax = (int)(ymax*20);
1987     swf_SetRect(tag,&r);
1988     swf_SetShapeStyles(tag,shape);
1989     swf_ShapeCountBits(shape,NULL,NULL);
1990     swf_SetShapeBits(tag,shape);
1991     swf_ShapeSetAll(tag,shape,/*x*/0,/*y*/0,lsid,fsid,0);
1992     swflastx = swflasty = 0;
1993     moveto(tag, p1);
1994     lineto(tag, p2);
1995     lineto(tag, p3);
1996     lineto(tag, p4);
1997     lineto(tag, p1);
1998     /*
1999     ShapeMoveTo  (tag, shape, (int)(x1*20),(int)(y1*20));
2000     ShapeSetLine (tag, shape, (int)(x1*20);
2001     ShapeSetLine (tag, shape, x*20,0);
2002     ShapeSetLine (tag, shape, 0,-y*20);
2003     ShapeSetLine (tag, shape, -x*20,0);*/
2004     swf_ShapeSetEnd(tag);
2005
2006     /* instance */
2007     tag = swf_InsertTag(tag,ST_PLACEOBJECT2);
2008     swf_ObjectPlace(tag,myshapeid,/*depth*/depth++,NULL,NULL,NULL);
2009 }
2010
2011 int swfoutput_drawimagejpeg_old(struct swfoutput*obj, char*filename, int sizex,int sizey, 
2012         double x1,double y1,
2013         double x2,double y2,
2014         double x3,double y3,
2015         double x4,double y4)
2016 {
2017     TAG*oldtag;
2018     if(shapeid>=0)
2019         endshape(0);
2020     if(textid>=0)
2021         endtext(obj);
2022
2023     int bitid = ++currentswfid;
2024     oldtag = tag;
2025     tag = swf_InsertTag(tag,ST_DEFINEBITSJPEG2);
2026     swf_SetU16(tag, bitid);
2027     if(swf_SetJPEGBits(tag, filename, jpegquality)<0) {
2028         swf_DeleteTag(tag);
2029         tag = oldtag;
2030         return -1;
2031     }
2032
2033     drawimage(obj, bitid, sizex, sizey, x1,y1,x2,y2,x3,y3,x4,y4);
2034     return bitid;
2035 }
2036
2037 int swfoutput_drawimagejpeg(struct swfoutput*obj, RGBA*mem, int sizex,int sizey, 
2038         double x1,double y1,
2039         double x2,double y2,
2040         double x3,double y3,
2041         double x4,double y4)
2042 {
2043     TAG*oldtag;
2044     JPEGBITS*jpeg;
2045
2046     if(shapeid>=0)
2047         endshape(0);
2048     if(textid>=0)
2049         endtext(obj);
2050
2051     int bitid = ++currentswfid;
2052     oldtag = tag;
2053     tag = swf_InsertTag(tag,ST_DEFINEBITSJPEG2);
2054     swf_SetU16(tag, bitid);
2055     swf_SetJPEGBits2(tag,sizex,sizey,mem,jpegquality);
2056     drawimage(obj, bitid, sizex, sizey, x1,y1,x2,y2,x3,y3,x4,y4);
2057     return bitid;
2058 }
2059
2060 int swfoutput_drawimagelossless(struct swfoutput*obj, RGBA*mem, int sizex,int sizey, 
2061         double x1,double y1,
2062         double x2,double y2,
2063         double x3,double y3,
2064         double x4,double y4)
2065 {
2066     TAG*oldtag;
2067     if(shapeid>=0)
2068         endshape(0);
2069     if(textid>=0)
2070         endtext(obj);
2071
2072     int bitid = ++currentswfid;
2073     oldtag = tag;
2074     tag = swf_InsertTag(tag,ST_DEFINEBITSLOSSLESS);
2075     swf_SetU16(tag, bitid);
2076     if(swf_SetLosslessBits(tag,sizex,sizey,mem, BMF_32BIT)<0) {
2077         swf_DeleteTag(tag);
2078         tag = oldtag;
2079         return -1;
2080     }
2081     
2082     drawimage(obj, bitid, sizex, sizey, x1,y1,x2,y2,x3,y3,x4,y4);
2083     return bitid;
2084 }
2085
2086 int swfoutput_drawimagelosslessN(struct swfoutput*obj, U8*mem, RGBA*pal, int sizex,int sizey, 
2087         double x1,double y1,
2088         double x2,double y2,
2089         double x3,double y3,
2090         double x4,double y4, int n)
2091 {
2092     TAG*oldtag;
2093     U8*mem2 = 0;
2094     if(shapeid>=0)
2095         endshape(0);
2096     if(textid>=0)
2097         endtext(obj);
2098
2099     if(sizex&3)
2100     { 
2101         /* SWF expects scanlines to be 4 byte aligned */
2102         int x,y;
2103         U8*ptr;
2104         mem2 = (U8*)malloc(BYTES_PER_SCANLINE(sizex)*sizey);
2105         ptr = mem2;
2106         for(y=0;y<sizey;y++)
2107         {
2108             for(x=0;x<sizex;x++)
2109                 *ptr++ = mem[y*sizex+x];
2110             ptr+= BYTES_PER_SCANLINE(sizex)-sizex;
2111         }
2112         mem = mem2;
2113     }
2114
2115     int bitid = ++currentswfid;
2116     oldtag = tag;
2117     tag = swf_InsertTag(tag,ST_DEFINEBITSLOSSLESS2);
2118     swf_SetU16(tag, bitid);
2119     if(swf_SetLosslessBitsIndexed(tag,sizex,sizey,mem, pal, n)<0) {
2120         swf_DeleteTag(tag);
2121         tag = oldtag;
2122         return -1;
2123     }
2124     if(mem2)
2125         free(mem2);
2126   
2127     drawimage(obj, bitid, sizex, sizey, x1,y1,x2,y2,x3,y3,x4,y4);
2128     return bitid;
2129 }
2130
2131 void swfoutput_drawimageagain(struct swfoutput*obj, int id, int sizex,int sizey, 
2132         double x1,double y1,
2133         double x2,double y2,
2134         double x3,double y3,
2135         double x4,double y4)
2136 {
2137     if(id<0) return;
2138     if(shapeid>=0)
2139         endshape(0);
2140     if(textid>=0)
2141         endtext(obj);
2142
2143     drawimage(obj, id, sizex, sizey, x1,y1,x2,y2,x3,y3,x4,y4);
2144 }
2145
2146 void swfoutput_setparameter(char*name, char*value)
2147 {
2148     if(!strcmp(name, "drawonlyshapes")) {
2149         drawonlyshapes = atoi(value);
2150     } else if(!strcmp(name, "ignoredraworder")) {
2151         ignoredraworder = atoi(value);
2152     } else if(!strcmp(name, "filloverlap")) {
2153         filloverlap = atoi(value);
2154     } else if(!strcmp(name, "linksopennewwindow")) {
2155         opennewwindow = atoi(value);
2156     } else if(!strcmp(name, "opennewwindow")) {
2157         opennewwindow = atoi(value);
2158     } else if(!strcmp(name, "storeallcharacters")) {
2159         storeallcharacters = atoi(value);
2160     } else if(!strcmp(name, "enablezlib")) {
2161         enablezlib = atoi(value);
2162     } else if(!strcmp(name, "insertstop")) {
2163         insertstoptag = atoi(value);
2164     } else if(!strcmp(name, "flashversion")) {
2165         flashversion = atoi(value);
2166     } else if(!strcmp(name, "minlinewidth")) {
2167         minlinewidth = atof(value);
2168     } else if(!strcmp(name, "jpegquality")) {
2169         int val = atoi(value);
2170         if(val<0) val=0;
2171         if(val>100) val=100;
2172         jpegquality = val;
2173     } else if(!strcmp(name, "splinequality")) {
2174         int v = atoi(value);
2175         v = 500-(v*5); // 100% = 0.25 pixel, 0% = 25 pixel
2176         if(v<1) v = 1;
2177         splinemaxerror = v;
2178     } else if(!strcmp(name, "fontquality")) {
2179         int v = atoi(value);
2180         v = 500-(v*5); // 100% = 0.25 pixel, 0% = 25 pixel
2181         if(v<1) v = 1;
2182         fontsplinemaxerror = v;
2183     } else {
2184         fprintf(stderr, "unknown parameter: %s (=%s)\n", name, value);
2185     }
2186 }
2187