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