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