a007e801a6a8c09d32c827567079c800c646cc8f
[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
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         swf_FontUseGlyph(swffont, charid);
945         return 1;
946     }
947     /*else
948     {
949         SWF_OUTLINE*outline = font->getOutline(character, charnr);
950         char* charname = character;
951
952         if(!outline) {
953          msg("<warning> Didn't find character '%s' (%d) in current charset (%s)", 
954                  FIXNULL(character),charnr,FIXNULL(font->getName()));
955          return;
956         }
957         
958         swfmatrix m2=*m;    
959         m2.m11/=100;
960         m2.m21/=100;
961         m2.m12/=100;
962         m2.m22/=100;
963
964         if(textid>=0)
965             endtext();
966         if(shapeid<0)
967             startshape(obj);
968
969         startFill();
970
971         int lf = fill;
972         fill = 1;
973         drawpath(tag, outline, &m2, 0);
974         fill = lf;
975     }*/
976 }
977
978 /* draw a curved polygon. */
979 void swfoutput_drawpath(swfoutput*output, SWF_OUTLINE*outline, 
980                             struct swfmatrix*m)
981 {
982     if(textid>=0)
983         endtext();
984
985     /* Multiple polygons in one shape don't overlap correctly, 
986        so we better start a new shape here if the polygon is filled
987      */
988     if(shapeid>=0 && fill && !ignoredraworder) {
989         endshape();
990     }
991
992     if(shapeid<0)
993         startshape(output);
994
995     if(!fill)
996         stopFill();
997     else
998         startFill();
999
1000     drawpath(output, outline,m, 0); 
1001 }
1002
1003 void swfoutput_drawpath2poly(struct swfoutput*output, SWF_OUTLINE*outline, struct swfmatrix*m, int line_join, int line_cap, double line_width, double miter_limit)
1004 {
1005     if(textid>=0)
1006         endtext();
1007     if(shapeid>=0)
1008         endshape();
1009     assert(shapeid<0);
1010     startshape(output);
1011     stopFill();
1012
1013     drawpath2poly(output, outline, m, 0, line_join, line_cap, line_width, miter_limit); 
1014 }
1015
1016 int getCharID(SWFFONT *font, int charnr, char *charname, int u)
1017 {
1018     int t;
1019     if(charname) {
1020         for(t=0;t<font->numchars;t++) {
1021             if(font->glyphnames[t] && !strcmp(font->glyphnames[t],charname)) {
1022                 return t;
1023             }
1024         }
1025         /* if we didn't find the character, maybe
1026            we can find the capitalized version */
1027         for(t=0;t<font->numchars;t++) {
1028             if(font->glyphnames[t] && !strcasecmp(font->glyphnames[t],charname)) {
1029                 return t;
1030             }
1031         }
1032     }
1033
1034     if(u>0) {
1035         if(u>=font->maxascii)
1036             msg("<debug> u=%d, font->maxascii=%d",u,font->maxascii);
1037         else
1038             msg("<debug> u=%d, font->maxascii=%d ascci2glyph[%d]=%d",u,font->maxascii,u,font->ascii2glyph[u]);
1039
1040         /* try to use the unicode id */
1041         if(u>=0 && u<font->maxascii && font->ascii2glyph[u]>=0) {
1042             return font->ascii2glyph[u];
1043         }
1044     }
1045
1046     if(charnr>=0 && charnr<font->numchars) {
1047         return charnr;
1048     }
1049
1050     /* the following is technically wrong, and only works if the font encoding
1051        is US-ASCII based. It's needed for fonts which return broken unicode
1052        indices */
1053 /*    if(charnr>=0 && charnr<font->maxascii && font->ascii2glyph[charnr]>=0) {
1054         return font->ascii2glyph[charnr];
1055     }*/
1056
1057     return -1;
1058 }
1059
1060
1061 /* set's the t1 font index of the font to use for swfoutput_drawchar(). */
1062 void swfoutput_setfont(struct swfoutput*obj, char*fontid, char*filename)
1063 {
1064     fontlist_t*last=0,*iterator;
1065     if(!fontid) {
1066         msg("<error> No fontid");
1067         return;
1068     }
1069
1070     if(obj->swffont && obj->swffont->name && !strcmp((char*)obj->swffont->name,fontid))
1071         return;
1072
1073     iterator = fontlist;
1074     while(iterator) {
1075         if(!strcmp((char*)iterator->swffont->name,fontid)) {
1076             obj->swffont = iterator->swffont; 
1077             return;
1078         }
1079         last = iterator;
1080         iterator = iterator->next;
1081     }
1082
1083     if(!filename) {
1084         msg("<error> No filename given for font- internal error?");
1085         return;
1086     }
1087
1088     swf_SetLoadFontParameters(0,/*skip unused*/0,/*full unicode*/1);
1089     SWFFONT*swffont = swf_LoadFont(filename);
1090
1091     if(swffont == 0) {
1092         msg("<warning> Couldn't load font %s (%s)", fontid, filename);
1093         swffont = swf_LoadFont(0);
1094     }
1095
1096     swf_FontSetID(swffont, ++currentswfid);
1097     
1098     if(screenloglevel >= LOGLEVEL_DEBUG)  {
1099         // print font information
1100         msg("<debug> Font %s (%s)",swffont->name, filename);
1101         msg("<debug> |   ID: %d", swffont->id);
1102         msg("<debug> |   Version: %d", swffont->version);
1103         msg("<debug> |   Name: %s", fontid);
1104         msg("<debug> |   Numchars: %d", swffont->numchars);
1105         msg("<debug> |   Maxascii: %d", swffont->maxascii);
1106         msg("<debug> |   Style: %d", swffont->style);
1107         msg("<debug> |   Encoding: %d", swffont->encoding);
1108         for(int iii=0; iii<swffont->numchars;iii++) {
1109             msg("<debug> |   Glyph %d) name=%s, unicode=%d\n", iii, swffont->glyphnames[iii], swffont->glyph2ascii[iii]);
1110         }
1111     }
1112
1113     /* set the font name to the ID we use here */
1114     if(swffont->name) free(swffont->name);
1115     swffont->name = (U8*)strdup(fontid);
1116
1117     iterator = new fontlist_t;
1118     iterator->swffont = swffont;
1119     iterator->next = 0;
1120
1121     if(last) 
1122         last->next = iterator;
1123     else 
1124         fontlist = iterator;
1125
1126     obj->swffont = swffont; 
1127 }
1128
1129 int swfoutput_queryfont(struct swfoutput*obj, char*fontid)
1130 {
1131     fontlist_t *iterator = fontlist;
1132     while(iterator) {
1133         if(!strcmp((char*)iterator->swffont->name,fontid))
1134             return 1;
1135         iterator = iterator->next;
1136     }
1137     return 0;
1138 }
1139
1140 /* set's the matrix which is to be applied to characters drawn by
1141    swfoutput_drawchar() */
1142 void swfoutput_setfontmatrix(struct swfoutput*obj,double m11,double m12,
1143                                                   double m21,double m22)
1144 {
1145     if(obj->fontm11 == m11 &&
1146        obj->fontm12 == m12 &&
1147        obj->fontm21 == m21 &&
1148        obj->fontm22 == m22)
1149         return;
1150 //    if(textid>=0)
1151 //      endtext();
1152     obj->fontm11 = m11;
1153     obj->fontm12 = m12;
1154     obj->fontm21 = m21;
1155     obj->fontm22 = m22;
1156 }
1157
1158 /* draws a character at x,y. */
1159 int swfoutput_drawchar(struct swfoutput* obj,double x,double y,char*character, int charnr, int u) 
1160 {
1161     swfmatrix m;
1162     m.m11 = obj->fontm11;
1163     m.m12 = obj->fontm12;
1164     m.m21 = obj->fontm21;
1165     m.m22 = obj->fontm22;
1166     m.m13 = x;
1167     m.m23 = y;
1168     return drawchar(obj, obj->swffont, character, charnr, u, &m);
1169 }
1170
1171 /* initialize the swf writer */
1172 void swfoutput_init(struct swfoutput* obj, char*_filename, int x1, int y1, int x2, int y2)
1173 {
1174   RGBA rgb;
1175   SRECT r;
1176   memset(obj, 0, sizeof(struct swfoutput));
1177   filename = _filename;
1178   sizex = x2;
1179   sizey = y2;
1180
1181   msg("<verbose> initializing swf output for size %d*%d\n", sizex,sizey);
1182
1183   obj->swffont = 0;
1184   obj->drawmode = -1;
1185   
1186   memset(&swf,0x00,sizeof(SWF));
1187
1188   swf.fileVersion    = flashversion;
1189   swf.frameRate      = 0x0040; // 1 frame per 4 seconds
1190   swf.movieSize.xmin = 20*x1;
1191   swf.movieSize.ymin = 20*y1;
1192   swf.movieSize.xmax = 20*x2;
1193   swf.movieSize.ymax = 20*y2;
1194   
1195   depth = 1;
1196   
1197   swf.firstTag = swf_InsertTag(NULL,ST_SETBACKGROUNDCOLOR);
1198   tag = swf.firstTag;
1199   rgb.a = rgb.r = rgb.g = rgb.b = 0xff;
1200   swf_SetRGB(tag,&rgb);
1201
1202   if(1)/* add white rectangle */
1203   {
1204       SRECT r;
1205       SHAPE* s;
1206       int ls1=0,fs1=0;
1207       int shapeid = ++currentswfid;
1208       r.xmin = x1*20;
1209       r.ymin = y1*20;
1210       r.xmax = x2*20;
1211       r.ymax = y2*20;
1212       tag = swf_InsertTag(tag, ST_DEFINESHAPE);
1213       swf_ShapeNew(&s);
1214       fs1 = swf_ShapeAddSolidFillStyle(s, &rgb);
1215       swf_SetU16(tag,shapeid);
1216       swf_SetRect(tag,&r);
1217       swf_SetShapeHeader(tag,s);
1218       swf_ShapeSetAll(tag,s,x1*20,y1*20,ls1,fs1,0);
1219       swf_ShapeSetLine(tag,s,20*(x2-x1),0);
1220       swf_ShapeSetLine(tag,s,0,20*(y2-y1));
1221       swf_ShapeSetLine(tag,s,20*(x1-x2),0);
1222       swf_ShapeSetLine(tag,s,0,20*(y1-y2));
1223       swf_ShapeSetEnd(tag);
1224       swf_ShapeFree(s);
1225       tag = swf_InsertTag(tag, ST_PLACEOBJECT2);
1226       swf_ObjectPlace(tag,shapeid,depth++,0,0,0);
1227       tag = swf_InsertTag(tag, ST_PLACEOBJECT2);
1228       swf_ObjectPlaceClip(tag,shapeid,depth++,0,0,0,65535);
1229   }
1230
1231   if(flag_protected)
1232     tag = swf_InsertTag(tag, ST_PROTECT);
1233   
1234   startdepth = depth;
1235 }
1236
1237 void swfoutput_setprotected() //write PROTECT tag
1238 {
1239   flag_protected = 1;
1240 }
1241
1242 static void startshape(struct swfoutput*obj)
1243 {
1244   RGBA rgb;
1245   SRECT r;
1246
1247   if(textid>=0)
1248       endtext();
1249
1250   tag = swf_InsertTag(tag,ST_DEFINESHAPE);
1251
1252   swf_ShapeNew(&shape);
1253   linestyleid = swf_ShapeAddLineStyle(shape,obj->linewidth,&obj->strokergb);
1254   rgb.r = obj->fillrgb.r;
1255   rgb.g = obj->fillrgb.g;
1256   rgb.b = obj->fillrgb.b;
1257   fillstyleid = swf_ShapeAddSolidFillStyle(shape,&obj->fillrgb);
1258
1259   shapeid = ++currentswfid;
1260   swf_SetU16(tag,shapeid);  // ID
1261
1262   r.xmin = 0;
1263   r.ymin = 0;
1264   r.xmax = 20*sizex;
1265   r.ymax = 20*sizey;
1266   
1267   swf_SetRect(tag,&r);
1268
1269   swf_SetShapeStyles(tag,shape);
1270   swf_ShapeCountBits(shape,NULL,NULL);
1271   swf_SetShapeBits(tag,shape);
1272
1273   swf_ShapeSetAll(tag,shape,/*x*/0,/*y*/0,linestyleid,0,0);
1274   swflastx=swflasty=0;
1275   lastwasfill = 0;
1276   shapeisempty = 1;
1277 }
1278
1279 static void starttext(struct swfoutput*obj)
1280 {
1281   SRECT r;
1282   MATRIX m;
1283   if(shapeid>=0)
1284       endshape();
1285   tag = swf_InsertTag(tag,ST_DEFINETEXT);
1286   textid = ++currentswfid;
1287   swf_SetU16(tag, textid);
1288
1289   r.xmin = 0;
1290   r.ymin = 0;
1291   r.xmax = 20*sizex;
1292   r.ymax = 20*sizey;
1293   
1294   swf_SetRect(tag,&r);
1295
1296   m.sx = 65536;
1297   m.sy = 65536;
1298   m.r0 = 0;
1299   m.r1 = 0;
1300   m.tx = 0;
1301   m.ty = 0;
1302  
1303   swf_SetMatrix(tag,&m);
1304   swflastx=swflasty=0;
1305 }
1306
1307 static void endshape()
1308 {
1309     if(shapeid<0) 
1310         return;
1311     swf_ShapeSetEnd(tag);
1312
1313     if(shapeisempty) {
1314         // delete the tag again, we didn't do anything
1315         TAG*todel = tag;
1316         tag = tag->prev;
1317         swf_DeleteTag(todel);
1318     } else {
1319         /* TODO: fix bounding box */
1320         tag = swf_InsertTag(tag,ST_PLACEOBJECT2);
1321         swf_ObjectPlace(tag,shapeid,/*depth*/depth++,NULL,NULL,NULL);
1322     }
1323     shapeid = -1;
1324 }
1325
1326 static void endtext()
1327 {
1328     if(textid<0)
1329         return;
1330     putcharacters(tag);
1331     swf_SetU8(tag,0);
1332     tag = swf_InsertTag(tag,ST_PLACEOBJECT2);
1333     swf_ObjectPlace(tag,textid,/*depth*/depth++,NULL,NULL,NULL);
1334     textid = -1;
1335 }
1336
1337 static void endpage(struct swfoutput*obj)
1338 {
1339     if(shapeid>=0)
1340       endshape();
1341     if(textid>=0)
1342       endtext();
1343     while(clippos)
1344         swfoutput_endclip(obj);
1345
1346     if(insertstoptag) {
1347         ActionTAG*atag=0;
1348         atag = action_Stop(atag);
1349         atag = action_End(atag);
1350         tag = swf_InsertTag(tag,ST_DOACTION);
1351         swf_ActionSet(tag,atag);
1352     }
1353     tag = swf_InsertTag(tag,ST_SHOWFRAME);
1354 }
1355
1356 void swfoutput_newpage(struct swfoutput*obj)
1357 {
1358     endpage(obj);
1359
1360     for(depth--;depth>=startdepth;depth--) {
1361         tag = swf_InsertTag(tag,ST_REMOVEOBJECT2);
1362         swf_SetU16(tag,depth);
1363     }
1364
1365     depth = startdepth;
1366 }
1367
1368 /* Perform cleaning up, complete the swf, and write it out. */
1369 void swfoutput_destroy(struct swfoutput* obj) 
1370 {
1371     endpage(obj);
1372     fontlist_t *tmp,*iterator = fontlist;
1373     while(iterator) {
1374         TAG*mtag = swf.firstTag;
1375         if(iterator->swffont) {
1376             mtag = swf_InsertTag(mtag, ST_DEFINEFONT2);
1377             /*if(!storeallcharacters)
1378                 swf_FontReduce(iterator->swffont);*/
1379             swf_FontSetDefine2(mtag, iterator->swffont);
1380             swf_FontFree(iterator->swffont);
1381         }
1382
1383         tmp = iterator;
1384         iterator = iterator->next;
1385         delete tmp;
1386     }
1387
1388     if(!filename) 
1389         return;
1390     if(filename)
1391      fi = open(filename, O_BINARY|O_CREAT|O_TRUNC|O_WRONLY, 0777);
1392     else
1393      fi = 1; // stdout
1394     
1395     if(fi<=0) {
1396      msg("<fatal> Could not create \"%s\". ", FIXNULL(filename));
1397      exit(1);
1398     }
1399  
1400     tag = swf_InsertTag(tag,ST_END);
1401
1402     if(enablezlib || flashversion>=6) {
1403       if FAILED(swf_WriteSWC(fi,&swf)) 
1404        msg("<error> WriteSWC() failed.\n");
1405     } else {
1406       if FAILED(swf_WriteSWF(fi,&swf)) 
1407        msg("<error> WriteSWF() failed.\n");
1408     }
1409
1410     if(filename)
1411      close(fi);
1412     msg("<notice> SWF written\n");
1413 }
1414
1415 void swfoutput_setdrawmode(swfoutput* obj, int mode)
1416 {
1417     obj->drawmode = mode;
1418     if(mode == DRAWMODE_FILL)
1419      fill = 1;
1420     else if(mode == DRAWMODE_EOFILL)
1421      fill = 1;
1422     else if(mode == DRAWMODE_STROKE)
1423      fill = 0;
1424     else if(mode == DRAWMODE_CLIP)
1425      fill = 1;
1426     else if(mode == DRAWMODE_EOCLIP)
1427      fill = 1;
1428 }
1429
1430 void swfoutput_setfillcolor(swfoutput* obj, u8 r, u8 g, u8 b, u8 a)
1431 {
1432     if(obj->fillrgb.r == r &&
1433        obj->fillrgb.g == g &&
1434        obj->fillrgb.b == b &&
1435        obj->fillrgb.a == a) return;
1436     if(shapeid>=0)
1437      endshape();
1438
1439     obj->fillrgb.r = r;
1440     obj->fillrgb.g = g;
1441     obj->fillrgb.b = b;
1442     obj->fillrgb.a = a;
1443 }
1444
1445 void swfoutput_setstrokecolor(swfoutput* obj, u8 r, u8 g, u8 b, u8 a)
1446 {
1447     if(obj->strokergb.r == r &&
1448        obj->strokergb.g == g &&
1449        obj->strokergb.b == b &&
1450        obj->strokergb.a == a) return;
1451
1452     if(shapeid>=0)
1453      endshape();
1454     obj->strokergb.r = r;
1455     obj->strokergb.g = g;
1456     obj->strokergb.b = b;
1457     obj->strokergb.a = a;
1458 }
1459
1460 void swfoutput_setlinewidth(struct swfoutput*obj, double linewidth)
1461 {
1462     if(obj->linewidth == (u16)(linewidth*20))
1463         return;
1464
1465     if(shapeid>=0)
1466      endshape();
1467     obj->linewidth = (u16)(linewidth*20);
1468 }
1469
1470
1471 void swfoutput_startclip(swfoutput*obj, SWF_OUTLINE*outline, struct swfmatrix*m)
1472 {
1473     if(textid>=0)
1474      endtext();
1475     if(shapeid>=0)
1476      endshape();
1477
1478     if(clippos >= 127)
1479     {
1480         msg("<warning> Too many clip levels.");
1481         clippos --;
1482     } 
1483     
1484     startshape(obj);
1485     int olddrawmode = obj->drawmode;
1486     swfoutput_setdrawmode(obj, DRAWMODE_CLIP);
1487     swfoutput_drawpath(obj, outline, m);
1488     swf_ShapeSetEnd(tag);
1489     swfoutput_setdrawmode(obj, olddrawmode);
1490
1491     tag = swf_InsertTag(tag,ST_PLACEOBJECT2);
1492     cliptags[clippos] = tag;
1493     clipshapes[clippos] = shapeid;
1494     clipdepths[clippos] = depth++;
1495     clippos++;
1496     shapeid = -1;
1497 }
1498
1499 void swfoutput_endclip(swfoutput*obj)
1500 {
1501     if(textid>=0)
1502      endtext();
1503     if(shapeid>=0)
1504      endshape();
1505
1506     if(!clippos) {
1507         msg("<error> Invalid end of clipping region");
1508         return;
1509     }
1510     clippos--;
1511     swf_ObjectPlaceClip(cliptags[clippos],clipshapes[clippos],clipdepths[clippos],NULL,NULL,NULL,depth++);
1512 }
1513
1514 static void drawlink(struct swfoutput*obj, ActionTAG*,ActionTAG*, swfcoord*points, char mouseover);
1515
1516 void swfoutput_linktourl(struct swfoutput*obj, char*url, swfcoord*points)
1517 {
1518     ActionTAG* actions;
1519     if(!strncmp("http://pdf2swf:", url, 15)) {
1520      char*tmp = strdup(url);
1521      int l = strlen(tmp);
1522      if(tmp[l-1] == '/')
1523         tmp[l-1] = 0;
1524      swfoutput_namedlink(obj, tmp+15, points);
1525      free(tmp);
1526      return;
1527     }
1528     
1529     if(shapeid>=0)
1530      endshape();
1531     if(textid>=0)
1532      endtext();
1533     
1534     if(opennewwindow)
1535       actions = action_GetUrl(0, url, "_parent");
1536     else
1537       actions = action_GetUrl(0, url, "_this");
1538     actions = action_End(actions);
1539     
1540     drawlink(obj, actions, 0, points,0);
1541 }
1542 void swfoutput_linktopage(struct swfoutput*obj, int page, swfcoord*points)
1543 {
1544     ActionTAG* actions;
1545
1546     if(shapeid>=0)
1547      endshape();
1548     if(textid>=0)
1549      endtext();
1550    
1551       actions = action_GotoFrame(0, page);
1552       actions = action_End(actions);
1553
1554     drawlink(obj, actions, 0, points,0);
1555 }
1556
1557 /* Named Links (a.k.a. Acrobatmenu) are used to implement various gadgets
1558    of the viewer objects, like subtitles, index elements etc.
1559 */
1560 void swfoutput_namedlink(struct swfoutput*obj, char*name, swfcoord*points)
1561 {
1562     ActionTAG *actions1,*actions2;
1563     char*tmp = strdup(name);
1564     char mouseover = 1;
1565
1566     if(shapeid>=0)
1567      endshape();
1568     if(textid>=0)
1569      endtext();
1570
1571     if(!strncmp(tmp, "call:", 5))
1572     {
1573         char*x = strchr(&tmp[5], ':');
1574         if(!x) {
1575             actions1 = action_PushInt(0, 0); //number of parameters (0)
1576             actions1 = action_PushString(actions1, &tmp[5]); //function name
1577             actions1 = action_CallFunction(actions1);
1578         } else {
1579             *x = 0;
1580             actions1 = action_PushString(0, x+1); //parameter
1581             actions1 = action_PushInt(actions1, 1); //number of parameters (1)
1582             actions1 = action_PushString(actions1, &tmp[5]); //function name
1583             actions1 = action_CallFunction(actions1);
1584         }
1585         actions2 = action_End(0);
1586         mouseover = 0;
1587     }
1588     else
1589     {
1590         actions1 = action_PushString(0, "/:subtitle");
1591         actions1 = action_PushString(actions1, name);
1592         actions1 = action_SetVariable(actions1);
1593         actions1 = action_End(actions1);
1594
1595         actions2 = action_PushString(0, "/:subtitle");
1596         actions2 = action_PushString(actions2, "");
1597         actions2 = action_SetVariable(actions2);
1598         actions2 = action_End(actions2);
1599     }
1600
1601     drawlink(obj, actions1, actions2, points,mouseover);
1602
1603     swf_ActionFree(actions1);
1604     swf_ActionFree(actions2);
1605     free(tmp);
1606 }
1607
1608 static void drawlink(struct swfoutput*obj, ActionTAG*actions1, ActionTAG*actions2, swfcoord*points, char mouseover)
1609 {
1610     RGBA rgb;
1611     SRECT r;
1612     int lsid=0;
1613     int fsid;
1614     struct plotxy p1,p2,p3,p4;
1615     int myshapeid;
1616     int myshapeid2;
1617     double xmin,ymin;
1618     double xmax=xmin=points[0].x,ymax=ymin=points[0].y;
1619     double posx = 0;
1620     double posy = 0;
1621     int t;
1622     int buttonid = ++currentswfid;
1623     for(t=1;t<4;t++)
1624     {
1625         if(points[t].x>xmax) xmax=points[t].x;
1626         if(points[t].y>ymax) ymax=points[t].y;
1627         if(points[t].x<xmin) xmin=points[t].x;
1628         if(points[t].y<ymin) ymin=points[t].y;
1629     }
1630    
1631     p1.x=points[0].x; p1.y=points[0].y; p2.x=points[1].x; p2.y=points[1].y; 
1632     p3.x=points[2].x; p3.y=points[2].y; p4.x=points[3].x; p4.y=points[3].y;
1633    
1634     /* the following code subtracts the upper left edge from all coordinates,
1635        and set's posx,posy so that ST_PLACEOBJECT is used with a matrix.
1636        Necessary for preprocessing with swfcombine. */
1637     posx = xmin; posy = ymin;
1638     p1.x-=posx;p2.x-=posx;p3.x-=posx;p4.x-=posx;
1639     p1.y-=posy;p2.y-=posy;p3.y-=posy;p4.y-=posy;
1640     xmin -= posx; ymin -= posy;
1641     xmax -= posx; ymax -= posy;
1642     
1643     /* shape */
1644     myshapeid = ++currentswfid;
1645     tag = swf_InsertTag(tag,ST_DEFINESHAPE3);
1646     swf_ShapeNew(&shape);
1647     rgb.r = rgb.b = rgb.a = rgb.g = 0; 
1648     fsid = swf_ShapeAddSolidFillStyle(shape,&rgb);
1649     swf_SetU16(tag, myshapeid);
1650     r.xmin = (int)(xmin*20);
1651     r.ymin = (int)(ymin*20);
1652     r.xmax = (int)(xmax*20);
1653     r.ymax = (int)(ymax*20);
1654     swf_SetRect(tag,&r);
1655     swf_SetShapeStyles(tag,shape);
1656     swf_ShapeCountBits(shape,NULL,NULL);
1657     swf_SetShapeBits(tag,shape);
1658     swf_ShapeSetAll(tag,shape,/*x*/0,/*y*/0,0,fsid,0);
1659     swflastx = swflasty = 0;
1660     moveto(tag, p1);
1661     lineto(tag, p2);
1662     lineto(tag, p3);
1663     lineto(tag, p4);
1664     lineto(tag, p1);
1665     swf_ShapeSetEnd(tag);
1666
1667     /* shape2 */
1668     myshapeid2 = ++currentswfid;
1669     tag = swf_InsertTag(tag,ST_DEFINESHAPE3);
1670     swf_ShapeNew(&shape);
1671     rgb.r = rgb.b = rgb.a = rgb.g = 255;
1672     rgb.a = 40;
1673     fsid = swf_ShapeAddSolidFillStyle(shape,&rgb);
1674     swf_SetU16(tag, myshapeid2);
1675     r.xmin = (int)(xmin*20);
1676     r.ymin = (int)(ymin*20);
1677     r.xmax = (int)(xmax*20);
1678     r.ymax = (int)(ymax*20);
1679     swf_SetRect(tag,&r);
1680     swf_SetShapeStyles(tag,shape);
1681     swf_ShapeCountBits(shape,NULL,NULL);
1682     swf_SetShapeBits(tag,shape);
1683     swf_ShapeSetAll(tag,shape,/*x*/0,/*y*/0,0,fsid,0);
1684     swflastx = swflasty = 0;
1685     moveto(tag, p1);
1686     lineto(tag, p2);
1687     lineto(tag, p3);
1688     lineto(tag, p4);
1689     lineto(tag, p1);
1690     swf_ShapeSetEnd(tag);
1691
1692     if(!mouseover)
1693     {
1694         tag = swf_InsertTag(tag,ST_DEFINEBUTTON);
1695         swf_SetU16(tag,buttonid); //id
1696         swf_ButtonSetFlags(tag, 0); //menu=no
1697         swf_ButtonSetRecord(tag,0x01,myshapeid,depth,0,0);
1698         swf_ButtonSetRecord(tag,0x02,myshapeid2,depth,0,0);
1699         swf_ButtonSetRecord(tag,0x04,myshapeid2,depth,0,0);
1700         swf_ButtonSetRecord(tag,0x08,myshapeid,depth,0,0);
1701         swf_SetU8(tag,0);
1702         swf_ActionSet(tag,actions1);
1703         swf_SetU8(tag,0);
1704     }
1705     else
1706     {
1707         tag = swf_InsertTag(tag,ST_DEFINEBUTTON2);
1708         swf_SetU16(tag,buttonid); //id
1709         swf_ButtonSetFlags(tag, 0); //menu=no
1710         swf_ButtonSetRecord(tag,0x01,myshapeid,depth,0,0);
1711         swf_ButtonSetRecord(tag,0x02,myshapeid2,depth,0,0);
1712         swf_ButtonSetRecord(tag,0x04,myshapeid2,depth,0,0);
1713         swf_ButtonSetRecord(tag,0x08,myshapeid,depth,0,0);
1714         swf_SetU8(tag,0); // end of button records
1715         swf_ButtonSetCondition(tag, BC_IDLE_OVERUP);
1716         swf_ActionSet(tag,actions1);
1717         if(actions2) {
1718             swf_ButtonSetCondition(tag, BC_OVERUP_IDLE);
1719             swf_ActionSet(tag,actions2);
1720             swf_SetU8(tag,0);
1721             swf_ButtonPostProcess(tag, 2);
1722         } else {
1723             swf_SetU8(tag,0);
1724             swf_ButtonPostProcess(tag, 1);
1725         }
1726     }
1727     
1728     tag = swf_InsertTag(tag,ST_PLACEOBJECT2);
1729
1730     if(posx!=0 || posy!=0) {
1731         MATRIX m;
1732         swf_GetMatrix(0,&m);
1733         m.tx = (int)(posx*20);
1734         m.ty = (int)(posy*20);
1735         swf_ObjectPlace(tag, buttonid, depth++,&m,0,0);
1736     }
1737     else {
1738         swf_ObjectPlace(tag, buttonid, depth++,0,0,0);
1739     }
1740 }
1741
1742 static void drawimage(struct swfoutput*obj, int bitid, int sizex,int sizey, 
1743         double x1,double y1,
1744         double x2,double y2,
1745         double x3,double y3,
1746         double x4,double y4)
1747 {
1748     RGBA rgb;
1749     SRECT r;
1750     int lsid=0;
1751     int fsid;
1752     struct plotxy p1,p2,p3,p4;
1753     int myshapeid;
1754     double xmax=x1,ymax=y1,xmin=x1,ymin=y1;
1755     if(x2>xmax) xmax=x2;
1756     if(y2>ymax) ymax=y2;
1757     if(x2<xmin) xmin=x2;
1758     if(y2<ymin) ymin=y2;
1759     if(x3>xmax) xmax=x3;
1760     if(y3>ymax) ymax=y3;
1761     if(x3<xmin) xmin=x3;
1762     if(y3<ymin) ymin=y3;
1763     if(x4>xmax) xmax=x4;
1764     if(y4>ymax) ymax=y4;
1765     if(x4<xmin) xmin=x4;
1766     if(y4<ymin) ymin=y4;
1767     p1.x=x1; p1.y=y1;
1768     p2.x=x2; p2.y=y2;
1769     p3.x=x3; p3.y=y3;
1770     p4.x=x4; p4.y=y4;
1771
1772     {p1.x = (int)(p1.x*20)/20.0;
1773      p1.y = (int)(p1.y*20)/20.0;
1774      p2.x = (int)(p2.x*20)/20.0;
1775      p2.y = (int)(p2.y*20)/20.0;
1776      p3.x = (int)(p3.x*20)/20.0;
1777      p3.y = (int)(p3.y*20)/20.0;
1778      p4.x = (int)(p4.x*20)/20.0;
1779      p4.y = (int)(p4.y*20)/20.0;}
1780     
1781     MATRIX m;
1782     m.sx = (int)(65536*20*(p4.x-p1.x)/sizex);
1783     m.r1 = -(int)(65536*20*(p4.y-p1.y)/sizex);
1784     m.r0 = (int)(65536*20*(p1.x-p2.x)/sizey);
1785     m.sy = -(int)(65536*20*(p1.y-p2.y)/sizey);
1786
1787     m.tx = (int)(p1.x*20);
1788     m.ty = (int)(p1.y*20);
1789   
1790     /* shape */
1791     myshapeid = ++currentswfid;
1792     tag = swf_InsertTag(tag,ST_DEFINESHAPE);
1793     swf_ShapeNew(&shape);
1794     //lsid = ShapeAddLineStyle(shape,obj->linewidth,&obj->strokergb);
1795     //fsid = ShapeAddSolidFillStyle(shape,&obj->fillrgb);
1796     fsid = swf_ShapeAddBitmapFillStyle(shape,&m,bitid,1);
1797     swf_SetU16(tag, myshapeid);
1798     r.xmin = (int)(xmin*20);
1799     r.ymin = (int)(ymin*20);
1800     r.xmax = (int)(xmax*20);
1801     r.ymax = (int)(ymax*20);
1802     swf_SetRect(tag,&r);
1803     swf_SetShapeStyles(tag,shape);
1804     swf_ShapeCountBits(shape,NULL,NULL);
1805     swf_SetShapeBits(tag,shape);
1806     swf_ShapeSetAll(tag,shape,/*x*/0,/*y*/0,lsid,fsid,0);
1807     swflastx = swflasty = 0;
1808     moveto(tag, p1);
1809     lineto(tag, p2);
1810     lineto(tag, p3);
1811     lineto(tag, p4);
1812     lineto(tag, p1);
1813     /*
1814     ShapeMoveTo  (tag, shape, (int)(x1*20),(int)(y1*20));
1815     ShapeSetLine (tag, shape, (int)(x1*20);
1816     ShapeSetLine (tag, shape, x*20,0);
1817     ShapeSetLine (tag, shape, 0,-y*20);
1818     ShapeSetLine (tag, shape, -x*20,0);*/
1819     swf_ShapeSetEnd(tag);
1820
1821     /* instance */
1822     tag = swf_InsertTag(tag,ST_PLACEOBJECT2);
1823     swf_ObjectPlace(tag,myshapeid,/*depth*/depth++,NULL,NULL,NULL);
1824 }
1825
1826 int swfoutput_drawimagejpeg_old(struct swfoutput*obj, char*filename, int sizex,int sizey, 
1827         double x1,double y1,
1828         double x2,double y2,
1829         double x3,double y3,
1830         double x4,double y4)
1831 {
1832     TAG*oldtag;
1833     if(shapeid>=0)
1834      endshape();
1835     if(textid>=0)
1836      endtext();
1837
1838     int bitid = ++currentswfid;
1839     oldtag = tag;
1840     tag = swf_InsertTag(tag,ST_DEFINEBITSJPEG2);
1841     swf_SetU16(tag, bitid);
1842     if(swf_SetJPEGBits(tag, filename, jpegquality)<0) {
1843         swf_DeleteTag(tag);
1844         tag = oldtag;
1845         return -1;
1846     }
1847
1848     drawimage(obj, bitid, sizex, sizey, x1,y1,x2,y2,x3,y3,x4,y4);
1849     return bitid;
1850 }
1851
1852 int swfoutput_drawimagejpeg(struct swfoutput*obj, RGBA*mem, int sizex,int sizey, 
1853         double x1,double y1,
1854         double x2,double y2,
1855         double x3,double y3,
1856         double x4,double y4)
1857 {
1858     TAG*oldtag;
1859     JPEGBITS*jpeg;
1860
1861     if(shapeid>=0)
1862      endshape();
1863     if(textid>=0)
1864      endtext();
1865
1866     int bitid = ++currentswfid;
1867     oldtag = tag;
1868     tag = swf_InsertTag(tag,ST_DEFINEBITSJPEG2);
1869     swf_SetU16(tag, bitid);
1870     swf_SetJPEGBits2(tag,sizex,sizey,mem,jpegquality);
1871     drawimage(obj, bitid, sizex, sizey, x1,y1,x2,y2,x3,y3,x4,y4);
1872     return bitid;
1873 }
1874
1875 int swfoutput_drawimagelossless(struct swfoutput*obj, RGBA*mem, int sizex,int sizey, 
1876         double x1,double y1,
1877         double x2,double y2,
1878         double x3,double y3,
1879         double x4,double y4)
1880 {
1881     TAG*oldtag;
1882     if(shapeid>=0)
1883      endshape();
1884     if(textid>=0)
1885      endtext();
1886
1887     int bitid = ++currentswfid;
1888     oldtag = tag;
1889     tag = swf_InsertTag(tag,ST_DEFINEBITSLOSSLESS);
1890     swf_SetU16(tag, bitid);
1891     if(swf_SetLosslessBits(tag,sizex,sizey,mem, BMF_32BIT)<0) {
1892         swf_DeleteTag(tag);
1893         tag = oldtag;
1894         return -1;
1895     }
1896     
1897     drawimage(obj, bitid, sizex, sizey, x1,y1,x2,y2,x3,y3,x4,y4);
1898     return bitid;
1899 }
1900
1901 int swfoutput_drawimagelosslessN(struct swfoutput*obj, U8*mem, RGBA*pal, int sizex,int sizey, 
1902         double x1,double y1,
1903         double x2,double y2,
1904         double x3,double y3,
1905         double x4,double y4, int n)
1906 {
1907     TAG*oldtag;
1908     U8*mem2 = 0;
1909     if(shapeid>=0)
1910      endshape();
1911     if(textid>=0)
1912      endtext();
1913
1914     if(sizex&3)
1915     { 
1916         /* SWF expects scanlines to be 4 byte aligned */
1917         int x,y;
1918         U8*ptr;
1919         mem2 = (U8*)malloc(BYTES_PER_SCANLINE(sizex)*sizey);
1920         ptr = mem2;
1921         for(y=0;y<sizey;y++)
1922         {
1923             for(x=0;x<sizex;x++)
1924                 *ptr++ = mem[y*sizex+x];
1925             ptr+= BYTES_PER_SCANLINE(sizex)-sizex;
1926         }
1927         mem = mem2;
1928     }
1929
1930     int bitid = ++currentswfid;
1931     oldtag = tag;
1932     tag = swf_InsertTag(tag,ST_DEFINEBITSLOSSLESS2);
1933     swf_SetU16(tag, bitid);
1934     if(swf_SetLosslessBitsIndexed(tag,sizex,sizey,mem, pal, n)<0) {
1935         swf_DeleteTag(tag);
1936         tag = oldtag;
1937         return -1;
1938     }
1939     if(mem2)
1940         free(mem2);
1941   
1942     drawimage(obj, bitid, sizex, sizey, x1,y1,x2,y2,x3,y3,x4,y4);
1943     return bitid;
1944 }
1945
1946 void swfoutput_drawimageagain(struct swfoutput*obj, int id, int sizex,int sizey, 
1947         double x1,double y1,
1948         double x2,double y2,
1949         double x3,double y3,
1950         double x4,double y4)
1951 {
1952     if(id<0) return;
1953     if(shapeid>=0)
1954      endshape();
1955     if(textid>=0)
1956      endtext();
1957
1958     drawimage(obj, id, sizex, sizey, x1,y1,x2,y2,x3,y3,x4,y4);
1959 }
1960