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