b4c1229fb72b06b20cb54b61353d35cbab8853b5
[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 /* Notice: we can only put chars in the range -1639,1638 (-32768/20,32768/20).
1005    So if we set this value to high, the char coordinates will overflow.
1006    If we set it to low, however, the char positions will be inaccurate */
1007 #define FONT_INTERNAL_SIZE 4
1008
1009 /* process a character. */
1010 static int drawchar(struct swfoutput*obj, SWFFONT *swffont, char*character, int charnr, int u, swfmatrix*m)
1011 {
1012     if(!swffont) {
1013         msg("<warning> Font is NULL");
1014         return 0;
1015     }
1016
1017     int charid = getCharID(swffont, charnr, character, u); 
1018     
1019     if(charid<0) {
1020         msg("<warning> Didn't find character '%s' (c=%d,u=%d) in current charset (%s, %d characters)", 
1021                 FIXNULL(character),charnr, u, FIXNULL((char*)swffont->name), swffont->numchars);
1022         return 0;
1023     }
1024
1025     if(shapeid>=0)
1026         endshape(obj,0);
1027     if(textid<0)
1028         starttext(obj);
1029
1030     float x = m->m13;
1031     float y = m->m23;
1032     float det = ((m->m11*m->m22)-(m->m21*m->m12));
1033     if(fabs(det) < 0.0005) { 
1034         /* x direction equals y direction- the text is invisible */
1035         return 1;
1036     }
1037     det = 20*FONT_INTERNAL_SIZE / det;
1038
1039     SPOINT p;
1040     p.x = (SCOORD)((  x * m->m22 - y * m->m12)*det);
1041     p.y = (SCOORD)((- x * m->m21 + y * m->m11)*det);
1042
1043     putcharacter(obj, swffont->id, charid,p.x,p.y,FONT_INTERNAL_SIZE);
1044     swf_FontUseGlyph(swffont, charid);
1045     return 1;
1046
1047     /*else
1048     {
1049         SWF_OUTLINE*outline = font->getOutline(character, charnr);
1050         char* charname = character;
1051
1052         if(!outline) {
1053          msg("<warning> Didn't find character '%s' (%d) in current charset (%s)", 
1054                  FIXNULL(character),charnr,FIXNULL(font->getName()));
1055          return;
1056         }
1057         
1058         swfmatrix m2=*m;    
1059         m2.m11/=100;
1060         m2.m21/=100;
1061         m2.m12/=100;
1062         m2.m22/=100;
1063
1064         if(textid>=0)
1065             endtext(obj);
1066         if(shapeid<0)
1067             startshape(obj);
1068
1069         startFill();
1070
1071         int lf = fill;
1072         fill = 1;
1073         drawpath(tag, outline, &m2, 0);
1074         fill = lf;
1075     }*/
1076 }
1077
1078 static void endtext(swfoutput*obj)
1079 {
1080     if(textid<0)
1081         return;
1082
1083     tag = swf_InsertTag(tag,ST_DEFINETEXT);
1084     swf_SetU16(tag, textid);
1085
1086     SRECT r;
1087     r = getcharacterbbox(obj->swffont);
1088     
1089     swf_SetRect(tag,&r);
1090
1091     MATRIX m;
1092     swf_GetMatrix(0, &m);
1093     swf_SetMatrix(tag,&m);
1094
1095     putcharacters(tag);
1096     swf_SetU8(tag,0);
1097     tag = swf_InsertTag(tag,ST_PLACEOBJECT2);
1098     swf_ObjectPlace(tag,textid,/*depth*/depth++,&obj->fontmatrix,NULL,NULL);
1099     textid = -1;
1100 }
1101
1102
1103 /* draw a curved polygon. */
1104 void swfoutput_drawpath(swfoutput*output, SWF_OUTLINE*outline, 
1105                             struct swfmatrix*m)
1106 {
1107     if(textid>=0)
1108         endtext(output);
1109
1110     /* Multiple polygons in one shape don't overlap correctly, 
1111        so we better start a new shape here if the polygon is filled
1112      */
1113     if(shapeid>=0 && fill && !ignoredraworder) {
1114         endshape(output,0);
1115     }
1116
1117     if(shapeid<0)
1118         startshape(output);
1119
1120     if(!fill)
1121         stopFill();
1122     else
1123         startFill();
1124
1125     drawpath(output, outline,m, 0); 
1126 }
1127
1128 void swfoutput_drawpath2poly(struct swfoutput*output, SWF_OUTLINE*outline, struct swfmatrix*m, int line_join, int line_cap, double line_width, double miter_limit)
1129 {
1130     if(textid>=0)
1131         endtext(output);
1132     if(shapeid>=0)
1133         endshape(output,0);
1134     assert(shapeid<0);
1135     startshape(output);
1136     stopFill();
1137
1138     drawpath2poly(output, outline, m, 0, line_join, line_cap, line_width, miter_limit); 
1139 }
1140
1141 int getCharID(SWFFONT *font, int charnr, char *charname, int u)
1142 {
1143     int t;
1144     if(charname) {
1145         for(t=0;t<font->numchars;t++) {
1146             if(font->glyphnames[t] && !strcmp(font->glyphnames[t],charname)) {
1147                 msg("<debug> Char [%d,>%s<,%d] maps to %d\n", charnr, charname, u, t);
1148                 return t;
1149             }
1150         }
1151         /* if we didn't find the character, maybe
1152            we can find the capitalized version */
1153         for(t=0;t<font->numchars;t++) {
1154             if(font->glyphnames[t] && !strcasecmp(font->glyphnames[t],charname)) {
1155                 msg("<debug> Char [%d,>>%s<<,%d] maps to %d\n", charnr, charname, u, t);
1156                 return t;
1157             }
1158         }
1159     }
1160
1161     if(u>0) {
1162         /* try to use the unicode id */
1163         if(u>=0 && u<font->maxascii && font->ascii2glyph[u]>=0) {
1164             msg("<debug> Char [%d,%s,>%d<] maps to %d\n", charnr, charname, u, font->ascii2glyph[u]);
1165             return font->ascii2glyph[u];
1166         }
1167     }
1168
1169     if(charnr>=0 && charnr<font->numchars) {
1170         msg("<debug> Char [>%d<,%s,%d] maps to %d\n", charnr, charname, u, charnr);
1171         return charnr;
1172     }
1173
1174     if(font->encoding != FONT_ENCODING_UNICODE) {
1175         /* the following only works if the font encoding
1176            is US-ASCII based. It's needed for fonts which return broken unicode
1177            indices */
1178         if(charnr>=0 && charnr<font->maxascii && font->ascii2glyph[charnr]>=0) {
1179             msg("<debug> Char [>%d<,%s,%d] maps to %d\n", charnr, charname, u, font->ascii2glyph[charnr]);
1180             return font->ascii2glyph[charnr];
1181         }
1182     }
1183
1184     return -1;
1185 }
1186
1187
1188 /* set's the t1 font index of the font to use for swfoutput_drawchar(). */
1189 void swfoutput_setfont(struct swfoutput*obj, char*fontid, char*filename)
1190 {
1191     fontlist_t*last=0,*iterator;
1192     if(!fontid) {
1193         msg("<error> No fontid");
1194         return;
1195     }
1196
1197     if(obj->swffont && obj->swffont->name && !strcmp((char*)obj->swffont->name,fontid))
1198         return;
1199
1200     /* TODO: remove the need for this (enhance getcharacterbbox so that it can cope
1201              with multiple fonts */
1202     endtext(obj);
1203
1204     iterator = fontlist;
1205     while(iterator) {
1206         if(!strcmp((char*)iterator->swffont->name,fontid)) {
1207             obj->swffont = iterator->swffont; 
1208             return;
1209         }
1210         last = iterator;
1211         iterator = iterator->next;
1212     }
1213
1214     if(!filename) {
1215         msg("<error> No filename given for font- internal error?");
1216         return;
1217     }
1218
1219     swf_SetLoadFontParameters(64,/*skip unused*/0,/*full unicode*/1);
1220     SWFFONT*swffont = swf_LoadFont(filename);
1221
1222     if(swffont == 0) {
1223         msg("<warning> Couldn't load font %s (%s)", fontid, filename);
1224         swffont = swf_LoadFont(0);
1225     }
1226
1227     swf_FontSetID(swffont, ++currentswfid);
1228     
1229     if(screenloglevel >= LOGLEVEL_DEBUG)  {
1230         // print font information
1231         msg("<debug> Font %s (%s)",swffont->name, filename);
1232         msg("<debug> |   ID: %d", swffont->id);
1233         msg("<debug> |   Version: %d", swffont->version);
1234         msg("<debug> |   Name: %s", fontid);
1235         msg("<debug> |   Numchars: %d", swffont->numchars);
1236         msg("<debug> |   Maxascii: %d", swffont->maxascii);
1237         msg("<debug> |   Style: %d", swffont->style);
1238         msg("<debug> |   Encoding: %d", swffont->encoding);
1239         for(int iii=0; iii<swffont->numchars;iii++) {
1240             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, 
1241                     swffont->layout->bounds[iii].xmin/20.0,
1242                     swffont->layout->bounds[iii].ymin/20.0,
1243                     swffont->layout->bounds[iii].xmax/20.0,
1244                     swffont->layout->bounds[iii].ymax/20.0
1245                     );
1246             int t;
1247             for(t=0;t<swffont->maxascii;t++) {
1248                 if(swffont->ascii2glyph[t] == iii)
1249                     msg("<debug> | - maps to %d",t);
1250             }
1251         }
1252     }
1253
1254     /* set the font name to the ID we use here */
1255     if(swffont->name) free(swffont->name);
1256     swffont->name = (U8*)strdup(fontid);
1257
1258     iterator = new fontlist_t;
1259     iterator->swffont = swffont;
1260     iterator->next = 0;
1261
1262     if(last) 
1263         last->next = iterator;
1264     else 
1265         fontlist = iterator;
1266
1267     obj->swffont = swffont; 
1268 }
1269
1270 int swfoutput_queryfont(struct swfoutput*obj, char*fontid)
1271 {
1272     fontlist_t *iterator = fontlist;
1273     while(iterator) {
1274         if(!strcmp((char*)iterator->swffont->name,fontid))
1275             return 1;
1276         iterator = iterator->next;
1277     }
1278     return 0;
1279 }
1280
1281 /* set's the matrix which is to be applied to characters drawn by
1282    swfoutput_drawchar() */
1283 void swfoutput_setfontmatrix(struct swfoutput*obj,double m11,double m12,
1284                                                   double m21,double m22)
1285 {
1286     if(obj->fontm11 == m11 &&
1287        obj->fontm12 == m12 &&
1288        obj->fontm21 == m21 &&
1289        obj->fontm22 == m22)
1290         return;
1291    if(textid>=0)
1292         endtext(obj);
1293     obj->fontm11 = m11;
1294     obj->fontm12 = m12;
1295     obj->fontm21 = m21;
1296     obj->fontm22 = m22;
1297     
1298     MATRIX m;
1299     m.sx = (U32)(((obj->fontm11)*65536)/FONT_INTERNAL_SIZE); m.r1 = (U32)(((obj->fontm12)*65536)/FONT_INTERNAL_SIZE);
1300     m.r0 = (U32)(((obj->fontm21)*65536)/FONT_INTERNAL_SIZE); m.sy = (U32)(((obj->fontm22)*65536)/FONT_INTERNAL_SIZE); 
1301     m.tx = 0;
1302     m.ty = 0;
1303     obj->fontmatrix = m;
1304 }
1305
1306 /* draws a character at x,y. */
1307 int swfoutput_drawchar(struct swfoutput* obj,double x,double y,char*character, int charnr, int u) 
1308 {
1309     swfmatrix m;
1310     m.m11 = obj->fontm11;
1311     m.m12 = obj->fontm12;
1312     m.m21 = obj->fontm21;
1313     m.m22 = obj->fontm22;
1314     m.m13 = x;
1315     m.m23 = y;
1316     return drawchar(obj, obj->swffont, character, charnr, u, &m);
1317 }
1318
1319 /* initialize the swf writer */
1320 void swfoutput_init(struct swfoutput* obj, char*_filename, int x1, int y1, int x2, int y2)
1321 {
1322   RGBA rgb;
1323   SRECT r;
1324   memset(obj, 0, sizeof(struct swfoutput));
1325   filename = _filename;
1326   sizex = x2;
1327   sizey = y2;
1328
1329   msg("<verbose> initializing swf output for size %d*%d\n", sizex,sizey);
1330
1331   obj->swffont = 0;
1332   obj->drawmode = -1;
1333   
1334   memset(&swf,0x00,sizeof(SWF));
1335
1336   swf.fileVersion    = flashversion;
1337   swf.frameRate      = 0x0040; // 1 frame per 4 seconds
1338   swf.movieSize.xmin = 20*x1;
1339   swf.movieSize.ymin = 20*y1;
1340   swf.movieSize.xmax = 20*x2;
1341   swf.movieSize.ymax = 20*y2;
1342   
1343   depth = 1;
1344   
1345   swf.firstTag = swf_InsertTag(NULL,ST_SETBACKGROUNDCOLOR);
1346   tag = swf.firstTag;
1347   rgb.a = rgb.r = rgb.g = rgb.b = 0xff;
1348   swf_SetRGB(tag,&rgb);
1349
1350   if(1)/* add white rectangle */
1351   {
1352       SRECT r;
1353       SHAPE* s;
1354       int ls1=0,fs1=0;
1355       int shapeid = ++currentswfid;
1356       r.xmin = x1*20;
1357       r.ymin = y1*20;
1358       r.xmax = x2*20;
1359       r.ymax = y2*20;
1360       tag = swf_InsertTag(tag, ST_DEFINESHAPE);
1361       swf_ShapeNew(&s);
1362       fs1 = swf_ShapeAddSolidFillStyle(s, &rgb);
1363       swf_SetU16(tag,shapeid);
1364       swf_SetRect(tag,&r);
1365       swf_SetShapeHeader(tag,s);
1366       swf_ShapeSetAll(tag,s,x1*20,y1*20,ls1,fs1,0);
1367       swf_ShapeSetLine(tag,s,20*(x2-x1),0);
1368       swf_ShapeSetLine(tag,s,0,20*(y2-y1));
1369       swf_ShapeSetLine(tag,s,20*(x1-x2),0);
1370       swf_ShapeSetLine(tag,s,0,20*(y1-y2));
1371       swf_ShapeSetEnd(tag);
1372       swf_ShapeFree(s);
1373       tag = swf_InsertTag(tag, ST_PLACEOBJECT2);
1374       swf_ObjectPlace(tag,shapeid,depth++,0,0,0);
1375       tag = swf_InsertTag(tag, ST_PLACEOBJECT2);
1376       swf_ObjectPlaceClip(tag,shapeid,depth++,0,0,0,65535);
1377   }
1378
1379   if(flag_protected)
1380     tag = swf_InsertTag(tag, ST_PROTECT);
1381   
1382   startdepth = depth;
1383 }
1384
1385 void swfoutput_setprotected() //write PROTECT tag
1386 {
1387   flag_protected = 1;
1388 }
1389
1390 static void startshape(struct swfoutput*obj)
1391 {
1392   RGBA rgb;
1393   SRECT r;
1394
1395   if(textid>=0)
1396       endtext(obj);
1397
1398   tag = swf_InsertTag(tag,ST_DEFINESHAPE);
1399
1400   swf_ShapeNew(&shape);
1401   linestyleid = swf_ShapeAddLineStyle(shape,linewidth,&obj->strokergb);
1402   rgb.r = obj->fillrgb.r;
1403   rgb.g = obj->fillrgb.g;
1404   rgb.b = obj->fillrgb.b;
1405   fillstyleid = swf_ShapeAddSolidFillStyle(shape,&obj->fillrgb);
1406
1407   shapeid = ++currentswfid;
1408   swf_SetU16(tag,shapeid);  // ID
1409
1410   bboxrectpos = tag->len;
1411   r.xmin = 0;
1412   r.ymin = 0;
1413   r.xmax = 20*sizex;
1414   r.ymax = 20*sizey;
1415   swf_SetRect(tag,&r);
1416  
1417   memset(&bboxrect, 0, sizeof(bboxrect));
1418
1419   swf_SetShapeStyles(tag,shape);
1420   swf_ShapeCountBits(shape,NULL,NULL);
1421   swf_SetShapeBits(tag,shape);
1422
1423   /* TODO: do we really need this? */
1424   swf_ShapeSetAll(tag,shape,/*x*/0,/*y*/0,linestyleid,0,0);
1425   swflastx=swflasty=0;
1426   lastwasfill = 0;
1427   shapeisempty = 1;
1428 }
1429
1430 static void starttext(struct swfoutput*obj)
1431 {
1432   if(shapeid>=0)
1433       endshape(obj,0);
1434     
1435   textid = ++currentswfid;
1436
1437   swflastx=swflasty=0;
1438 }
1439             
1440
1441 /* TODO: move to ../lib/rfxswf */
1442 void changeRect(TAG*tag, int pos, SRECT*newrect)
1443 {
1444     /* determine length of old rect */
1445     tag->pos = pos;
1446     tag->readBit = 0;
1447     SRECT old;
1448     swf_GetRect(tag, &old);
1449     swf_ResetReadBits(tag);
1450     int pos_end = tag->pos;
1451
1452     int len = tag->len - pos_end;
1453     U8*data = (U8*)malloc(len);
1454     memcpy(data, &tag->data[pos_end], len);
1455     tag->writeBit = 0;
1456     tag->len = pos;
1457     swf_SetRect(tag, newrect);
1458     swf_SetBlock(tag, data, len);
1459     free(data);
1460     tag->pos = tag->readBit = 0;
1461 }
1462
1463 void cancelshape(swfoutput*obj)
1464 {
1465     /* delete old shape tag */
1466     TAG*todel = tag;
1467     tag = tag->prev;
1468     swf_DeleteTag(todel);
1469     shapeid = -1;
1470     bboxrectpos = -1;
1471 }
1472
1473 void fixAreas(swfoutput*obj)
1474 {
1475     if(!shapeisempty && fill &&
1476        (bboxrect.xmin == bboxrect.xmax ||
1477         bboxrect.ymin == bboxrect.ymax) &&
1478         minlinewidth >= 0.001
1479        ) {
1480         msg("<debug> Shape has size 0: width=%.2f height=%.2f",
1481                 (bboxrect.xmax-bboxrect.xmin)/20.0,
1482                 (bboxrect.ymax-bboxrect.ymin)/20.0
1483                 );
1484     
1485         SRECT r = bboxrect;
1486         
1487         if(r.xmin == r.xmax && r.ymin == r.ymax) {
1488             /* this thing comes down to a single dot- nothing to fix here */
1489             return;
1490         }
1491
1492         cancelshape(obj);
1493
1494         RGBA save_col = obj->strokergb;
1495         int  save_width = linewidth;
1496
1497         obj->strokergb = obj->fillrgb;
1498         linewidth = (int)(minlinewidth*20);
1499         if(linewidth==0) linewidth = 1;
1500         
1501         startshape(obj);
1502
1503         moveto(tag, r.xmin/20.0,r.ymin/20.0);
1504         lineto(tag, r.xmax/20.0,r.ymax/20.0);
1505
1506         obj->strokergb = save_col;
1507         linewidth = save_width;
1508     }
1509     
1510 }
1511
1512 static void endshape(swfoutput*obj, int clipdepth)
1513 {
1514     if(shapeid<0) 
1515         return;
1516
1517     if(!clipdepth)
1518         fixAreas(obj);
1519         
1520     if(shapeisempty ||
1521        (bboxrect.xmin == bboxrect.xmax && bboxrect.ymin == bboxrect.ymax)) 
1522     {
1523         // delete the shape again, we didn't do anything
1524         cancelshape(obj);
1525         return;
1526     }
1527     
1528     swf_ShapeSetEnd(tag);
1529
1530     changeRect(tag, bboxrectpos, &bboxrect);
1531
1532     tag = swf_InsertTag(tag,ST_PLACEOBJECT2);
1533     if(clipdepth)
1534         swf_ObjectPlaceClip(tag,shapeid,depth++,NULL,NULL,NULL,clipdepth);
1535     else
1536         swf_ObjectPlace(tag,shapeid,/*depth*/depth++,NULL,NULL,NULL);
1537
1538     shapeid = -1;
1539     bboxrectpos = -1;
1540 }
1541
1542 static void endpage(struct swfoutput*obj)
1543 {
1544     if(shapeid>=0)
1545       endshape(obj,0);
1546     if(textid>=0)
1547       endtext(obj);
1548     while(clippos)
1549         swfoutput_endclip(obj);
1550
1551     if(insertstoptag) {
1552         ActionTAG*atag=0;
1553         atag = action_Stop(atag);
1554         atag = action_End(atag);
1555         tag = swf_InsertTag(tag,ST_DOACTION);
1556         swf_ActionSet(tag,atag);
1557     }
1558     tag = swf_InsertTag(tag,ST_SHOWFRAME);
1559 }
1560
1561 void swfoutput_newpage(struct swfoutput*obj)
1562 {
1563     endpage(obj);
1564
1565     for(depth--;depth>=startdepth;depth--) {
1566         tag = swf_InsertTag(tag,ST_REMOVEOBJECT2);
1567         swf_SetU16(tag,depth);
1568     }
1569
1570     depth = startdepth;
1571 }
1572
1573 /* Perform cleaning up, complete the swf, and write it out. */
1574 void swfoutput_destroy(struct swfoutput* obj) 
1575 {
1576     endpage(obj);
1577     fontlist_t *tmp,*iterator = fontlist;
1578     while(iterator) {
1579         TAG*mtag = swf.firstTag;
1580         if(iterator->swffont) {
1581             mtag = swf_InsertTag(mtag, ST_DEFINEFONT2);
1582             /*if(!storeallcharacters)
1583                 swf_FontReduce(iterator->swffont);*/
1584             swf_FontSetDefine2(mtag, iterator->swffont);
1585             swf_FontFree(iterator->swffont);
1586         }
1587
1588         tmp = iterator;
1589         iterator = iterator->next;
1590         delete tmp;
1591     }
1592
1593     if(!filename) 
1594         return;
1595     if(filename)
1596      fi = open(filename, O_BINARY|O_CREAT|O_TRUNC|O_WRONLY, 0777);
1597     else
1598      fi = 1; // stdout
1599     
1600     if(fi<=0) {
1601      msg("<fatal> Could not create \"%s\". ", FIXNULL(filename));
1602      exit(1);
1603     }
1604  
1605     tag = swf_InsertTag(tag,ST_END);
1606
1607     if(enablezlib || flashversion>=6) {
1608       if FAILED(swf_WriteSWC(fi,&swf)) 
1609        msg("<error> WriteSWC() failed.\n");
1610     } else {
1611       if FAILED(swf_WriteSWF(fi,&swf)) 
1612        msg("<error> WriteSWF() failed.\n");
1613     }
1614
1615     if(filename)
1616      close(fi);
1617     msg("<notice> SWF written\n");
1618 }
1619
1620 void swfoutput_setdrawmode(swfoutput* obj, int mode)
1621 {
1622     obj->drawmode = mode;
1623     if(mode == DRAWMODE_FILL)
1624      fill = 1;
1625     else if(mode == DRAWMODE_EOFILL)
1626      fill = 1;
1627     else if(mode == DRAWMODE_STROKE)
1628      fill = 0;
1629     else if(mode == DRAWMODE_CLIP)
1630      fill = 1;
1631     else if(mode == DRAWMODE_EOCLIP)
1632      fill = 1;
1633 }
1634
1635 void swfoutput_setfillcolor(swfoutput* obj, u8 r, u8 g, u8 b, u8 a)
1636 {
1637     if(obj->fillrgb.r == r &&
1638        obj->fillrgb.g == g &&
1639        obj->fillrgb.b == b &&
1640        obj->fillrgb.a == a) return;
1641     if(shapeid>=0)
1642      endshape(obj,0);
1643
1644     obj->fillrgb.r = r;
1645     obj->fillrgb.g = g;
1646     obj->fillrgb.b = b;
1647     obj->fillrgb.a = a;
1648 }
1649
1650 void swfoutput_setstrokecolor(swfoutput* obj, u8 r, u8 g, u8 b, u8 a)
1651 {
1652     if(obj->strokergb.r == r &&
1653        obj->strokergb.g == g &&
1654        obj->strokergb.b == b &&
1655        obj->strokergb.a == a) return;
1656
1657     if(shapeid>=0)
1658      endshape(obj,0);
1659     obj->strokergb.r = r;
1660     obj->strokergb.g = g;
1661     obj->strokergb.b = b;
1662     obj->strokergb.a = a;
1663 }
1664
1665 void swfoutput_setlinewidth(struct swfoutput*obj, double _linewidth)
1666 {
1667     if(linewidth == (u16)(_linewidth*20))
1668         return;
1669
1670     if(shapeid>=0)
1671         endshape(obj,0);
1672     linewidth = (u16)(_linewidth*20);
1673 }
1674
1675
1676 void swfoutput_startclip(swfoutput*obj, SWF_OUTLINE*outline, struct swfmatrix*m)
1677 {
1678     if(textid>=0)
1679      endtext(obj);
1680     if(shapeid>=0)
1681      endshape(obj,0);
1682
1683     if(clippos >= 127)
1684     {
1685         msg("<warning> Too many clip levels.");
1686         clippos --;
1687     } 
1688     
1689     startshape(obj);
1690     int olddrawmode = obj->drawmode;
1691     swfoutput_setdrawmode(obj, DRAWMODE_CLIP);
1692     swfoutput_drawpath(obj, outline, m);
1693     swf_ShapeSetEnd(tag);
1694     swfoutput_setdrawmode(obj, olddrawmode);
1695
1696     tag = swf_InsertTag(tag,ST_PLACEOBJECT2);
1697     cliptags[clippos] = tag;
1698     clipshapes[clippos] = shapeid;
1699     clipdepths[clippos] = depth++;
1700     clippos++;
1701     shapeid = -1;
1702 }
1703
1704 void swfoutput_endclip(swfoutput*obj)
1705 {
1706     if(textid>=0)
1707         endtext(obj);
1708     if(shapeid>=0)
1709         endshape(obj,0);
1710
1711     if(!clippos) {
1712         msg("<error> Invalid end of clipping region");
1713         return;
1714     }
1715     clippos--;
1716     swf_ObjectPlaceClip(cliptags[clippos],clipshapes[clippos],clipdepths[clippos],NULL,NULL,NULL,depth++);
1717 }
1718
1719 static void drawlink(struct swfoutput*obj, ActionTAG*,ActionTAG*, swfcoord*points, char mouseover);
1720
1721 void swfoutput_linktourl(struct swfoutput*obj, char*url, swfcoord*points)
1722 {
1723     ActionTAG* actions;
1724     if(!strncmp("http://pdf2swf:", url, 15)) {
1725      char*tmp = strdup(url);
1726      int l = strlen(tmp);
1727      if(tmp[l-1] == '/')
1728         tmp[l-1] = 0;
1729      swfoutput_namedlink(obj, tmp+15, points);
1730      free(tmp);
1731      return;
1732     }
1733     
1734     if(shapeid>=0)
1735         endshape(obj,0);
1736     if(textid>=0)
1737         endtext(obj);
1738     
1739     if(opennewwindow)
1740       actions = action_GetUrl(0, url, "_parent");
1741     else
1742       actions = action_GetUrl(0, url, "_this");
1743     actions = action_End(actions);
1744     
1745     drawlink(obj, actions, 0, points,0);
1746 }
1747 void swfoutput_linktopage(struct swfoutput*obj, int page, swfcoord*points)
1748 {
1749     ActionTAG* actions;
1750
1751     if(shapeid>=0)
1752         endshape(obj,0);
1753     if(textid>=0)
1754         endtext(obj);
1755    
1756       actions = action_GotoFrame(0, page);
1757       actions = action_End(actions);
1758
1759     drawlink(obj, actions, 0, points,0);
1760 }
1761
1762 /* Named Links (a.k.a. Acrobatmenu) are used to implement various gadgets
1763    of the viewer objects, like subtitles, index elements etc.
1764 */
1765 void swfoutput_namedlink(struct swfoutput*obj, char*name, swfcoord*points)
1766 {
1767     ActionTAG *actions1,*actions2;
1768     char*tmp = strdup(name);
1769     char mouseover = 1;
1770
1771     if(shapeid>=0)
1772         endshape(obj,0);
1773     if(textid>=0)
1774         endtext(obj);
1775
1776     if(!strncmp(tmp, "call:", 5))
1777     {
1778         char*x = strchr(&tmp[5], ':');
1779         if(!x) {
1780             actions1 = action_PushInt(0, 0); //number of parameters (0)
1781             actions1 = action_PushString(actions1, &tmp[5]); //function name
1782             actions1 = action_CallFunction(actions1);
1783         } else {
1784             *x = 0;
1785             actions1 = action_PushString(0, x+1); //parameter
1786             actions1 = action_PushInt(actions1, 1); //number of parameters (1)
1787             actions1 = action_PushString(actions1, &tmp[5]); //function name
1788             actions1 = action_CallFunction(actions1);
1789         }
1790         actions2 = action_End(0);
1791         mouseover = 0;
1792     }
1793     else
1794     {
1795         actions1 = action_PushString(0, "/:subtitle");
1796         actions1 = action_PushString(actions1, name);
1797         actions1 = action_SetVariable(actions1);
1798         actions1 = action_End(actions1);
1799
1800         actions2 = action_PushString(0, "/:subtitle");
1801         actions2 = action_PushString(actions2, "");
1802         actions2 = action_SetVariable(actions2);
1803         actions2 = action_End(actions2);
1804     }
1805
1806     drawlink(obj, actions1, actions2, points,mouseover);
1807
1808     swf_ActionFree(actions1);
1809     swf_ActionFree(actions2);
1810     free(tmp);
1811 }
1812
1813 static void drawlink(struct swfoutput*obj, ActionTAG*actions1, ActionTAG*actions2, swfcoord*points, char mouseover)
1814 {
1815     RGBA rgb;
1816     SRECT r;
1817     int lsid=0;
1818     int fsid;
1819     struct plotxy p1,p2,p3,p4;
1820     int myshapeid;
1821     int myshapeid2;
1822     double xmin,ymin;
1823     double xmax=xmin=points[0].x,ymax=ymin=points[0].y;
1824     double posx = 0;
1825     double posy = 0;
1826     int t;
1827     int buttonid = ++currentswfid;
1828     for(t=1;t<4;t++)
1829     {
1830         if(points[t].x>xmax) xmax=points[t].x;
1831         if(points[t].y>ymax) ymax=points[t].y;
1832         if(points[t].x<xmin) xmin=points[t].x;
1833         if(points[t].y<ymin) ymin=points[t].y;
1834     }
1835    
1836     p1.x=points[0].x; p1.y=points[0].y; p2.x=points[1].x; p2.y=points[1].y; 
1837     p3.x=points[2].x; p3.y=points[2].y; p4.x=points[3].x; p4.y=points[3].y;
1838    
1839     /* the following code subtracts the upper left edge from all coordinates,
1840        and set's posx,posy so that ST_PLACEOBJECT is used with a matrix.
1841        Necessary for preprocessing with swfcombine. */
1842     posx = xmin; posy = ymin;
1843     p1.x-=posx;p2.x-=posx;p3.x-=posx;p4.x-=posx;
1844     p1.y-=posy;p2.y-=posy;p3.y-=posy;p4.y-=posy;
1845     xmin -= posx; ymin -= posy;
1846     xmax -= posx; ymax -= posy;
1847     
1848     /* shape */
1849     myshapeid = ++currentswfid;
1850     tag = swf_InsertTag(tag,ST_DEFINESHAPE3);
1851     swf_ShapeNew(&shape);
1852     rgb.r = rgb.b = rgb.a = rgb.g = 0; 
1853     fsid = swf_ShapeAddSolidFillStyle(shape,&rgb);
1854     swf_SetU16(tag, myshapeid);
1855     r.xmin = (int)(xmin*20);
1856     r.ymin = (int)(ymin*20);
1857     r.xmax = (int)(xmax*20);
1858     r.ymax = (int)(ymax*20);
1859     swf_SetRect(tag,&r);
1860     swf_SetShapeStyles(tag,shape);
1861     swf_ShapeCountBits(shape,NULL,NULL);
1862     swf_SetShapeBits(tag,shape);
1863     swf_ShapeSetAll(tag,shape,/*x*/0,/*y*/0,0,fsid,0);
1864     swflastx = swflasty = 0;
1865     moveto(tag, p1);
1866     lineto(tag, p2);
1867     lineto(tag, p3);
1868     lineto(tag, p4);
1869     lineto(tag, p1);
1870     swf_ShapeSetEnd(tag);
1871
1872     /* shape2 */
1873     myshapeid2 = ++currentswfid;
1874     tag = swf_InsertTag(tag,ST_DEFINESHAPE3);
1875     swf_ShapeNew(&shape);
1876     rgb.r = rgb.b = rgb.a = rgb.g = 255;
1877     rgb.a = 40;
1878     fsid = swf_ShapeAddSolidFillStyle(shape,&rgb);
1879     swf_SetU16(tag, myshapeid2);
1880     r.xmin = (int)(xmin*20);
1881     r.ymin = (int)(ymin*20);
1882     r.xmax = (int)(xmax*20);
1883     r.ymax = (int)(ymax*20);
1884     swf_SetRect(tag,&r);
1885     swf_SetShapeStyles(tag,shape);
1886     swf_ShapeCountBits(shape,NULL,NULL);
1887     swf_SetShapeBits(tag,shape);
1888     swf_ShapeSetAll(tag,shape,/*x*/0,/*y*/0,0,fsid,0);
1889     swflastx = swflasty = 0;
1890     moveto(tag, p1);
1891     lineto(tag, p2);
1892     lineto(tag, p3);
1893     lineto(tag, p4);
1894     lineto(tag, p1);
1895     swf_ShapeSetEnd(tag);
1896
1897     if(!mouseover)
1898     {
1899         tag = swf_InsertTag(tag,ST_DEFINEBUTTON);
1900         swf_SetU16(tag,buttonid); //id
1901         swf_ButtonSetFlags(tag, 0); //menu=no
1902         swf_ButtonSetRecord(tag,0x01,myshapeid,depth,0,0);
1903         swf_ButtonSetRecord(tag,0x02,myshapeid2,depth,0,0);
1904         swf_ButtonSetRecord(tag,0x04,myshapeid2,depth,0,0);
1905         swf_ButtonSetRecord(tag,0x08,myshapeid,depth,0,0);
1906         swf_SetU8(tag,0);
1907         swf_ActionSet(tag,actions1);
1908         swf_SetU8(tag,0);
1909     }
1910     else
1911     {
1912         tag = swf_InsertTag(tag,ST_DEFINEBUTTON2);
1913         swf_SetU16(tag,buttonid); //id
1914         swf_ButtonSetFlags(tag, 0); //menu=no
1915         swf_ButtonSetRecord(tag,0x01,myshapeid,depth,0,0);
1916         swf_ButtonSetRecord(tag,0x02,myshapeid2,depth,0,0);
1917         swf_ButtonSetRecord(tag,0x04,myshapeid2,depth,0,0);
1918         swf_ButtonSetRecord(tag,0x08,myshapeid,depth,0,0);
1919         swf_SetU8(tag,0); // end of button records
1920         swf_ButtonSetCondition(tag, BC_IDLE_OVERUP);
1921         swf_ActionSet(tag,actions1);
1922         if(actions2) {
1923             swf_ButtonSetCondition(tag, BC_OVERUP_IDLE);
1924             swf_ActionSet(tag,actions2);
1925             swf_SetU8(tag,0);
1926             swf_ButtonPostProcess(tag, 2);
1927         } else {
1928             swf_SetU8(tag,0);
1929             swf_ButtonPostProcess(tag, 1);
1930         }
1931     }
1932     
1933     tag = swf_InsertTag(tag,ST_PLACEOBJECT2);
1934
1935     if(posx!=0 || posy!=0) {
1936         MATRIX m;
1937         swf_GetMatrix(0,&m);
1938         m.tx = (int)(posx*20);
1939         m.ty = (int)(posy*20);
1940         swf_ObjectPlace(tag, buttonid, depth++,&m,0,0);
1941     }
1942     else {
1943         swf_ObjectPlace(tag, buttonid, depth++,0,0,0);
1944     }
1945 }
1946
1947 static void drawimage(struct swfoutput*obj, int bitid, int sizex,int sizey, 
1948         double x1,double y1,
1949         double x2,double y2,
1950         double x3,double y3,
1951         double x4,double y4)
1952 {
1953     RGBA rgb;
1954     SRECT r;
1955     int lsid=0;
1956     int fsid;
1957     struct plotxy p1,p2,p3,p4;
1958     int myshapeid;
1959     double xmax=x1,ymax=y1,xmin=x1,ymin=y1;
1960     if(x2>xmax) xmax=x2;
1961     if(y2>ymax) ymax=y2;
1962     if(x2<xmin) xmin=x2;
1963     if(y2<ymin) ymin=y2;
1964     if(x3>xmax) xmax=x3;
1965     if(y3>ymax) ymax=y3;
1966     if(x3<xmin) xmin=x3;
1967     if(y3<ymin) ymin=y3;
1968     if(x4>xmax) xmax=x4;
1969     if(y4>ymax) ymax=y4;
1970     if(x4<xmin) xmin=x4;
1971     if(y4<ymin) ymin=y4;
1972     p1.x=x1; p1.y=y1;
1973     p2.x=x2; p2.y=y2;
1974     p3.x=x3; p3.y=y3;
1975     p4.x=x4; p4.y=y4;
1976
1977     {p1.x = (int)(p1.x*20)/20.0;
1978      p1.y = (int)(p1.y*20)/20.0;
1979      p2.x = (int)(p2.x*20)/20.0;
1980      p2.y = (int)(p2.y*20)/20.0;
1981      p3.x = (int)(p3.x*20)/20.0;
1982      p3.y = (int)(p3.y*20)/20.0;
1983      p4.x = (int)(p4.x*20)/20.0;
1984      p4.y = (int)(p4.y*20)/20.0;}
1985     
1986     MATRIX m;
1987     m.sx = (int)(65536*20*(p4.x-p1.x)/sizex);
1988     m.r1 = -(int)(65536*20*(p4.y-p1.y)/sizex);
1989     m.r0 = (int)(65536*20*(p1.x-p2.x)/sizey);
1990     m.sy = -(int)(65536*20*(p1.y-p2.y)/sizey);
1991
1992     m.tx = (int)(p1.x*20);
1993     m.ty = (int)(p1.y*20);
1994   
1995     /* shape */
1996     myshapeid = ++currentswfid;
1997     tag = swf_InsertTag(tag,ST_DEFINESHAPE);
1998     swf_ShapeNew(&shape);
1999     //lsid = ShapeAddLineStyle(shape,linewidth,&obj->strokergb);
2000     //fsid = ShapeAddSolidFillStyle(shape,&obj->fillrgb);
2001     fsid = swf_ShapeAddBitmapFillStyle(shape,&m,bitid,1);
2002     swf_SetU16(tag, myshapeid);
2003     r.xmin = (int)(xmin*20);
2004     r.ymin = (int)(ymin*20);
2005     r.xmax = (int)(xmax*20);
2006     r.ymax = (int)(ymax*20);
2007     swf_SetRect(tag,&r);
2008     swf_SetShapeStyles(tag,shape);
2009     swf_ShapeCountBits(shape,NULL,NULL);
2010     swf_SetShapeBits(tag,shape);
2011     swf_ShapeSetAll(tag,shape,/*x*/0,/*y*/0,lsid,fsid,0);
2012     swflastx = swflasty = 0;
2013     moveto(tag, p1);
2014     lineto(tag, p2);
2015     lineto(tag, p3);
2016     lineto(tag, p4);
2017     lineto(tag, p1);
2018     /*
2019     ShapeMoveTo  (tag, shape, (int)(x1*20),(int)(y1*20));
2020     ShapeSetLine (tag, shape, (int)(x1*20);
2021     ShapeSetLine (tag, shape, x*20,0);
2022     ShapeSetLine (tag, shape, 0,-y*20);
2023     ShapeSetLine (tag, shape, -x*20,0);*/
2024     swf_ShapeSetEnd(tag);
2025
2026     /* instance */
2027     tag = swf_InsertTag(tag,ST_PLACEOBJECT2);
2028     swf_ObjectPlace(tag,myshapeid,/*depth*/depth++,NULL,NULL,NULL);
2029 }
2030
2031 int swfoutput_drawimagejpeg_old(struct swfoutput*obj, char*filename, int sizex,int sizey, 
2032         double x1,double y1,
2033         double x2,double y2,
2034         double x3,double y3,
2035         double x4,double y4)
2036 {
2037     TAG*oldtag;
2038     if(shapeid>=0)
2039         endshape(obj,0);
2040     if(textid>=0)
2041         endtext(obj);
2042
2043     int bitid = ++currentswfid;
2044     oldtag = tag;
2045     tag = swf_InsertTag(tag,ST_DEFINEBITSJPEG2);
2046     swf_SetU16(tag, bitid);
2047     if(swf_SetJPEGBits(tag, filename, jpegquality)<0) {
2048         swf_DeleteTag(tag);
2049         tag = oldtag;
2050         return -1;
2051     }
2052
2053     drawimage(obj, bitid, sizex, sizey, x1,y1,x2,y2,x3,y3,x4,y4);
2054     return bitid;
2055 }
2056
2057 int swfoutput_drawimagejpeg(struct swfoutput*obj, RGBA*mem, int sizex,int sizey, 
2058         double x1,double y1,
2059         double x2,double y2,
2060         double x3,double y3,
2061         double x4,double y4)
2062 {
2063     TAG*oldtag;
2064     JPEGBITS*jpeg;
2065
2066     if(shapeid>=0)
2067         endshape(obj,0);
2068     if(textid>=0)
2069         endtext(obj);
2070
2071     int bitid = ++currentswfid;
2072     oldtag = tag;
2073     tag = swf_InsertTag(tag,ST_DEFINEBITSJPEG2);
2074     swf_SetU16(tag, bitid);
2075     swf_SetJPEGBits2(tag,sizex,sizey,mem,jpegquality);
2076     drawimage(obj, bitid, sizex, sizey, x1,y1,x2,y2,x3,y3,x4,y4);
2077     return bitid;
2078 }
2079
2080 int swfoutput_drawimagelossless(struct swfoutput*obj, RGBA*mem, int sizex,int sizey, 
2081         double x1,double y1,
2082         double x2,double y2,
2083         double x3,double y3,
2084         double x4,double y4)
2085 {
2086     TAG*oldtag;
2087     if(shapeid>=0)
2088         endshape(obj,0);
2089     if(textid>=0)
2090         endtext(obj);
2091
2092     int bitid = ++currentswfid;
2093     oldtag = tag;
2094     tag = swf_InsertTag(tag,ST_DEFINEBITSLOSSLESS);
2095     swf_SetU16(tag, bitid);
2096     if(swf_SetLosslessBits(tag,sizex,sizey,mem, BMF_32BIT)<0) {
2097         swf_DeleteTag(tag);
2098         tag = oldtag;
2099         return -1;
2100     }
2101     
2102     drawimage(obj, bitid, sizex, sizey, x1,y1,x2,y2,x3,y3,x4,y4);
2103     return bitid;
2104 }
2105
2106 int swfoutput_drawimagelosslessN(struct swfoutput*obj, U8*mem, RGBA*pal, int sizex,int sizey, 
2107         double x1,double y1,
2108         double x2,double y2,
2109         double x3,double y3,
2110         double x4,double y4, int n)
2111 {
2112     TAG*oldtag;
2113     U8*mem2 = 0;
2114     if(shapeid>=0)
2115         endshape(obj,0);
2116     if(textid>=0)
2117         endtext(obj);
2118
2119     if(sizex&3)
2120     { 
2121         /* SWF expects scanlines to be 4 byte aligned */
2122         int x,y;
2123         U8*ptr;
2124         mem2 = (U8*)malloc(BYTES_PER_SCANLINE(sizex)*sizey);
2125         ptr = mem2;
2126         for(y=0;y<sizey;y++)
2127         {
2128             for(x=0;x<sizex;x++)
2129                 *ptr++ = mem[y*sizex+x];
2130             ptr+= BYTES_PER_SCANLINE(sizex)-sizex;
2131         }
2132         mem = mem2;
2133     }
2134
2135     int bitid = ++currentswfid;
2136     oldtag = tag;
2137     tag = swf_InsertTag(tag,ST_DEFINEBITSLOSSLESS2);
2138     swf_SetU16(tag, bitid);
2139     if(swf_SetLosslessBitsIndexed(tag,sizex,sizey,mem, pal, n)<0) {
2140         swf_DeleteTag(tag);
2141         tag = oldtag;
2142         return -1;
2143     }
2144     if(mem2)
2145         free(mem2);
2146   
2147     drawimage(obj, bitid, sizex, sizey, x1,y1,x2,y2,x3,y3,x4,y4);
2148     return bitid;
2149 }
2150
2151 void swfoutput_drawimageagain(struct swfoutput*obj, int id, int sizex,int sizey, 
2152         double x1,double y1,
2153         double x2,double y2,
2154         double x3,double y3,
2155         double x4,double y4)
2156 {
2157     if(id<0) return;
2158     if(shapeid>=0)
2159         endshape(obj,0);
2160     if(textid>=0)
2161         endtext(obj);
2162
2163     drawimage(obj, id, sizex, sizey, x1,y1,x2,y2,x3,y3,x4,y4);
2164 }
2165
2166 void swfoutput_setparameter(char*name, char*value)
2167 {
2168     if(!strcmp(name, "drawonlyshapes")) {
2169         drawonlyshapes = atoi(value);
2170     } else if(!strcmp(name, "ignoredraworder")) {
2171         ignoredraworder = atoi(value);
2172     } else if(!strcmp(name, "filloverlap")) {
2173         filloverlap = atoi(value);
2174     } else if(!strcmp(name, "linksopennewwindow")) {
2175         opennewwindow = atoi(value);
2176     } else if(!strcmp(name, "opennewwindow")) {
2177         opennewwindow = atoi(value);
2178     } else if(!strcmp(name, "storeallcharacters")) {
2179         storeallcharacters = atoi(value);
2180     } else if(!strcmp(name, "enablezlib")) {
2181         enablezlib = atoi(value);
2182     } else if(!strcmp(name, "insertstop")) {
2183         insertstoptag = atoi(value);
2184     } else if(!strcmp(name, "flashversion")) {
2185         flashversion = atoi(value);
2186     } else if(!strcmp(name, "minlinewidth")) {
2187         minlinewidth = atof(value);
2188     } else if(!strcmp(name, "jpegquality")) {
2189         int val = atoi(value);
2190         if(val<0) val=0;
2191         if(val>100) val=100;
2192         jpegquality = val;
2193     } else if(!strcmp(name, "splinequality")) {
2194         int v = atoi(value);
2195         v = 500-(v*5); // 100% = 0.25 pixel, 0% = 25 pixel
2196         if(v<1) v = 1;
2197         splinemaxerror = v;
2198     } else if(!strcmp(name, "fontquality")) {
2199         int v = atoi(value);
2200         v = 500-(v*5); // 100% = 0.25 pixel, 0% = 25 pixel
2201         if(v<1) v = 1;
2202         fontsplinemaxerror = v;
2203     } else {
2204         fprintf(stderr, "unknown parameter: %s (=%s)\n", name, value);
2205     }
2206 }
2207