removed a debug statement.
[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 }
1072
1073 /* free all tables, write out definefont tags */
1074 SWFFont::~SWFFont()
1075 {
1076     int t,usednum=0;
1077     int*ptr; 
1078
1079     if(storeallcharacters)
1080     {
1081         int t;
1082         for(t=0;t<this->charnum;t++) 
1083         {
1084             if(this->charname[t])
1085               getSWFCharID(this->charname[t], -1);
1086         }
1087     }
1088     
1089     ptr = (int*)malloc(swfcharpos*sizeof(int));
1090
1091     for(t=0;t<charnum;t++)
1092         if(used[t]) usednum++;
1093
1094     if(usednum && !drawonlyshapes)
1095     {
1096         msg("<verbose> Font %s has %d used characters",FIXNULL(fontid), usednum);
1097         TAG*ftag = swf_InsertTag(swf.firstTag,ST_DEFINEFONT);
1098         swf_SetU16(ftag, this->swfid);
1099         int initpos = swf_GetTagLen(ftag);
1100         swfmatrix m;
1101         m.m11 = m.m22 = 1;
1102         m.m21 = m.m12 = 0;
1103         m.m13 = CHARMIDX;
1104         m.m23 = CHARMIDY;
1105
1106         for(t=0;t<swfcharpos;t++) 
1107         {
1108             ptr[t] = swf_GetTagLen(ftag);
1109             swf_SetU16(ftag, 0x1234);
1110         }
1111         for(t=0;t<swfcharpos;t++)
1112         {
1113             *(U16*)&ftag->data[ptr[t]] = 
1114                 SWAP16(swf_GetTagLen(ftag)-initpos);
1115
1116             swflastx=0;
1117             swflasty=0;
1118             swf_SetU8(ftag,0x10); //1 fill bits, 0 linestyle bits
1119             SHAPE s;
1120             s.bits.fill = 1;
1121             s.bits.line = 0;
1122             swf_ShapeSetStyle(ftag,&s,0,1,0);
1123             fillstylechanged = 1;
1124             int lastfill = fill;
1125             fill = 1;
1126             storefont = 1;
1127             drawpath(ftag, outline[swfcharid2char[t]],&m, 0);
1128             storefont = 0;
1129             fill = lastfill;
1130             swf_ShapeSetEnd(ftag);
1131         }
1132         ftag = swf_InsertTag(ftag,ST_DEFINEFONTINFO);
1133         swf_SetU16(ftag, this->swfid);
1134         if(this->fontid) {
1135             swf_SetU8(ftag, strlen(this->fontid));
1136             swf_SetBlock(ftag, (U8*)this->fontid, strlen(this->fontid));
1137         } else {
1138             swf_SetU8(ftag, 0);
1139         }
1140         swf_SetU8(ftag, 0); //flags
1141         for(t=0;t<swfcharpos;t++)
1142         {
1143             int s;
1144             char * name = this->charname[this->swfcharid2char[t]];
1145             for(s=0;s<256;s++) {
1146                 if(standardEncodingNames[s] && 
1147                         !strcmp(name,standardEncodingNames[s]))
1148                     break;
1149             }
1150             swf_SetU8(ftag, (U8)s);
1151         }
1152     }
1153
1154     free(ptr);
1155     free(outline);
1156     for(t=0;t<charnum;t++)
1157         free(charname[t]);
1158     for(t=0;t<standardtablesize;t++)
1159         if(standardtable[t]) {
1160             free(standardtable[t]);
1161         }
1162     free(standardtable);
1163     free(charname);
1164     free(width);
1165     free(used);
1166     free(swfcharid2char);
1167     free(char2swfcharid);
1168 }
1169
1170 T1_OUTLINE*SWFFont::getOutline(char*name, int charnr)
1171 {
1172     int t;
1173     for(t=0;t<this->charnum;t++) {
1174         if(!strcmp(this->charname[t],name)) {
1175             return outline[t];
1176         }
1177     }
1178     
1179     /* if we didn't find the character, maybe
1180        we can find the capitalized version */
1181     for(t=0;t<this->charnum;t++) {
1182         if(!strcasecmp(this->charname[t],name))
1183             return outline[t];
1184     }
1185
1186     /* if we didn't find it by name, use the names of the first 256 characters
1187        of the font to try a new name based on charnr */
1188     if(this->standardtable && charnr>=0 && charnr < this->standardtablesize) {
1189         return getOutline(this->standardtable[charnr], -1);
1190     }
1191
1192     msg("<warning> Didn't find character '%s' in font '%s'", FIXNULL(name), this->name);
1193     return 0;
1194 }
1195
1196 int SWFFont::getSWFCharID(char*name, int charnr)
1197 {
1198     int t;
1199     for(t=0;t<this->charnum;t++) {
1200         if(!strcmp(this->charname[t],name)) {
1201             if(!used[t])
1202             {
1203                 swfcharid2char[swfcharpos] = t;
1204                 char2swfcharid[t] = swfcharpos++;
1205                 used[t] = 1;
1206             }
1207             return char2swfcharid[t];
1208         }
1209     }
1210
1211     /* if we didn't find the character, maybe
1212        we can find the capitalized version */
1213     for(t=0;t<this->charnum;t++) {
1214         if(!strcasecmp(this->charname[t],name)) {
1215             if(!used[t])
1216             {
1217                 swfcharid2char[swfcharpos] = t;
1218                 char2swfcharid[t] = swfcharpos++;
1219                 used[t] = 1;
1220             }
1221             return char2swfcharid[t];
1222         }
1223     }
1224
1225     /* if we didn't find it by name, use the names of the first 256 (or so) characters
1226        of the font to try a new name based on charnr */
1227     if(this->standardtable && charnr>=0 && charnr < this->standardtablesize) {
1228         return getSWFCharID(this->standardtable[charnr], -1);
1229     }
1230     msg("<warning> Didn't find character '%s' in font '%s'", FIXNULL(name), this->name);
1231     return 0;
1232 }
1233
1234 int SWFFont::getWidth(char*name)
1235 {
1236     int t;
1237     for(t=0;t<this->charnum;t++) {
1238         if(!strcmp(this->charname[t],name)) {
1239             return this->width[t];
1240         }
1241     }
1242     return 0;
1243 }
1244
1245 char*SWFFont::getName()
1246 {
1247     return this->name;
1248 }
1249
1250 struct fontlist_t 
1251 {
1252     SWFFont * font;
1253     fontlist_t*next;
1254 } *fontlist = 0;
1255
1256 /* set's the t1 font index of the font to use for swfoutput_drawchar(). */
1257 void swfoutput_setfont(struct swfoutput*obj, char*fontid, int t1id, char*filename)
1258 {
1259     fontlist_t*last=0,*iterator;
1260     if(obj->font && !strcmp(obj->font->fontid,fontid))
1261         return;
1262
1263     iterator = fontlist;
1264     while(iterator) {
1265         if(!strcmp(iterator->font->fontid,fontid))
1266             break;
1267         last = iterator;
1268         iterator = iterator->next;
1269     }
1270     if(iterator) 
1271     {
1272         obj->font = iterator->font;
1273         return ;
1274     }
1275
1276     if(t1id<0) {
1277         msg("<error> internal error: t1id:%d, fontid:%s\n", t1id,FIXNULL(fontid));
1278     }
1279     
1280     SWFFont*font = new SWFFont(fontid, t1id, filename);
1281     iterator = new fontlist_t;
1282     iterator->font = font;
1283     iterator->next = 0;
1284
1285     if(last) 
1286         last->next = iterator;
1287     else 
1288         fontlist = iterator;
1289     obj->font = font;
1290 }
1291
1292 int swfoutput_queryfont(struct swfoutput*obj, char*fontid)
1293 {
1294     fontlist_t *iterator = fontlist;
1295     while(iterator) {
1296         if(!strcmp(iterator->font->fontid,fontid))
1297             return 1;
1298         iterator = iterator->next;
1299     }
1300     return 0;
1301 }
1302
1303 /* set's the matrix which is to be applied to characters drawn by
1304    swfoutput_drawchar() */
1305 void swfoutput_setfontmatrix(struct swfoutput*obj,double m11,double m12,
1306                                                   double m21,double m22)
1307 {
1308     if(obj->fontm11 == m11 &&
1309        obj->fontm12 == m12 &&
1310        obj->fontm21 == m21 &&
1311        obj->fontm22 == m22)
1312         return;
1313 //    if(textid>=0)
1314 //      endtext();
1315     obj->fontm11 = m11;
1316     obj->fontm12 = m12;
1317     obj->fontm21 = m21;
1318     obj->fontm22 = m22;
1319 }
1320
1321 /* draws a character at x,y. */
1322 void swfoutput_drawchar(struct swfoutput* obj,double x,double y,char*character, int charnr) 
1323 {
1324     swfmatrix m;
1325     m.m11 = obj->fontm11;
1326     m.m12 = obj->fontm12;
1327     m.m21 = obj->fontm21;
1328     m.m22 = obj->fontm22;
1329     m.m13 = x;
1330     m.m23 = y;
1331     drawchar(obj, obj->font, character, charnr, &m);
1332 }
1333
1334 /* initialize the swf writer */
1335 void swfoutput_init(struct swfoutput* obj, char*_filename, int _sizex, int _sizey) 
1336 {
1337   GLYPH *glyph;
1338   RGBA rgb;
1339   SRECT r;
1340   memset(obj, 0, sizeof(struct swfoutput));
1341   filename = _filename;
1342   sizex = _sizex;
1343   sizey = _sizey;
1344
1345   msg("<verbose> initializing swf output for size %d*%d\n", sizex,sizey);
1346
1347   obj->font = 0;
1348   
1349   memset(&swf,0x00,sizeof(SWF));
1350
1351   swf.fileVersion    = flashversion;
1352   swf.frameRate      = 0x0040; // 1 frame per 4 seconds
1353   swf.movieSize.xmax = 20*sizex;
1354   swf.movieSize.ymax = 20*sizey;
1355   
1356   swf.firstTag = swf_InsertTag(NULL,ST_SETBACKGROUNDCOLOR);
1357   tag = swf.firstTag;
1358   rgb.r = 0xff;
1359   rgb.g = 0xff;
1360   rgb.b = 0xff;
1361   swf_SetRGB(tag,&rgb);
1362   if(flag_protected)  // good practice! /r
1363     tag = swf_InsertTag(tag, ST_PROTECT);
1364   depth = 1;
1365   startdepth = depth;
1366 }
1367
1368 void swfoutput_setprotected() //write PROTECT tag
1369 {
1370   flag_protected = 1;
1371 }
1372
1373 static void startshape(struct swfoutput*obj)
1374 {
1375   RGBA rgb;
1376   SRECT r;
1377
1378   if(textid>=0)
1379       endtext();
1380
1381   tag = swf_InsertTag(tag,ST_DEFINESHAPE);
1382
1383   swf_ShapeNew(&shape);
1384   linestyleid = swf_ShapeAddLineStyle(shape,obj->linewidth,&obj->strokergb);
1385   rgb.r = obj->fillrgb.r;
1386   rgb.g = obj->fillrgb.g;
1387   rgb.b = obj->fillrgb.b;
1388   fillstyleid = swf_ShapeAddSolidFillStyle(shape,&obj->fillrgb);
1389
1390   shapeid = ++currentswfid;
1391   swf_SetU16(tag,shapeid);  // ID
1392
1393   r.xmin = 0;
1394   r.ymin = 0;
1395   r.xmax = 20*sizex;
1396   r.ymax = 20*sizey;
1397   
1398   swf_SetRect(tag,&r);
1399
1400   swf_SetShapeStyles(tag,shape);
1401   swf_ShapeCountBits(shape,NULL,NULL);
1402   swf_SetShapeBits(tag,shape);
1403
1404   swf_ShapeSetAll(tag,shape,/*x*/0,/*y*/0,linestyleid,0,0);
1405   swflastx=swflasty=0;
1406   lastwasfill = 0;
1407 }
1408
1409 static void starttext(struct swfoutput*obj)
1410 {
1411   SRECT r;
1412   MATRIX m;
1413   if(shapeid>=0)
1414       endshape();
1415   tag = swf_InsertTag(tag,ST_DEFINETEXT);
1416   textid = ++currentswfid;
1417   swf_SetU16(tag, textid);
1418
1419   r.xmin = 0;
1420   r.ymin = 0;
1421   r.xmax = 20*sizex;
1422   r.ymax = 20*sizey;
1423   
1424   swf_SetRect(tag,&r);
1425
1426   m.sx = 65536;
1427   m.sy = 65536;
1428   m.r0 = 0;
1429   m.r1 = 0;
1430   m.tx = 0;
1431   m.ty = 0;
1432  
1433   swf_SetMatrix(tag,&m);
1434   swflastx=swflasty=0;
1435 }
1436
1437 static void endshape()
1438 {
1439     if(shapeid<0) 
1440         return;
1441     swf_ShapeSetEnd(tag);
1442     tag = swf_InsertTag(tag,ST_PLACEOBJECT2);
1443     swf_ObjectPlace(tag,shapeid,/*depth*/depth++,NULL,NULL,NULL);
1444     shapeid = -1;
1445 }
1446
1447 static void endtext()
1448 {
1449     if(textid<0)
1450         return;
1451     putcharacters(tag);
1452     swf_SetU8(tag,0);
1453     tag = swf_InsertTag(tag,ST_PLACEOBJECT2);
1454     swf_ObjectPlace(tag,textid,/*depth*/depth++,NULL,NULL,NULL);
1455     textid = -1;
1456 }
1457
1458 static void endpage(struct swfoutput*obj)
1459 {
1460     if(shapeid>=0)
1461       endshape();
1462     if(textid>=0)
1463       endtext();
1464     while(clippos)
1465         swfoutput_endclip(obj);
1466
1467     if(insertstoptag) {
1468         ActionTAG*atag=0;
1469         atag = action_Stop(atag);
1470         atag = action_End(atag);
1471         tag = swf_InsertTag(tag,ST_DOACTION);
1472         swf_ActionSet(tag,atag);
1473     }
1474     tag = swf_InsertTag(tag,ST_SHOWFRAME);
1475 }
1476
1477 void swfoutput_newpage(struct swfoutput*obj)
1478 {
1479     endpage(obj);
1480
1481     for(depth--;depth>=startdepth;depth--) {
1482         tag = swf_InsertTag(tag,ST_REMOVEOBJECT2);
1483         swf_SetU16(tag,depth);
1484     }
1485
1486     depth = 1;
1487     startdepth = depth;
1488 }
1489
1490 /* "destroy" like in (oo-terminology) "destructor". Perform cleaning
1491    up, complete the swf, and write it out. */
1492 void swfoutput_destroy(struct swfoutput* obj) 
1493 {
1494     endpage(obj);
1495     fontlist_t *tmp,*iterator = fontlist;
1496     while(iterator) {
1497         delete iterator->font;
1498         iterator->font = 0;
1499         tmp = iterator;
1500         iterator = iterator->next;
1501         delete tmp;
1502     }
1503
1504     T1_CloseLib();
1505     if(!filename) 
1506         return;
1507     if(filename)
1508      fi = open(filename, O_BINARY|O_CREAT|O_TRUNC|O_WRONLY, 0777);
1509     else
1510      fi = 1; // stdout
1511     
1512     if(fi<=0) {
1513      msg("<fatal> Could not create \"%s\". ", FIXNULL(filename));
1514      exit(1);
1515     }
1516  
1517     tag = swf_InsertTag(tag,ST_END);
1518
1519     if(enablezlib) {
1520       if FAILED(swf_WriteSWC(fi,&swf)) 
1521        msg("<error> WriteSWC() failed.\n");
1522     } else {
1523       if FAILED(swf_WriteSWF(fi,&swf)) 
1524        msg("<error> WriteSWF() failed.\n");
1525     }
1526
1527     if(filename)
1528      close(fi);
1529     msg("<notice> SWF written\n");
1530 }
1531
1532 void swfoutput_setdrawmode(swfoutput* obj, int mode)
1533 {
1534     drawmode = mode;
1535     if(mode == DRAWMODE_FILL)
1536      fill = 1;
1537     else if(mode == DRAWMODE_EOFILL)
1538      fill = 1;
1539     else if(mode == DRAWMODE_STROKE)
1540      fill = 0;
1541     else if(mode == DRAWMODE_CLIP)
1542      fill = 1;
1543     else if(mode == DRAWMODE_EOCLIP)
1544      fill = 1;
1545 }
1546
1547 void swfoutput_setfillcolor(swfoutput* obj, u8 r, u8 g, u8 b, u8 a)
1548 {
1549     if(obj->fillrgb.r == r &&
1550        obj->fillrgb.g == g &&
1551        obj->fillrgb.b == b &&
1552        obj->fillrgb.a == a) return;
1553     if(shapeid>=0)
1554      endshape();
1555
1556     obj->fillrgb.r = r;
1557     obj->fillrgb.g = g;
1558     obj->fillrgb.b = b;
1559     obj->fillrgb.a = a;
1560 }
1561
1562 void swfoutput_setstrokecolor(swfoutput* obj, u8 r, u8 g, u8 b, u8 a)
1563 {
1564     if(obj->strokergb.r == r &&
1565        obj->strokergb.g == g &&
1566        obj->strokergb.b == b &&
1567        obj->strokergb.a == a) return;
1568
1569     if(shapeid>=0)
1570      endshape();
1571     obj->strokergb.r = r;
1572     obj->strokergb.g = g;
1573     obj->strokergb.b = b;
1574     obj->strokergb.a = a;
1575 }
1576
1577 void swfoutput_setlinewidth(struct swfoutput*obj, double linewidth)
1578 {
1579     if(obj->linewidth == (u16)(linewidth*20))
1580         return;
1581
1582     if(shapeid>=0)
1583      endshape();
1584     obj->linewidth = (u16)(linewidth*20);
1585 }
1586
1587
1588 void swfoutput_startclip(swfoutput*obj, T1_OUTLINE*outline, struct swfmatrix*m)
1589 {
1590     if(textid>=0)
1591      endtext();
1592     if(shapeid>=0)
1593      endshape();
1594
1595     if(clippos >= 127)
1596     {
1597         msg("<warning> Too many clip levels.");
1598         clippos --;
1599     } 
1600     
1601     startshape(obj);
1602     int olddrawmode = drawmode;
1603     swfoutput_setdrawmode(obj, DRAWMODE_CLIP);
1604     swfoutput_drawpath(obj, outline, m);
1605     swf_ShapeSetEnd(tag);
1606     swfoutput_setdrawmode(obj, olddrawmode);
1607
1608     tag = swf_InsertTag(tag,ST_PLACEOBJECT2);
1609     cliptags[clippos] = tag;
1610     clipshapes[clippos] = shapeid;
1611     clipdepths[clippos] = depth++;
1612     clippos++;
1613     shapeid = -1;
1614 }
1615
1616 void swfoutput_endclip(swfoutput*obj)
1617 {
1618     if(textid>=0)
1619      endtext();
1620     if(shapeid>=0)
1621      endshape();
1622
1623     if(!clippos) {
1624         msg("<error> Invalid end of clipping region");
1625         return;
1626     }
1627     clippos--;
1628     swf_ObjectPlaceClip(cliptags[clippos],clipshapes[clippos],clipdepths[clippos],NULL,NULL,NULL,depth++);
1629 }
1630
1631 static void drawlink(struct swfoutput*obj, ActionTAG*,ActionTAG*, swfcoord*points, char mouseover);
1632
1633 void swfoutput_linktourl(struct swfoutput*obj, char*url, swfcoord*points)
1634 {
1635     ActionTAG* actions;
1636     if(!strncmp("http://pdf2swf:", url, 15)) {
1637      char*tmp = strdup(url);
1638      int l = strlen(tmp);
1639      if(tmp[l-1] == '/')
1640         tmp[l-1] = 0;
1641      swfoutput_namedlink(obj, tmp+15, points);
1642      free(tmp);
1643      return;
1644     }
1645     
1646     if(shapeid>=0)
1647      endshape();
1648     if(textid>=0)
1649      endtext();
1650     
1651     if(opennewwindow)
1652       actions = action_GetUrl(0, url, "_parent");
1653     else
1654       actions = action_GetUrl(0, url, "_this");
1655     actions = action_End(actions);
1656     
1657     drawlink(obj, actions, 0, points,0);
1658 }
1659 void swfoutput_linktopage(struct swfoutput*obj, int page, swfcoord*points)
1660 {
1661     ActionTAG* actions;
1662
1663     if(shapeid>=0)
1664      endshape();
1665     if(textid>=0)
1666      endtext();
1667    
1668       actions = action_GotoFrame(0, page);
1669       actions = action_End(actions);
1670
1671     drawlink(obj, actions, 0, points,0);
1672 }
1673
1674 /* Named Links (a.k.a. Acrobatmenu) are used to implement various gadgets
1675    of the viewer objects, like subtitles, index elements etc.
1676 */
1677 void swfoutput_namedlink(struct swfoutput*obj, char*name, swfcoord*points)
1678 {
1679     ActionTAG *actions1,*actions2;
1680     char*tmp = strdup(name);
1681     char mouseover = 1;
1682
1683     if(shapeid>=0)
1684      endshape();
1685     if(textid>=0)
1686      endtext();
1687
1688     if(!strncmp(tmp, "call:", 5))
1689     {
1690         char*x = strchr(&tmp[5], ':');
1691         if(!x) {
1692             actions1 = action_PushInt(0, 0); //number of parameters (0)
1693             actions1 = action_PushString(actions1, &tmp[5]); //function name
1694             actions1 = action_CallFunction(actions1);
1695         } else {
1696             *x = 0;
1697             actions1 = action_PushString(0, x+1); //parameter
1698             actions1 = action_PushInt(actions1, 1); //number of parameters (1)
1699             actions1 = action_PushString(actions1, &tmp[5]); //function name
1700             actions1 = action_CallFunction(actions1);
1701         }
1702         actions2 = action_End(0);
1703         mouseover = 0;
1704     }
1705     else
1706     {
1707         actions1 = action_PushString(0, "/:subtitle");
1708         actions1 = action_PushString(actions1, name);
1709         actions1 = action_SetVariable(actions1);
1710         actions1 = action_End(actions1);
1711
1712         actions2 = action_PushString(0, "/:subtitle");
1713         actions2 = action_PushString(actions2, "");
1714         actions2 = action_SetVariable(actions2);
1715         actions2 = action_End(actions2);
1716     }
1717
1718     drawlink(obj, actions1, actions2, points,mouseover);
1719
1720     swf_ActionFree(actions1);
1721     swf_ActionFree(actions2);
1722     free(tmp);
1723 }
1724
1725 static void drawlink(struct swfoutput*obj, ActionTAG*actions1, ActionTAG*actions2, swfcoord*points, char mouseover)
1726 {
1727     RGBA rgb;
1728     SRECT r;
1729     int lsid=0;
1730     int fsid;
1731     struct plotxy p1,p2,p3,p4;
1732     int myshapeid;
1733     int myshapeid2;
1734     double xmin,ymin;
1735     double xmax=xmin=points[0].x,ymax=ymin=points[0].y;
1736     double posx = 0;
1737     double posy = 0;
1738     int t;
1739     int buttonid = ++currentswfid;
1740     for(t=1;t<4;t++)
1741     {
1742         if(points[t].x>xmax) xmax=points[t].x;
1743         if(points[t].y>ymax) ymax=points[t].y;
1744         if(points[t].x<xmin) xmin=points[t].x;
1745         if(points[t].y<ymin) ymin=points[t].y;
1746     }
1747    
1748     p1.x=points[0].x; p1.y=points[0].y; p2.x=points[1].x; p2.y=points[1].y; 
1749     p3.x=points[2].x; p3.y=points[2].y; p4.x=points[3].x; p4.y=points[3].y;
1750    
1751     /* the following code subtracts the upper left edge from all coordinates,
1752        and set's posx,posy so that ST_PLACEOBJECT is used with a matrix.
1753        Necessary for preprocessing with swfcombine. */
1754     posx = xmin; posy = ymin;
1755     p1.x-=posx;p2.x-=posx;p3.x-=posx;p4.x-=posx;
1756     p1.y-=posy;p2.y-=posy;p3.y-=posy;p4.y-=posy;
1757     xmin -= posx; ymin -= posy;
1758     xmax -= posx; ymax -= posy;
1759     
1760     /* shape */
1761     myshapeid = ++currentswfid;
1762     tag = swf_InsertTag(tag,ST_DEFINESHAPE3);
1763     swf_ShapeNew(&shape);
1764     rgb.r = rgb.b = rgb.a = rgb.g = 0; 
1765     fsid = swf_ShapeAddSolidFillStyle(shape,&rgb);
1766     swf_SetU16(tag, myshapeid);
1767     r.xmin = (int)(xmin*20);
1768     r.ymin = (int)(ymin*20);
1769     r.xmax = (int)(xmax*20);
1770     r.ymax = (int)(ymax*20);
1771     swf_SetRect(tag,&r);
1772     swf_SetShapeStyles(tag,shape);
1773     swf_ShapeCountBits(shape,NULL,NULL);
1774     swf_SetShapeBits(tag,shape);
1775     swf_ShapeSetAll(tag,shape,/*x*/0,/*y*/0,0,fsid,0);
1776     swflastx = swflasty = 0;
1777     moveto(tag, p1);
1778     lineto(tag, p2);
1779     lineto(tag, p3);
1780     lineto(tag, p4);
1781     lineto(tag, p1);
1782     swf_ShapeSetEnd(tag);
1783
1784     /* shape2 */
1785     myshapeid2 = ++currentswfid;
1786     tag = swf_InsertTag(tag,ST_DEFINESHAPE3);
1787     swf_ShapeNew(&shape);
1788     rgb.r = rgb.b = rgb.a = rgb.g = 255;
1789     rgb.a = 40;
1790     fsid = swf_ShapeAddSolidFillStyle(shape,&rgb);
1791     swf_SetU16(tag, myshapeid2);
1792     r.xmin = (int)(xmin*20);
1793     r.ymin = (int)(ymin*20);
1794     r.xmax = (int)(xmax*20);
1795     r.ymax = (int)(ymax*20);
1796     swf_SetRect(tag,&r);
1797     swf_SetShapeStyles(tag,shape);
1798     swf_ShapeCountBits(shape,NULL,NULL);
1799     swf_SetShapeBits(tag,shape);
1800     swf_ShapeSetAll(tag,shape,/*x*/0,/*y*/0,0,fsid,0);
1801     swflastx = swflasty = 0;
1802     moveto(tag, p1);
1803     lineto(tag, p2);
1804     lineto(tag, p3);
1805     lineto(tag, p4);
1806     lineto(tag, p1);
1807     swf_ShapeSetEnd(tag);
1808
1809     if(!mouseover)
1810     {
1811         tag = swf_InsertTag(tag,ST_DEFINEBUTTON);
1812         swf_SetU16(tag,buttonid); //id
1813         swf_ButtonSetFlags(tag, 0); //menu=no
1814         swf_ButtonSetRecord(tag,0x01,myshapeid,depth,0,0);
1815         swf_ButtonSetRecord(tag,0x02,myshapeid2,depth,0,0);
1816         swf_ButtonSetRecord(tag,0x04,myshapeid2,depth,0,0);
1817         swf_ButtonSetRecord(tag,0x08,myshapeid,depth,0,0);
1818         swf_SetU8(tag,0);
1819         swf_ActionSet(tag,actions1);
1820         swf_SetU8(tag,0);
1821     }
1822     else
1823     {
1824         tag = swf_InsertTag(tag,ST_DEFINEBUTTON2);
1825         swf_SetU16(tag,buttonid); //id
1826         swf_ButtonSetFlags(tag, 0); //menu=no
1827         swf_ButtonSetRecord(tag,0x01,myshapeid,depth,0,0);
1828         swf_ButtonSetRecord(tag,0x02,myshapeid2,depth,0,0);
1829         swf_ButtonSetRecord(tag,0x04,myshapeid2,depth,0,0);
1830         swf_ButtonSetRecord(tag,0x08,myshapeid,depth,0,0);
1831         swf_SetU8(tag,0); // end of button records
1832         swf_ButtonSetCondition(tag, BC_IDLE_OVERUP);
1833         swf_ActionSet(tag,actions1);
1834         if(actions2) {
1835             swf_ButtonSetCondition(tag, BC_OVERUP_IDLE);
1836             swf_ActionSet(tag,actions2);
1837             swf_SetU8(tag,0);
1838             swf_ButtonPostProcess(tag, 2);
1839         } else {
1840             swf_SetU8(tag,0);
1841             swf_ButtonPostProcess(tag, 1);
1842         }
1843     }
1844     
1845     tag = swf_InsertTag(tag,ST_PLACEOBJECT2);
1846
1847     if(posx!=0 || posy!=0) {
1848         MATRIX m;
1849         swf_GetMatrix(0,&m);
1850         m.tx = (int)(posx*20);
1851         m.ty = (int)(posy*20);
1852         swf_ObjectPlace(tag, buttonid, depth++,&m,0,0);
1853     }
1854     else {
1855         swf_ObjectPlace(tag, buttonid, depth++,0,0,0);
1856     }
1857 }
1858
1859 static void drawimage(struct swfoutput*obj, int bitid, int sizex,int sizey, 
1860         double x1,double y1,
1861         double x2,double y2,
1862         double x3,double y3,
1863         double x4,double y4)
1864 {
1865     RGBA rgb;
1866     SRECT r;
1867     int lsid=0;
1868     int fsid;
1869     struct plotxy p1,p2,p3,p4;
1870     int myshapeid;
1871     double xmax=x1,ymax=y1,xmin=x1,ymin=y1;
1872     if(x2>xmax) xmax=x2;
1873     if(y2>ymax) ymax=y2;
1874     if(x2<xmin) xmin=x2;
1875     if(y2<ymin) ymin=y2;
1876     if(x3>xmax) xmax=x3;
1877     if(y3>ymax) ymax=y3;
1878     if(x3<xmin) xmin=x3;
1879     if(y3<ymin) ymin=y3;
1880     if(x4>xmax) xmax=x4;
1881     if(y4>ymax) ymax=y4;
1882     if(x4<xmin) xmin=x4;
1883     if(y4<ymin) ymin=y4;
1884     p1.x=x1; p1.y=y1;
1885     p2.x=x2; p2.y=y2;
1886     p3.x=x3; p3.y=y3;
1887     p4.x=x4; p4.y=y4;
1888
1889     {p1.x = (int)(p1.x*20)/20.0;
1890      p1.y = (int)(p1.y*20)/20.0;
1891      p2.x = (int)(p2.x*20)/20.0;
1892      p2.y = (int)(p2.y*20)/20.0;
1893      p3.x = (int)(p3.x*20)/20.0;
1894      p3.y = (int)(p3.y*20)/20.0;
1895      p4.x = (int)(p4.x*20)/20.0;
1896      p4.y = (int)(p4.y*20)/20.0;}
1897     
1898     MATRIX m;
1899     m.sx = (int)(65536*20*(p4.x-p1.x)/sizex);
1900     m.r1 = -(int)(65536*20*(p4.y-p1.y)/sizex);
1901     m.r0 = (int)(65536*20*(p1.x-p2.x)/sizey);
1902     m.sy = -(int)(65536*20*(p1.y-p2.y)/sizey);
1903
1904     m.tx = (int)(p1.x*20);
1905     m.ty = (int)(p1.y*20);
1906   
1907     /* shape */
1908     myshapeid = ++currentswfid;
1909     tag = swf_InsertTag(tag,ST_DEFINESHAPE);
1910     swf_ShapeNew(&shape);
1911     //lsid = ShapeAddLineStyle(shape,obj->linewidth,&obj->strokergb);
1912     //fsid = ShapeAddSolidFillStyle(shape,&obj->fillrgb);
1913     fsid = swf_ShapeAddBitmapFillStyle(shape,&m,bitid,1);
1914     swf_SetU16(tag, myshapeid);
1915     r.xmin = (int)(xmin*20);
1916     r.ymin = (int)(ymin*20);
1917     r.xmax = (int)(xmax*20);
1918     r.ymax = (int)(ymax*20);
1919     swf_SetRect(tag,&r);
1920     swf_SetShapeStyles(tag,shape);
1921     swf_ShapeCountBits(shape,NULL,NULL);
1922     swf_SetShapeBits(tag,shape);
1923     swf_ShapeSetAll(tag,shape,/*x*/0,/*y*/0,lsid,fsid,0);
1924     swflastx = swflasty = 0;
1925     moveto(tag, p1);
1926     lineto(tag, p2);
1927     lineto(tag, p3);
1928     lineto(tag, p4);
1929     lineto(tag, p1);
1930     /*
1931     ShapeMoveTo  (tag, shape, (int)(x1*20),(int)(y1*20));
1932     ShapeSetLine (tag, shape, (int)(x1*20);
1933     ShapeSetLine (tag, shape, x*20,0);
1934     ShapeSetLine (tag, shape, 0,-y*20);
1935     ShapeSetLine (tag, shape, -x*20,0);*/
1936     swf_ShapeSetEnd(tag);
1937
1938     /* instance */
1939     tag = swf_InsertTag(tag,ST_PLACEOBJECT2);
1940     swf_ObjectPlace(tag,myshapeid,/*depth*/depth++,NULL,NULL,NULL);
1941 }
1942
1943 int swfoutput_drawimagejpeg_old(struct swfoutput*obj, char*filename, 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();
1954
1955     int bitid = ++currentswfid;
1956     oldtag = tag;
1957     tag = swf_InsertTag(tag,ST_DEFINEBITSJPEG2);
1958     swf_SetU16(tag, bitid);
1959     if(swf_SetJPEGBits(tag, filename, jpegquality)<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_drawimagejpeg(struct swfoutput*obj, RGBA*mem, int sizex,int sizey, 
1970         double x1,double y1,
1971         double x2,double y2,
1972         double x3,double y3,
1973         double x4,double y4)
1974 {
1975     TAG*oldtag;
1976     JPEGBITS*jpeg;
1977
1978     if(shapeid>=0)
1979      endshape();
1980     if(textid>=0)
1981      endtext();
1982
1983     int bitid = ++currentswfid;
1984     oldtag = tag;
1985     tag = swf_InsertTag(tag,ST_DEFINEBITSJPEG2);
1986     swf_SetU16(tag, bitid);
1987     swf_SetJPEGBits2(tag,sizex,sizey,mem,jpegquality);
1988     drawimage(obj, bitid, sizex, sizey, x1,y1,x2,y2,x3,y3,x4,y4);
1989     return bitid;
1990 }
1991
1992 int swfoutput_drawimagelossless(struct swfoutput*obj, RGBA*mem, int sizex,int sizey, 
1993         double x1,double y1,
1994         double x2,double y2,
1995         double x3,double y3,
1996         double x4,double y4)
1997 {
1998     TAG*oldtag;
1999     if(shapeid>=0)
2000      endshape();
2001     if(textid>=0)
2002      endtext();
2003
2004     int bitid = ++currentswfid;
2005     oldtag = tag;
2006     tag = swf_InsertTag(tag,ST_DEFINEBITSLOSSLESS);
2007     swf_SetU16(tag, bitid);
2008     if(swf_SetLosslessBits(tag,sizex,sizey,mem, BMF_32BIT)<0) {
2009         swf_DeleteTag(tag);
2010         tag = oldtag;
2011         return -1;
2012     }
2013     
2014     drawimage(obj, bitid, sizex, sizey, x1,y1,x2,y2,x3,y3,x4,y4);
2015     return bitid;
2016 }
2017
2018 int swfoutput_drawimagelosslessN(struct swfoutput*obj, U8*mem, RGBA*pal, int sizex,int sizey, 
2019         double x1,double y1,
2020         double x2,double y2,
2021         double x3,double y3,
2022         double x4,double y4, int n)
2023 {
2024     TAG*oldtag;
2025     U8*mem2 = 0;
2026     if(shapeid>=0)
2027      endshape();
2028     if(textid>=0)
2029      endtext();
2030
2031     if(sizex&3)
2032     { 
2033         /* SWF expects scanlines to be 4 byte aligned */
2034         int x,y;
2035         U8*ptr;
2036         mem2 = (U8*)malloc(BYTES_PER_SCANLINE(sizex)*sizey);
2037         ptr = mem2;
2038         for(y=0;y<sizey;y++)
2039         {
2040             for(x=0;x<sizex;x++)
2041                 *ptr++ = mem[y*sizex+x];
2042             ptr+= BYTES_PER_SCANLINE(sizex)-sizex;
2043         }
2044         mem = mem2;
2045     }
2046
2047     int bitid = ++currentswfid;
2048     oldtag = tag;
2049     tag = swf_InsertTag(tag,ST_DEFINEBITSLOSSLESS2);
2050     swf_SetU16(tag, bitid);
2051     if(swf_SetLosslessBitsIndexed(tag,sizex,sizey,mem, pal, n)<0) {
2052         swf_DeleteTag(tag);
2053         tag = oldtag;
2054         return -1;
2055     }
2056     if(mem2)
2057         free(mem2);
2058   
2059     drawimage(obj, bitid, sizex, sizey, x1,y1,x2,y2,x3,y3,x4,y4);
2060     return bitid;
2061 }
2062
2063 void swfoutput_drawimageagain(struct swfoutput*obj, int id, int sizex,int sizey, 
2064         double x1,double y1,
2065         double x2,double y2,
2066         double x3,double y3,
2067         double x4,double y4)
2068 {
2069     if(id<0) return;
2070     if(shapeid>=0)
2071      endshape();
2072     if(textid>=0)
2073      endtext();
2074
2075     drawimage(obj, id, sizex, sizey, x1,y1,x2,y2,x3,y3,x4,y4);
2076 }
2077