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