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