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