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