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