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