* fixed small bbox 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     endpage(obj);
1343
1344     for(depth--;depth>=startdepth;depth--) {
1345         tag = swf_InsertTag(tag,ST_REMOVEOBJECT2);
1346         swf_SetU16(tag,depth);
1347     }
1348     depth = startdepth = 3; /* leave room for clip and background rectangle */
1349
1350     sizex = x2;
1351     sizey = y2;
1352     x1*=20;y1*=20;x2*=20;y2*=20;
1353
1354     if(lastpagesize.xmin != x1 ||
1355        lastpagesize.xmax != x2 ||
1356        lastpagesize.ymin != y1 ||
1357        lastpagesize.ymax != y2)
1358     {/* add white clipping rectangle */
1359         msg("<notice> processing page %d (%dx%d)", pageNum,sizex,sizey);
1360
1361         if(!firstpage) {
1362             msg("<notice> Page has a different size than previous ones");
1363             tag = swf_InsertTag(tag,ST_REMOVEOBJECT2);
1364             swf_SetU16(tag,1);
1365             tag = swf_InsertTag(tag,ST_REMOVEOBJECT2);
1366             swf_SetU16(tag,2);
1367         }
1368
1369         RGBA rgb;
1370         rgb.a = rgb.r = rgb.g = rgb.b = 0xff;
1371         SRECT r;
1372         SHAPE* s;
1373         int ls1=0,fs1=0;
1374         int shapeid = ++currentswfid;
1375         r.xmin = x1;
1376         r.ymin = y1;
1377         r.xmax = x2;
1378         r.ymax = y2;
1379         tag = swf_InsertTag(tag, ST_DEFINESHAPE);
1380         swf_ShapeNew(&s);
1381         fs1 = swf_ShapeAddSolidFillStyle(s, &rgb);
1382         swf_SetU16(tag,shapeid);
1383         swf_SetRect(tag,&r);
1384         swf_SetShapeHeader(tag,s);
1385         swf_ShapeSetAll(tag,s,x1,y1,ls1,fs1,0);
1386         swf_ShapeSetLine(tag,s,(x2-x1),0);
1387         swf_ShapeSetLine(tag,s,0,(y2-y1));
1388         swf_ShapeSetLine(tag,s,(x1-x2),0);
1389         swf_ShapeSetLine(tag,s,0,(y1-y2));
1390         swf_ShapeSetEnd(tag);
1391         swf_ShapeFree(s);
1392         tag = swf_InsertTag(tag, ST_PLACEOBJECT2);
1393         swf_ObjectPlace(tag,shapeid,/*depth*/1,0,0,0);
1394         tag = swf_InsertTag(tag, ST_PLACEOBJECT2);
1395         swf_ObjectPlaceClip(tag,shapeid,/*depth*/2,0,0,0,65535);
1396     } else {
1397         msg("<notice> processing page %d", pageNum);
1398     }
1399
1400     lastpagesize.xmin = x1;
1401     lastpagesize.xmax = x2;
1402     lastpagesize.ymin = y1;
1403     lastpagesize.ymax = y2;
1404     swf_ExpandRect2(&swf.movieSize, &lastpagesize);
1405
1406     firstpage = 0;
1407 }
1408
1409 /* initialize the swf writer */
1410 void swfoutput_init(struct swfoutput* obj, char*_filename)
1411 {
1412   SRECT r;
1413   RGBA rgb;
1414   memset(obj, 0, sizeof(struct swfoutput));
1415   filename = _filename;
1416
1417   msg("<verbose> initializing swf output for size %d*%d\n", sizex,sizey);
1418
1419   obj->swffont = 0;
1420   obj->drawmode = -1;
1421   
1422   memset(&swf,0x00,sizeof(SWF));
1423   memset(&lastpagesize,0x00,sizeof(SRECT));
1424
1425   swf.fileVersion    = flashversion;
1426   swf.frameRate      = 0x0040; // 1 frame per 4 seconds
1427   swf.movieSize.xmin = 0;
1428   swf.movieSize.ymin = 0;
1429   swf.movieSize.xmax = 0;
1430   swf.movieSize.ymax = 0;
1431   
1432   swf.firstTag = swf_InsertTag(NULL,ST_SETBACKGROUNDCOLOR);
1433   tag = swf.firstTag;
1434   rgb.a = rgb.r = rgb.g = rgb.b = 0xff;
1435   swf_SetRGB(tag,&rgb);
1436
1437   if(flag_protected)
1438     tag = swf_InsertTag(tag, ST_PROTECT);
1439   
1440   startdepth = depth = 0;
1441 }
1442
1443 void swfoutput_setprotected() //write PROTECT tag
1444 {
1445   flag_protected = 1;
1446 }
1447
1448 static void startshape(struct swfoutput*obj)
1449 {
1450   RGBA rgb;
1451   SRECT r;
1452
1453   if(textid>=0)
1454       endtext(obj);
1455
1456   tag = swf_InsertTag(tag,ST_DEFINESHAPE);
1457
1458   swf_ShapeNew(&shape);
1459   linestyleid = swf_ShapeAddLineStyle(shape,linewidth,&obj->strokergb);
1460   rgb.r = obj->fillrgb.r;
1461   rgb.g = obj->fillrgb.g;
1462   rgb.b = obj->fillrgb.b;
1463   fillstyleid = swf_ShapeAddSolidFillStyle(shape,&obj->fillrgb);
1464
1465   shapeid = ++currentswfid;
1466   swf_SetU16(tag,shapeid);  // ID
1467
1468   bboxrectpos = tag->len;
1469   r.xmin = 0;
1470   r.ymin = 0;
1471   r.xmax = 20*sizex;
1472   r.ymax = 20*sizey;
1473   swf_SetRect(tag,&r);
1474  
1475   memset(&bboxrect, 0, sizeof(bboxrect));
1476
1477   swf_SetShapeStyles(tag,shape);
1478   swf_ShapeCountBits(shape,NULL,NULL);
1479   swf_SetShapeBits(tag,shape);
1480
1481   /* TODO: do we really need this? */
1482   swf_ShapeSetAll(tag,shape,/*x*/0,/*y*/0,linestyleid,0,0);
1483   swflastx=swflasty=0;
1484   lastwasfill = 0;
1485   shapeisempty = 1;
1486 }
1487
1488 static void starttext(struct swfoutput*obj)
1489 {
1490   if(shapeid>=0)
1491       endshape(obj,0);
1492     
1493   textid = ++currentswfid;
1494
1495   swflastx=swflasty=0;
1496 }
1497             
1498
1499 /* TODO: move to ../lib/rfxswf */
1500 void changeRect(TAG*tag, int pos, SRECT*newrect)
1501 {
1502     /* determine length of old rect */
1503     tag->pos = pos;
1504     tag->readBit = 0;
1505     SRECT old;
1506     swf_GetRect(tag, &old);
1507     swf_ResetReadBits(tag);
1508     int pos_end = tag->pos;
1509
1510     int len = tag->len - pos_end;
1511     U8*data = (U8*)malloc(len);
1512     memcpy(data, &tag->data[pos_end], len);
1513     tag->writeBit = 0;
1514     tag->len = pos;
1515     swf_SetRect(tag, newrect);
1516     swf_SetBlock(tag, data, len);
1517     free(data);
1518     tag->pos = tag->readBit = 0;
1519 }
1520
1521 void cancelshape(swfoutput*obj)
1522 {
1523     /* delete old shape tag */
1524     TAG*todel = tag;
1525     tag = tag->prev;
1526     swf_DeleteTag(todel);
1527     shapeid = -1;
1528     bboxrectpos = -1;
1529 }
1530
1531 void fixAreas(swfoutput*obj)
1532 {
1533     if(!shapeisempty && fill &&
1534        (bboxrect.xmin == bboxrect.xmax ||
1535         bboxrect.ymin == bboxrect.ymax) &&
1536         minlinewidth >= 0.001
1537        ) {
1538         msg("<debug> Shape has size 0: width=%.2f height=%.2f",
1539                 (bboxrect.xmax-bboxrect.xmin)/20.0,
1540                 (bboxrect.ymax-bboxrect.ymin)/20.0
1541                 );
1542     
1543         SRECT r = bboxrect;
1544         
1545         if(r.xmin == r.xmax && r.ymin == r.ymax) {
1546             /* this thing comes down to a single dot- nothing to fix here */
1547             return;
1548         }
1549
1550         cancelshape(obj);
1551
1552         RGBA save_col = obj->strokergb;
1553         int  save_width = linewidth;
1554
1555         obj->strokergb = obj->fillrgb;
1556         linewidth = (int)(minlinewidth*20);
1557         if(linewidth==0) linewidth = 1;
1558         
1559         startshape(obj);
1560
1561         moveto(tag, r.xmin/20.0,r.ymin/20.0);
1562         lineto(tag, r.xmax/20.0,r.ymax/20.0);
1563
1564         obj->strokergb = save_col;
1565         linewidth = save_width;
1566     }
1567     
1568 }
1569
1570 static void endshape(swfoutput*obj, int clipdepth)
1571 {
1572     if(shapeid<0) 
1573         return;
1574
1575     if(!clipdepth)
1576         fixAreas(obj);
1577         
1578     if(shapeisempty ||
1579        (bboxrect.xmin == bboxrect.xmax && bboxrect.ymin == bboxrect.ymax)) 
1580     {
1581         // delete the shape again, we didn't do anything
1582         cancelshape(obj);
1583         return;
1584     }
1585     
1586     swf_ShapeSetEnd(tag);
1587
1588     changeRect(tag, bboxrectpos, &bboxrect);
1589
1590     tag = swf_InsertTag(tag,ST_PLACEOBJECT2);
1591     if(clipdepth)
1592         swf_ObjectPlaceClip(tag,shapeid,depth++,NULL,NULL,NULL,clipdepth);
1593     else
1594         swf_ObjectPlace(tag,shapeid,/*depth*/depth++,NULL,NULL,NULL);
1595
1596     shapeid = -1;
1597     bboxrectpos = -1;
1598 }
1599
1600 /* Perform cleaning up, complete the swf, and write it out. */
1601 void swfoutput_destroy(struct swfoutput* obj) 
1602 {
1603     endpage(obj);
1604     fontlist_t *tmp,*iterator = fontlist;
1605     while(iterator) {
1606         TAG*mtag = swf.firstTag;
1607         if(iterator->swffont) {
1608             mtag = swf_InsertTag(mtag, ST_DEFINEFONT2);
1609             /*if(!storeallcharacters)
1610                 swf_FontReduce(iterator->swffont);*/
1611             swf_FontSetDefine2(mtag, iterator->swffont);
1612             swf_FontFree(iterator->swffont);
1613         }
1614
1615         tmp = iterator;
1616         iterator = iterator->next;
1617         delete tmp;
1618     }
1619
1620     if(!filename) 
1621         return;
1622     if(filename)
1623      fi = open(filename, O_BINARY|O_CREAT|O_TRUNC|O_WRONLY, 0777);
1624     else
1625      fi = 1; // stdout
1626     
1627     if(fi<=0) {
1628      msg("<fatal> Could not create \"%s\". ", FIXNULL(filename));
1629      exit(1);
1630     }
1631  
1632     tag = swf_InsertTag(tag,ST_END);
1633
1634     if(enablezlib || flashversion>=6) {
1635       if FAILED(swf_WriteSWC(fi,&swf)) 
1636        msg("<error> WriteSWC() failed.\n");
1637     } else {
1638       if FAILED(swf_WriteSWF(fi,&swf)) 
1639        msg("<error> WriteSWF() failed.\n");
1640     }
1641
1642     if(filename)
1643      close(fi);
1644     msg("<notice> SWF written\n");
1645 }
1646
1647 void swfoutput_setdrawmode(swfoutput* obj, int mode)
1648 {
1649     obj->drawmode = mode;
1650     if(mode == DRAWMODE_FILL)
1651      fill = 1;
1652     else if(mode == DRAWMODE_EOFILL)
1653      fill = 1;
1654     else if(mode == DRAWMODE_STROKE)
1655      fill = 0;
1656     else if(mode == DRAWMODE_CLIP)
1657      fill = 1;
1658     else if(mode == DRAWMODE_EOCLIP)
1659      fill = 1;
1660 }
1661
1662 void swfoutput_setfillcolor(swfoutput* obj, u8 r, u8 g, u8 b, u8 a)
1663 {
1664     if(obj->fillrgb.r == r &&
1665        obj->fillrgb.g == g &&
1666        obj->fillrgb.b == b &&
1667        obj->fillrgb.a == a) return;
1668     if(shapeid>=0)
1669      endshape(obj,0);
1670
1671     obj->fillrgb.r = r;
1672     obj->fillrgb.g = g;
1673     obj->fillrgb.b = b;
1674     obj->fillrgb.a = a;
1675 }
1676
1677 void swfoutput_setstrokecolor(swfoutput* obj, u8 r, u8 g, u8 b, u8 a)
1678 {
1679     if(obj->strokergb.r == r &&
1680        obj->strokergb.g == g &&
1681        obj->strokergb.b == b &&
1682        obj->strokergb.a == a) return;
1683
1684     if(shapeid>=0)
1685      endshape(obj,0);
1686     obj->strokergb.r = r;
1687     obj->strokergb.g = g;
1688     obj->strokergb.b = b;
1689     obj->strokergb.a = a;
1690 }
1691
1692 void swfoutput_setlinewidth(struct swfoutput*obj, double _linewidth)
1693 {
1694     if(linewidth == (u16)(_linewidth*20))
1695         return;
1696
1697     if(shapeid>=0)
1698         endshape(obj,0);
1699     linewidth = (u16)(_linewidth*20);
1700 }
1701
1702
1703 void swfoutput_startclip(swfoutput*obj, SWF_OUTLINE*outline, struct swfmatrix*m)
1704 {
1705     if(textid>=0)
1706      endtext(obj);
1707     if(shapeid>=0)
1708      endshape(obj,0);
1709
1710     if(clippos >= 127)
1711     {
1712         msg("<warning> Too many clip levels.");
1713         clippos --;
1714     } 
1715     
1716     startshape(obj);
1717     int olddrawmode = obj->drawmode;
1718     swfoutput_setdrawmode(obj, DRAWMODE_CLIP);
1719     swfoutput_drawpath(obj, outline, m);
1720     swf_ShapeSetEnd(tag);
1721     swfoutput_setdrawmode(obj, olddrawmode);
1722
1723     tag = swf_InsertTag(tag,ST_PLACEOBJECT2);
1724     cliptags[clippos] = tag;
1725     clipshapes[clippos] = shapeid;
1726     clipdepths[clippos] = depth++;
1727     clippos++;
1728     shapeid = -1;
1729 }
1730
1731 void swfoutput_endclip(swfoutput*obj)
1732 {
1733     if(textid>=0)
1734         endtext(obj);
1735     if(shapeid>=0)
1736         endshape(obj,0);
1737
1738     if(!clippos) {
1739         msg("<error> Invalid end of clipping region");
1740         return;
1741     }
1742     clippos--;
1743     swf_ObjectPlaceClip(cliptags[clippos],clipshapes[clippos],clipdepths[clippos],NULL,NULL,NULL,depth++);
1744 }
1745
1746 static void drawlink(struct swfoutput*obj, ActionTAG*,ActionTAG*, swfcoord*points, char mouseover);
1747
1748 void swfoutput_linktourl(struct swfoutput*obj, char*url, swfcoord*points)
1749 {
1750     ActionTAG* actions;
1751     if(!strncmp("http://pdf2swf:", url, 15)) {
1752      char*tmp = strdup(url);
1753      int l = strlen(tmp);
1754      if(tmp[l-1] == '/')
1755         tmp[l-1] = 0;
1756      swfoutput_namedlink(obj, tmp+15, points);
1757      free(tmp);
1758      return;
1759     }
1760     
1761     if(shapeid>=0)
1762         endshape(obj,0);
1763     if(textid>=0)
1764         endtext(obj);
1765     
1766     if(opennewwindow)
1767       actions = action_GetUrl(0, url, "_parent");
1768     else
1769       actions = action_GetUrl(0, url, "_this");
1770     actions = action_End(actions);
1771     
1772     drawlink(obj, actions, 0, points,0);
1773 }
1774 void swfoutput_linktopage(struct swfoutput*obj, int page, swfcoord*points)
1775 {
1776     ActionTAG* actions;
1777
1778     if(shapeid>=0)
1779         endshape(obj,0);
1780     if(textid>=0)
1781         endtext(obj);
1782    
1783       actions = action_GotoFrame(0, page);
1784       actions = action_End(actions);
1785
1786     drawlink(obj, actions, 0, points,0);
1787 }
1788
1789 /* Named Links (a.k.a. Acrobatmenu) are used to implement various gadgets
1790    of the viewer objects, like subtitles, index elements etc.
1791 */
1792 void swfoutput_namedlink(struct swfoutput*obj, char*name, swfcoord*points)
1793 {
1794     ActionTAG *actions1,*actions2;
1795     char*tmp = strdup(name);
1796     char mouseover = 1;
1797
1798     if(shapeid>=0)
1799         endshape(obj,0);
1800     if(textid>=0)
1801         endtext(obj);
1802
1803     if(!strncmp(tmp, "call:", 5))
1804     {
1805         char*x = strchr(&tmp[5], ':');
1806         if(!x) {
1807             actions1 = action_PushInt(0, 0); //number of parameters (0)
1808             actions1 = action_PushString(actions1, &tmp[5]); //function name
1809             actions1 = action_CallFunction(actions1);
1810         } else {
1811             *x = 0;
1812             actions1 = action_PushString(0, x+1); //parameter
1813             actions1 = action_PushInt(actions1, 1); //number of parameters (1)
1814             actions1 = action_PushString(actions1, &tmp[5]); //function name
1815             actions1 = action_CallFunction(actions1);
1816         }
1817         actions2 = action_End(0);
1818         mouseover = 0;
1819     }
1820     else
1821     {
1822         actions1 = action_PushString(0, "/:subtitle");
1823         actions1 = action_PushString(actions1, name);
1824         actions1 = action_SetVariable(actions1);
1825         actions1 = action_End(actions1);
1826
1827         actions2 = action_PushString(0, "/:subtitle");
1828         actions2 = action_PushString(actions2, "");
1829         actions2 = action_SetVariable(actions2);
1830         actions2 = action_End(actions2);
1831     }
1832
1833     drawlink(obj, actions1, actions2, points,mouseover);
1834
1835     swf_ActionFree(actions1);
1836     swf_ActionFree(actions2);
1837     free(tmp);
1838 }
1839
1840 static void drawlink(struct swfoutput*obj, ActionTAG*actions1, ActionTAG*actions2, swfcoord*points, char mouseover)
1841 {
1842     RGBA rgb;
1843     SRECT r;
1844     int lsid=0;
1845     int fsid;
1846     struct plotxy p1,p2,p3,p4;
1847     int myshapeid;
1848     int myshapeid2;
1849     double xmin,ymin;
1850     double xmax=xmin=points[0].x,ymax=ymin=points[0].y;
1851     double posx = 0;
1852     double posy = 0;
1853     int t;
1854     int buttonid = ++currentswfid;
1855     for(t=1;t<4;t++)
1856     {
1857         if(points[t].x>xmax) xmax=points[t].x;
1858         if(points[t].y>ymax) ymax=points[t].y;
1859         if(points[t].x<xmin) xmin=points[t].x;
1860         if(points[t].y<ymin) ymin=points[t].y;
1861     }
1862    
1863     p1.x=points[0].x; p1.y=points[0].y; p2.x=points[1].x; p2.y=points[1].y; 
1864     p3.x=points[2].x; p3.y=points[2].y; p4.x=points[3].x; p4.y=points[3].y;
1865    
1866     /* the following code subtracts the upper left edge from all coordinates,
1867        and set's posx,posy so that ST_PLACEOBJECT is used with a matrix.
1868        Necessary for preprocessing with swfcombine. */
1869     posx = xmin; posy = ymin;
1870     p1.x-=posx;p2.x-=posx;p3.x-=posx;p4.x-=posx;
1871     p1.y-=posy;p2.y-=posy;p3.y-=posy;p4.y-=posy;
1872     xmin -= posx; ymin -= posy;
1873     xmax -= posx; ymax -= posy;
1874     
1875     /* shape */
1876     myshapeid = ++currentswfid;
1877     tag = swf_InsertTag(tag,ST_DEFINESHAPE3);
1878     swf_ShapeNew(&shape);
1879     rgb.r = rgb.b = rgb.a = rgb.g = 0; 
1880     fsid = swf_ShapeAddSolidFillStyle(shape,&rgb);
1881     swf_SetU16(tag, myshapeid);
1882     r.xmin = (int)(xmin*20);
1883     r.ymin = (int)(ymin*20);
1884     r.xmax = (int)(xmax*20);
1885     r.ymax = (int)(ymax*20);
1886     swf_SetRect(tag,&r);
1887     swf_SetShapeStyles(tag,shape);
1888     swf_ShapeCountBits(shape,NULL,NULL);
1889     swf_SetShapeBits(tag,shape);
1890     swf_ShapeSetAll(tag,shape,/*x*/0,/*y*/0,0,fsid,0);
1891     swflastx = swflasty = 0;
1892     moveto(tag, p1);
1893     lineto(tag, p2);
1894     lineto(tag, p3);
1895     lineto(tag, p4);
1896     lineto(tag, p1);
1897     swf_ShapeSetEnd(tag);
1898
1899     /* shape2 */
1900     myshapeid2 = ++currentswfid;
1901     tag = swf_InsertTag(tag,ST_DEFINESHAPE3);
1902     swf_ShapeNew(&shape);
1903     rgb.r = rgb.b = rgb.a = rgb.g = 255;
1904     rgb.a = 40;
1905     fsid = swf_ShapeAddSolidFillStyle(shape,&rgb);
1906     swf_SetU16(tag, myshapeid2);
1907     r.xmin = (int)(xmin*20);
1908     r.ymin = (int)(ymin*20);
1909     r.xmax = (int)(xmax*20);
1910     r.ymax = (int)(ymax*20);
1911     swf_SetRect(tag,&r);
1912     swf_SetShapeStyles(tag,shape);
1913     swf_ShapeCountBits(shape,NULL,NULL);
1914     swf_SetShapeBits(tag,shape);
1915     swf_ShapeSetAll(tag,shape,/*x*/0,/*y*/0,0,fsid,0);
1916     swflastx = swflasty = 0;
1917     moveto(tag, p1);
1918     lineto(tag, p2);
1919     lineto(tag, p3);
1920     lineto(tag, p4);
1921     lineto(tag, p1);
1922     swf_ShapeSetEnd(tag);
1923
1924     if(!mouseover)
1925     {
1926         tag = swf_InsertTag(tag,ST_DEFINEBUTTON);
1927         swf_SetU16(tag,buttonid); //id
1928         swf_ButtonSetFlags(tag, 0); //menu=no
1929         swf_ButtonSetRecord(tag,0x01,myshapeid,depth,0,0);
1930         swf_ButtonSetRecord(tag,0x02,myshapeid2,depth,0,0);
1931         swf_ButtonSetRecord(tag,0x04,myshapeid2,depth,0,0);
1932         swf_ButtonSetRecord(tag,0x08,myshapeid,depth,0,0);
1933         swf_SetU8(tag,0);
1934         swf_ActionSet(tag,actions1);
1935         swf_SetU8(tag,0);
1936     }
1937     else
1938     {
1939         tag = swf_InsertTag(tag,ST_DEFINEBUTTON2);
1940         swf_SetU16(tag,buttonid); //id
1941         swf_ButtonSetFlags(tag, 0); //menu=no
1942         swf_ButtonSetRecord(tag,0x01,myshapeid,depth,0,0);
1943         swf_ButtonSetRecord(tag,0x02,myshapeid2,depth,0,0);
1944         swf_ButtonSetRecord(tag,0x04,myshapeid2,depth,0,0);
1945         swf_ButtonSetRecord(tag,0x08,myshapeid,depth,0,0);
1946         swf_SetU8(tag,0); // end of button records
1947         swf_ButtonSetCondition(tag, BC_IDLE_OVERUP);
1948         swf_ActionSet(tag,actions1);
1949         if(actions2) {
1950             swf_ButtonSetCondition(tag, BC_OVERUP_IDLE);
1951             swf_ActionSet(tag,actions2);
1952             swf_SetU8(tag,0);
1953             swf_ButtonPostProcess(tag, 2);
1954         } else {
1955             swf_SetU8(tag,0);
1956             swf_ButtonPostProcess(tag, 1);
1957         }
1958     }
1959     
1960     tag = swf_InsertTag(tag,ST_PLACEOBJECT2);
1961
1962     if(posx!=0 || posy!=0) {
1963         MATRIX m;
1964         swf_GetMatrix(0,&m);
1965         m.tx = (int)(posx*20);
1966         m.ty = (int)(posy*20);
1967         swf_ObjectPlace(tag, buttonid, depth++,&m,0,0);
1968     }
1969     else {
1970         swf_ObjectPlace(tag, buttonid, depth++,0,0,0);
1971     }
1972 }
1973
1974 static void drawimage(struct swfoutput*obj, int bitid, int sizex,int sizey, 
1975         double x1,double y1,
1976         double x2,double y2,
1977         double x3,double y3,
1978         double x4,double y4)
1979 {
1980     RGBA rgb;
1981     SRECT r;
1982     int lsid=0;
1983     int fsid;
1984     struct plotxy p1,p2,p3,p4;
1985     int myshapeid;
1986     double xmax=x1,ymax=y1,xmin=x1,ymin=y1;
1987     if(x2>xmax) xmax=x2;
1988     if(y2>ymax) ymax=y2;
1989     if(x2<xmin) xmin=x2;
1990     if(y2<ymin) ymin=y2;
1991     if(x3>xmax) xmax=x3;
1992     if(y3>ymax) ymax=y3;
1993     if(x3<xmin) xmin=x3;
1994     if(y3<ymin) ymin=y3;
1995     if(x4>xmax) xmax=x4;
1996     if(y4>ymax) ymax=y4;
1997     if(x4<xmin) xmin=x4;
1998     if(y4<ymin) ymin=y4;
1999     p1.x=x1; p1.y=y1;
2000     p2.x=x2; p2.y=y2;
2001     p3.x=x3; p3.y=y3;
2002     p4.x=x4; p4.y=y4;
2003
2004     {p1.x = (int)(p1.x*20)/20.0;
2005      p1.y = (int)(p1.y*20)/20.0;
2006      p2.x = (int)(p2.x*20)/20.0;
2007      p2.y = (int)(p2.y*20)/20.0;
2008      p3.x = (int)(p3.x*20)/20.0;
2009      p3.y = (int)(p3.y*20)/20.0;
2010      p4.x = (int)(p4.x*20)/20.0;
2011      p4.y = (int)(p4.y*20)/20.0;}
2012     
2013     MATRIX m;
2014     m.sx = (int)(65536*20*(p4.x-p1.x)/sizex);
2015     m.r1 = -(int)(65536*20*(p4.y-p1.y)/sizex);
2016     m.r0 = (int)(65536*20*(p1.x-p2.x)/sizey);
2017     m.sy = -(int)(65536*20*(p1.y-p2.y)/sizey);
2018
2019     m.tx = (int)(p1.x*20);
2020     m.ty = (int)(p1.y*20);
2021   
2022     /* shape */
2023     myshapeid = ++currentswfid;
2024     tag = swf_InsertTag(tag,ST_DEFINESHAPE);
2025     swf_ShapeNew(&shape);
2026     //lsid = ShapeAddLineStyle(shape,linewidth,&obj->strokergb);
2027     //fsid = ShapeAddSolidFillStyle(shape,&obj->fillrgb);
2028     fsid = swf_ShapeAddBitmapFillStyle(shape,&m,bitid,1);
2029     swf_SetU16(tag, myshapeid);
2030     r.xmin = (int)(xmin*20);
2031     r.ymin = (int)(ymin*20);
2032     r.xmax = (int)(xmax*20);
2033     r.ymax = (int)(ymax*20);
2034     swf_SetRect(tag,&r);
2035     swf_SetShapeStyles(tag,shape);
2036     swf_ShapeCountBits(shape,NULL,NULL);
2037     swf_SetShapeBits(tag,shape);
2038     swf_ShapeSetAll(tag,shape,/*x*/0,/*y*/0,lsid,fsid,0);
2039     swflastx = swflasty = 0;
2040     moveto(tag, p1);
2041     lineto(tag, p2);
2042     lineto(tag, p3);
2043     lineto(tag, p4);
2044     lineto(tag, p1);
2045     /*
2046     ShapeMoveTo  (tag, shape, (int)(x1*20),(int)(y1*20));
2047     ShapeSetLine (tag, shape, (int)(x1*20);
2048     ShapeSetLine (tag, shape, x*20,0);
2049     ShapeSetLine (tag, shape, 0,-y*20);
2050     ShapeSetLine (tag, shape, -x*20,0);*/
2051     swf_ShapeSetEnd(tag);
2052
2053     /* instance */
2054     tag = swf_InsertTag(tag,ST_PLACEOBJECT2);
2055     swf_ObjectPlace(tag,myshapeid,/*depth*/depth++,NULL,NULL,NULL);
2056 }
2057
2058 int swfoutput_drawimagejpeg_old(struct swfoutput*obj, char*filename, int sizex,int sizey, 
2059         double x1,double y1,
2060         double x2,double y2,
2061         double x3,double y3,
2062         double x4,double y4)
2063 {
2064     TAG*oldtag;
2065     if(shapeid>=0)
2066         endshape(obj,0);
2067     if(textid>=0)
2068         endtext(obj);
2069
2070     int bitid = ++currentswfid;
2071     oldtag = tag;
2072     tag = swf_InsertTag(tag,ST_DEFINEBITSJPEG2);
2073     swf_SetU16(tag, bitid);
2074     if(swf_SetJPEGBits(tag, filename, jpegquality)<0) {
2075         swf_DeleteTag(tag);
2076         tag = oldtag;
2077         return -1;
2078     }
2079
2080     drawimage(obj, bitid, sizex, sizey, x1,y1,x2,y2,x3,y3,x4,y4);
2081     return bitid;
2082 }
2083
2084 int swfoutput_drawimagejpeg(struct swfoutput*obj, RGBA*mem, int sizex,int sizey, 
2085         double x1,double y1,
2086         double x2,double y2,
2087         double x3,double y3,
2088         double x4,double y4)
2089 {
2090     TAG*oldtag;
2091     JPEGBITS*jpeg;
2092
2093     if(shapeid>=0)
2094         endshape(obj,0);
2095     if(textid>=0)
2096         endtext(obj);
2097
2098     int bitid = ++currentswfid;
2099     oldtag = tag;
2100     tag = swf_InsertTag(tag,ST_DEFINEBITSJPEG2);
2101     swf_SetU16(tag, bitid);
2102     swf_SetJPEGBits2(tag,sizex,sizey,mem,jpegquality);
2103     drawimage(obj, bitid, sizex, sizey, x1,y1,x2,y2,x3,y3,x4,y4);
2104     return bitid;
2105 }
2106
2107 int swfoutput_drawimagelossless(struct swfoutput*obj, RGBA*mem, int sizex,int sizey, 
2108         double x1,double y1,
2109         double x2,double y2,
2110         double x3,double y3,
2111         double x4,double y4)
2112 {
2113     TAG*oldtag;
2114     if(shapeid>=0)
2115         endshape(obj,0);
2116     if(textid>=0)
2117         endtext(obj);
2118
2119     int bitid = ++currentswfid;
2120     oldtag = tag;
2121     tag = swf_InsertTag(tag,ST_DEFINEBITSLOSSLESS);
2122     swf_SetU16(tag, bitid);
2123     if(swf_SetLosslessBits(tag,sizex,sizey,mem, BMF_32BIT)<0) {
2124         swf_DeleteTag(tag);
2125         tag = oldtag;
2126         return -1;
2127     }
2128     
2129     drawimage(obj, bitid, sizex, sizey, x1,y1,x2,y2,x3,y3,x4,y4);
2130     return bitid;
2131 }
2132
2133 int swfoutput_drawimagelosslessN(struct swfoutput*obj, U8*mem, RGBA*pal, int sizex,int sizey, 
2134         double x1,double y1,
2135         double x2,double y2,
2136         double x3,double y3,
2137         double x4,double y4, int n)
2138 {
2139     TAG*oldtag;
2140     U8*mem2 = 0;
2141     if(shapeid>=0)
2142         endshape(obj,0);
2143     if(textid>=0)
2144         endtext(obj);
2145
2146     if(sizex&3)
2147     { 
2148         /* SWF expects scanlines to be 4 byte aligned */
2149         int x,y;
2150         U8*ptr;
2151         mem2 = (U8*)malloc(BYTES_PER_SCANLINE(sizex)*sizey);
2152         ptr = mem2;
2153         for(y=0;y<sizey;y++)
2154         {
2155             for(x=0;x<sizex;x++)
2156                 *ptr++ = mem[y*sizex+x];
2157             ptr+= BYTES_PER_SCANLINE(sizex)-sizex;
2158         }
2159         mem = mem2;
2160     }
2161
2162     int bitid = ++currentswfid;
2163     oldtag = tag;
2164     tag = swf_InsertTag(tag,ST_DEFINEBITSLOSSLESS2);
2165     swf_SetU16(tag, bitid);
2166     if(swf_SetLosslessBitsIndexed(tag,sizex,sizey,mem, pal, n)<0) {
2167         swf_DeleteTag(tag);
2168         tag = oldtag;
2169         return -1;
2170     }
2171     if(mem2)
2172         free(mem2);
2173   
2174     drawimage(obj, bitid, sizex, sizey, x1,y1,x2,y2,x3,y3,x4,y4);
2175     return bitid;
2176 }
2177
2178 void swfoutput_drawimageagain(struct swfoutput*obj, int id, int sizex,int sizey, 
2179         double x1,double y1,
2180         double x2,double y2,
2181         double x3,double y3,
2182         double x4,double y4)
2183 {
2184     if(id<0) return;
2185     if(shapeid>=0)
2186         endshape(obj,0);
2187     if(textid>=0)
2188         endtext(obj);
2189
2190     drawimage(obj, id, sizex, sizey, x1,y1,x2,y2,x3,y3,x4,y4);
2191 }
2192
2193 void swfoutput_setparameter(char*name, char*value)
2194 {
2195     if(!strcmp(name, "drawonlyshapes")) {
2196         drawonlyshapes = atoi(value);
2197     } else if(!strcmp(name, "ignoredraworder")) {
2198         ignoredraworder = atoi(value);
2199     } else if(!strcmp(name, "filloverlap")) {
2200         filloverlap = atoi(value);
2201     } else if(!strcmp(name, "linksopennewwindow")) {
2202         opennewwindow = atoi(value);
2203     } else if(!strcmp(name, "opennewwindow")) {
2204         opennewwindow = atoi(value);
2205     } else if(!strcmp(name, "storeallcharacters")) {
2206         storeallcharacters = atoi(value);
2207     } else if(!strcmp(name, "enablezlib")) {
2208         enablezlib = atoi(value);
2209     } else if(!strcmp(name, "insertstop")) {
2210         insertstoptag = atoi(value);
2211     } else if(!strcmp(name, "flashversion")) {
2212         flashversion = atoi(value);
2213     } else if(!strcmp(name, "minlinewidth")) {
2214         minlinewidth = atof(value);
2215     } else if(!strcmp(name, "jpegquality")) {
2216         int val = atoi(value);
2217         if(val<0) val=0;
2218         if(val>100) val=100;
2219         jpegquality = val;
2220     } else if(!strcmp(name, "splinequality")) {
2221         int v = atoi(value);
2222         v = 500-(v*5); // 100% = 0.25 pixel, 0% = 25 pixel
2223         if(v<1) v = 1;
2224         splinemaxerror = v;
2225     } else if(!strcmp(name, "fontquality")) {
2226         int v = atoi(value);
2227         v = 500-(v*5); // 100% = 0.25 pixel, 0% = 25 pixel
2228         if(v<1) v = 1;
2229         fontsplinemaxerror = v;
2230     } else {
2231         fprintf(stderr, "unknown parameter: %s (=%s)\n", name, value);
2232     }
2233 }
2234