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