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