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