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