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