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