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