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