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