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