* added freetype support in ttf2pt1
[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 #define logf logarithmf // logf is also used by ../lib/log.h
33 #include <math.h>
34 #undef logf
35 #include "swfoutput.h"
36 #include "spline.h"
37 extern "C" {
38 #include "../lib/log.h"
39 #include "../lib/rfxswf.h"
40 }
41 #define standardEncodingSize 335
42 extern char *standardEncodingNames[standardEncodingSize];
43
44 int opennewwindow=0;
45 int ignoredraworder=0;
46 int drawonlyshapes=0;
47 int jpegquality=85;
48 int storeallcharacters=0;
49 int enablezlib=0;
50 int insertstoptag=0;
51 int flashversion=4;
52 int splinemaxerror=1;
53 int fontsplinemaxerror=1;
54 static int flag_protected = 0;
55
56 typedef unsigned char u8;
57 typedef unsigned short int u16;
58 typedef unsigned long int u32;
59
60 static int fi;
61 static char* filename = 0;
62 static SWF swf;
63 static TAG *tag;
64 static int currentswfid = 0;
65 static int depth = 1;
66 static int startdepth = 1;
67
68 static SHAPE* shape;
69 static int shapeid = -1;
70 static int textid = -1;
71
72 static int drawmode = -1;
73 static char storefont = 0;
74 static int fillstyleid;
75 static int linestyleid;
76 static int swflastx=0;
77 static int swflasty=0;
78 static int lastwasfill = 0;
79 static char fill = 0;
80 static int sizex;
81 static int sizey;
82 TAG* cliptags[128];
83 int clipshapes[128];
84 u32 clipdepths[128];
85 int clippos = 0;
86
87 int CHARMIDX = 0;
88 int CHARMIDY = 0;
89
90 char fillstylechanged = 0;
91
92 static void startshape(struct swfoutput* obj);
93 static void starttext(struct swfoutput* obj);
94 static void endshape();
95 static void endtext();
96
97 // matrix multiplication. changes p0
98 static void transform (plotxy*p0,struct swfmatrix*m)
99 {
100     double x,y;
101     x = m->m11*p0->x+m->m12*p0->y;
102     y = m->m21*p0->x+m->m22*p0->y;
103     p0->x = x + m->m13;
104     p0->y = y + m->m23;
105 }
106
107 // write a move-to command into the swf
108 static int moveto(TAG*tag, plotxy p0)
109 {
110     int rx = (int)(p0.x*20);
111     int ry = (int)(p0.y*20);
112     if(rx!=swflastx || ry!=swflasty || fillstylechanged) {
113       swf_ShapeSetMove (tag, shape, rx,ry);
114       fillstylechanged = 0;
115       swflastx=rx;
116       swflasty=ry;
117       return 1;
118     }
119     return 0;
120 }
121
122 // write a line-to command into the swf
123 static void lineto(TAG*tag, plotxy p0)
124 {
125     int rx = ((int)(p0.x*20)-swflastx);
126     int ry = ((int)(p0.y*20)-swflasty);
127     /* we can't skip this for rx=0,ry=0, those
128        are plots */
129     swf_ShapeSetLine (tag, shape, rx,ry);
130     swflastx+=rx;
131     swflasty+=ry;
132 }
133
134 // write a spline-to command into the swf
135 static void splineto(TAG*tag, plotxy control,plotxy end)
136 {
137     int cx = ((int)(control.x*20)-swflastx);
138     int cy = ((int)(control.y*20)-swflasty);
139     swflastx += cx;
140     swflasty += cy;
141     int ex = ((int)(end.x*20)-swflastx);
142     int ey = ((int)(end.y*20)-swflasty);
143     swflastx += ex;
144     swflasty += ey;
145     if(cx || cy || ex || ey)
146         swf_ShapeSetCurve(tag, shape, cx,cy,ex,ey);
147 }
148
149 /* write a line, given two points and the transformation
150    matrix. */
151 static void line(TAG*tag, plotxy p0, plotxy p1, struct swfmatrix*m)
152 {
153     transform(&p0,m);
154     transform(&p1,m);
155     moveto(tag, p0);
156     lineto(tag, p1);
157 }
158
159 /* write a cubic (!) spline. This involves calling the approximate()
160    function out of spline.cc to convert it to a quadratic spline.  */
161 static void spline(TAG*tag,plotxy p0,plotxy p1,plotxy p2,plotxy p3,struct swfmatrix*m)
162 {
163     double d;
164     struct qspline q[128];
165     int num;
166     int t;
167     transform(&p0,m);
168     transform(&p1,m);
169     transform(&p2,m);
170     transform(&p3,m);
171     cspline c;
172     c.start = p3;
173     c.control1 = p2;
174     c.control2 = p1;
175     c.end = p0;
176
177     if(storefont) {
178         /* fonts use a different approximation than shapes */
179         num = cspline_approximate(&c, q, fontsplinemaxerror/20.0, APPROXIMATE_RECURSIVE_BINARY);
180         //num = cspline_approximate(&c, q, 10.0, APPROXIMATE_INFLECTION);
181     } else {
182         num = cspline_approximate(&c, q,     splinemaxerror/20.0, APPROXIMATE_RECURSIVE_BINARY);
183     }
184     for(t=0;t<num;t++) {
185         if(!t) 
186             moveto(tag,q[t].start);
187         splineto(tag,q[t].control, q[t].end);
188     }
189 }
190
191 void resetdrawer()
192 {
193     swflastx = 0;
194     swflasty = 0;
195 }
196
197 static void stopFill()
198 {
199     if(lastwasfill)
200     {
201      swf_ShapeSetStyle(tag,shape,linestyleid,0x8000,0);
202      fillstylechanged = 1;
203      lastwasfill = 0;
204     }
205 }
206 static void startFill()
207 {
208     if(!lastwasfill)
209     {
210      swf_ShapeSetStyle(tag,shape,0x8000,fillstyleid,0);
211      fillstylechanged = 1;
212      lastwasfill = 1;
213     }
214 }
215
216 /* draw a T1 outline. These are generated by pdf2swf and by t1lib
217    (representing characters). */
218 void drawpath(TAG*tag, T1_OUTLINE*outline, struct swfmatrix*m, int log)
219 {
220     if(tag->id != ST_DEFINEFONT &&
221         tag->id != ST_DEFINESHAPE &&
222         tag->id != ST_DEFINESHAPE2 &&
223         tag->id != ST_DEFINESHAPE3)
224     {
225         logf("<error> internal error: drawpath needs a shape tag, not %d\n",tag->id);
226         exit(1);
227     }
228     double x=0,y=0;
229     double lastx=0,lasty=0;
230     double firstx=0,firsty=0;
231     int init=1;
232
233     while (outline)
234     {
235         x += (outline->dest.x/(float)0xffff);
236         y += (outline->dest.y/(float)0xffff);
237         if(outline->type == T1_PATHTYPE_MOVE)
238         {
239             if(((int)(lastx*20) != (int)(firstx*20) ||
240                 (int)(lasty*20) != (int)(firsty*20)) &&
241                      fill && !init)
242             {
243                 plotxy p0;
244                 plotxy p1;
245                 p0.x=lastx;
246                 p0.y=lasty;
247                 p1.x=firstx;
248                 p1.y=firsty;
249                 if(log) printf("fix: %f,%f -> %f,%f\n",p0.x,p0.y,p1.x,p1.y);
250                 line(tag, p0, p1, m);
251             }
252             firstx=x;
253             firsty=y;
254             init = 0;
255         }
256         else if(outline->type == T1_PATHTYPE_LINE) 
257         {
258             plotxy p0;
259             plotxy p1;
260             p0.x=lastx;
261             p0.y=lasty;
262             p1.x=x;
263             p1.y=y;
264             if(log) printf("line: %f,%f -> %f,%f\n",p0.x,p0.y,p1.x,p1.y);
265             line(tag, p0,p1,m);
266         }
267         else if(outline->type == T1_PATHTYPE_BEZIER)
268         {
269             plotxy p0;
270             plotxy p1;
271             plotxy p2;
272             plotxy p3;
273             T1_BEZIERSEGMENT*o2 = (T1_BEZIERSEGMENT*)outline;
274             p0.x=x; 
275             p0.y=y;
276             p1.x=o2->C.x/(float)0xffff+lastx;
277             p1.y=o2->C.y/(float)0xffff+lasty;
278             p2.x=o2->B.x/(float)0xffff+lastx;
279             p2.y=o2->B.y/(float)0xffff+lasty;
280             p3.x=lastx;
281             p3.y=lasty;
282             if(log) printf("spline: %f,%f -> %f,%f\n",p3.x,p3.y,p0.x,p0.y);
283             spline(tag,p0,p1,p2,p3,m);
284         } 
285         else {
286             logf("<error> drawpath: unknown outline type:%d\n", outline->type);
287         }
288         lastx=x;
289         lasty=y;
290         outline = outline->link;
291     }
292     if(((int)(lastx*20) != (int)(firstx*20) ||
293         (int)(lasty*20) != (int)(firsty*20)) &&
294              fill)
295     {
296         plotxy p0;
297         plotxy p1;
298         p0.x=lastx;
299         p0.y=lasty;
300         p1.x=firstx;
301         p1.y=firsty;
302         if(log) printf("fix: %f,%f -> %f,%f\n",p0.x,p0.y,p1.x,p1.y);
303         line(tag, p0, p1, m);
304     }
305 }
306
307 plotxy getPivot(T1_OUTLINE*outline, int dir, double line_width, int end, int trytwo)
308 {
309     T1_PATHPOINT next, next2;
310     double xv=0,yv=0, xv2=0, yv2=0;
311     plotxy p;
312     int two = 0;
313
314     if(!end) {
315         if(outline->type == T1_PATHTYPE_LINE) {
316             next = outline->dest;
317         } else {
318             next = ((T1_BEZIERSEGMENT*)outline)->B;
319             if(next.x==0 && next.y==0) {
320                 next = ((T1_BEZIERSEGMENT*)outline)->C;
321             }
322             if(next.x==0 && next.y==0) {
323                 next = ((T1_BEZIERSEGMENT*)outline)->dest;
324             }
325         }
326         next2 = next;
327         if(trytwo && outline->last && outline->last->type != T1_PATHTYPE_MOVE) {
328             if(outline->type == T1_PATHTYPE_LINE) {
329                 next2 = outline->last->dest;
330             } else {
331                 T1_PATHPOINT c = ((T1_BEZIERSEGMENT*)(outline->last))->C;
332                 T1_PATHPOINT b = ((T1_BEZIERSEGMENT*)(outline->last))->B;
333                 next2.x = outline->last->dest.x - c.x;
334                 next2.y = outline->last->dest.y - c.y;
335                 if(next2.x==0 && next2.y==0) {
336                     next2.x = outline->last->dest.x - b.x;
337                     next2.y = outline->last->dest.y - b.y;
338                 }
339                 if(next2.x==0 && next2.y==0) {
340                     next2.x = outline->last->dest.x;
341                     next2.y = outline->last->dest.y;
342                 }
343             }
344             two = 1;
345         }
346     } else {
347         if(outline->type == T1_PATHTYPE_LINE) {
348             next = outline->dest;
349         } else {
350             T1_PATHPOINT c = ((T1_BEZIERSEGMENT*)outline)->C;
351             T1_PATHPOINT b = ((T1_BEZIERSEGMENT*)outline)->B;
352             next.x = outline->dest.x - c.x;
353             next.y = outline->dest.y - c.y;
354             if(next.x==0 && next.y==0) {
355                 next.x = outline->dest.x - b.x;
356                 next.y = outline->dest.y - b.y;
357             }
358             if(next.x==0 && next.y==0) {
359                 next.x = outline->dest.x;
360                 next.y = outline->dest.y;
361             }
362         }
363         next2 = next;
364         if(trytwo && outline->link && outline->link->type != T1_PATHTYPE_MOVE) {
365             if(outline->type == T1_PATHTYPE_LINE) {
366                 next2 = outline->link->dest;
367             } else {
368                 next2 = ((T1_BEZIERSEGMENT*)(outline->link))->B;
369                 if(next2.x==0 && next2.y==0) {
370                     next2 = ((T1_BEZIERSEGMENT*)outline->link)->C;
371                 }
372                 if(next2.x==0 && next2.y==0) {
373                     next2 = ((T1_BEZIERSEGMENT*)outline->link)->dest;
374                 }
375             }
376             two = 1;
377         }
378     }
379
380     if(dir) {
381         xv =  next.y/(float)0xffff;
382         yv = -next.x/(float)0xffff;
383     } else {
384         xv = -next.y/(float)0xffff;
385         yv =  next.x/(float)0xffff;
386     }
387
388     double r = (line_width/2)/sqrt(xv*xv+yv*yv);
389     xv*=r;
390     yv*=r;
391
392     if(two) {
393         if(dir) {
394             xv2 =  next2.y/(float)0xffff;
395             yv2 = -next2.x/(float)0xffff;
396         } else {
397             xv2 = -next2.y/(float)0xffff;
398             yv2 =  next2.x/(float)0xffff;
399         }
400
401         double r2 = (line_width/2)/sqrt(xv2*xv2+yv2*yv2);
402         xv2*=r2;
403         yv2*=r2;
404         xv = (xv+xv2)/2;
405         yv = (yv+yv2)/2;
406         double r3 = (line_width/2)/sqrt(xv*xv+yv*yv);
407         xv *= r3;
408         yv *= r3;
409     }
410
411     p.x = xv;
412     p.y = yv;
413     return p;
414 }
415
416 void drawShortPath(struct swfoutput*output, double x, double y, struct swfmatrix* m, T1_OUTLINE*outline)
417 {
418     double lastx=x, lasty=y;
419     while (outline && outline->type != T1_PATHTYPE_MOVE)
420     {
421         x += (outline->dest.x/(float)0xffff);
422         y += (outline->dest.y/(float)0xffff);
423
424         if(outline->type == T1_PATHTYPE_LINE)
425         {
426             plotxy p0, p1;
427             p0.x=lastx;
428             p0.y=lasty;
429             p1.x= x; 
430             p1.y= y;
431             line(tag, p0, p1, m);
432         }
433         else if(outline->type == T1_PATHTYPE_BEZIER)
434         {
435             plotxy p0,p1,p2,p3;
436             T1_BEZIERSEGMENT*o2 = (T1_BEZIERSEGMENT*)outline;
437             p3.x=lastx;
438             p3.y=lasty;
439             p1.x=o2->C.x/(float)0xffff+lastx;
440             p1.y=o2->C.y/(float)0xffff+lasty;
441             p2.x=o2->B.x/(float)0xffff+lastx;
442             p2.y=o2->B.y/(float)0xffff+lasty;
443             p0.x=x; 
444             p0.y=y;
445             spline(tag,p0,p1,p2,p3,m);
446         } 
447         lastx=x;
448         lasty=y;
449         outline = outline->link;
450     }
451 }
452
453 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)
454 {
455     plotxy d,d2;
456     int back = 0;
457
458     if(line_cap == LINE_CAP_BUTT || line_cap == LINE_CAP_SQUARE) {
459         endshape();
460         startshape(output);
461         T1_OUTLINE *last, *tmp=outline;
462         plotxy s,e,p0,p1,p2,p3,m0,m1,m2,m3;
463         double x2 = x;
464         double y2 = y;
465         double lx=x,ly=y;
466         double ee = 1.0;
467         int nr;
468         while(tmp && tmp->type != T1_PATHTYPE_MOVE) {
469             last = tmp;
470             lx += (tmp->dest.x/(float)0xffff);
471             ly += (tmp->dest.y/(float)0xffff);
472             tmp = tmp->link;
473         }
474         s = getPivot(outline, 0, line_width, 0, 0);
475         e = getPivot(last, 0, line_width, 1, 0);
476
477         if(line_cap == LINE_CAP_BUTT) {
478             /* make the clipping rectangle slighly bigger
479                than the line ending, so that it get's clipped
480                propertly */
481             //ee = 1.01;
482             ee=1.0;
483         }
484
485         p0.x = x2 + s.x*ee; 
486         p0.y = y2 + s.y*ee;
487         p1.x = x2 - s.x*ee; 
488         p1.y = y2 - s.y*ee;
489         p2.x = x2 - s.y - s.x*ee; 
490         p2.y = y2 + s.x - s.y*ee;
491         p3.x = x2 - s.y + s.x*ee; 
492         p3.y = y2 + s.x + s.y*ee;
493         m0.x = lx + e.x*ee; 
494         m0.y = ly + e.y*ee;
495         m1.x = lx - e.x*ee; 
496         m1.y = ly - e.y*ee;
497         m2.x = lx + e.y - e.x*ee; 
498         m2.y = ly - e.x - e.y*ee;
499         m3.x = lx + e.y + e.x*ee; 
500         m3.y = ly - e.x + e.y*ee;
501
502         for(nr=0;nr<2;nr++) {
503             int dir=0;
504             struct plotxy q0,q1,q2,q3,q4,q5;
505             startFill();
506             if(line_cap == LINE_CAP_BUTT) {
507                 if(dir) {
508                     q0.x = 0; q0.y = 0;
509                     q1.x = sizex; q1.y = 0;
510                     q2.x = sizex; q2.y = sizey;
511                     q3.x = 0; q3.y = sizey;
512                 } else {
513                     q0.x = sizex; q0.y = sizey;
514                     q1.x = 0; q1.y = sizey;
515                     q2.x = 0; q2.y = 0;
516                     q3.x = sizex; q3.y = 0;
517                 }
518                 q4.x = p0.x; 
519                 q4.y = p0.y;
520                 moveto(tag, q0);
521                 lineto(tag, q1);
522                 lineto(tag, q2);
523                 lineto(tag, q3);
524                 lineto(tag, q0);
525
526                 transform(&q4,m);
527                 lineto(tag, q4);
528             }
529
530             line(tag, p0, p1, m);
531             line(tag, p1, p2, m);
532             line(tag, p2, p3, m);
533             line(tag, p3, p0, m);
534
535             if(line_cap == LINE_CAP_BUTT) {
536                 lineto(tag, q0);
537                 swf_ShapeSetEnd(tag);
538                 tag = swf_InsertTag(tag, ST_PLACEOBJECT2);
539                 swf_ObjectPlaceClip(tag,shapeid,depth,NULL,NULL,NULL,depth+2-nr);
540                 depth++;
541                 shapeid = -1;
542                 startshape(output);
543             }
544             p0 = m0;
545             p1 = m1;
546             p2 = m2;
547             p3 = m3;
548         }
549
550         stopFill();
551     }
552
553     drawShortPath(output,x,y,m,outline);
554
555     if(line_cap == LINE_CAP_BUTT) {
556         endshape();
557         startshape(output);
558     }
559 }
560
561 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)
562 {
563     plotxy d1,d2,p1,p2,p3,p4;
564
565     d1.x = (outline->dest.x/(float)0xffff);
566     d1.y = (outline->dest.y/(float)0xffff);
567     d2 = getPivot(outline, 0, line_width, 0, 0);
568
569     assert(line_cap != LINE_CAP_ROUND);
570     if(line_cap == LINE_CAP_SQUARE) {
571         x -= +d2.y;
572         y -= -d2.x;
573         d1.x += +2*d2.y;
574         d1.y += -2*d2.x;
575     }
576
577     p1.x = x + d2.x;
578     p1.y = y + d2.y;
579     p2.x = x + d2.x + d1.x;
580     p2.y = y + d2.y + d1.y;
581     p3.x = x - d2.x + d1.x;
582     p3.y = y - d2.y + d1.y;
583     p4.x = x - d2.x;
584     p4.y = y - d2.y;
585
586     line(tag, p1,p2, m);
587     line(tag, p2,p3, m);
588     line(tag, p3,p4, m);
589     line(tag, p4,p1, m);
590 }
591
592 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)
593 {
594     T1_OUTLINE*tmp=outline;
595     double xx=x,yy=y;
596     int stop=0;
597     assert(shapeid>=0);
598
599     startFill();
600     drawT1toRect(output, x, y, m,outline, num, line_cap, line_join, line_width);
601
602     while(tmp->link && tmp->link->type!=T1_PATHTYPE_MOVE) {
603         xx += (tmp->dest.x/(float)0xffff);
604         yy += (tmp->dest.y/(float)0xffff);
605         tmp = tmp->link;
606     }
607     
608     assert(tmp->type == T1_PATHTYPE_LINE);
609     assert(outline->type == T1_PATHTYPE_LINE);
610     
611     if(tmp!=outline) {
612    
613         if(outline->link == tmp) {
614             /* the two straight line segments (which are everything we
615                need to draw) are very likely to overlap. To avoid that
616                they cancel each other out at the end points, start a new
617                shape for the second one */
618             endshape();startshape(output);
619             startFill();
620         }
621
622         drawT1toRect(output, xx, yy, m, tmp, num, line_cap, line_join, line_width);
623
624         if(outline->link != tmp)
625         {
626             stopFill();stop=1;
627             int save= tmp->type;
628             tmp->type = T1_PATHTYPE_MOVE;
629             x += (outline->dest.x/(float)0xffff);
630             y += (outline->dest.y/(float)0xffff);
631             outline = outline->link;
632             drawShortPath(output, x, y, m, outline);
633             tmp->type = save;
634         }
635     }
636     if(!stop)
637         stopFill();
638 }
639
640 static int t1len(T1_OUTLINE*line)
641 {
642     int num=0;
643     while(line && line->type != T1_PATHTYPE_MOVE) {
644         num++;
645         line = line->link;
646     }
647     return num;
648 }
649
650 static float t1linelen(T1_OUTLINE*line)
651 {
652     float x,y;
653     x = (line->dest.x/(float)0xffff);
654     y = (line->dest.y/(float)0xffff);
655     return sqrt(x*x+y*y);
656 }
657
658 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)
659 {
660     if(tag->id != ST_DEFINEFONT &&
661         tag->id != ST_DEFINESHAPE &&
662         tag->id != ST_DEFINESHAPE2 &&
663         tag->id != ST_DEFINESHAPE3) {
664         logf("<error> internal error: drawpath needs a shape tag, not %d\n",tag->id);
665         exit(1);
666     }
667     assert(shapeid>=0);
668     double x=0,y=0;
669     double lastx=0,lasty=0;
670     int valid = 0;
671     int lastwasline = 0;
672     T1_OUTLINE*tmp = outline, *last = 0;
673     tmp->last = 0;
674
675     while(1) {
676         if(tmp) {
677             x += (tmp->dest.x/(float)0xffff);
678             y += (tmp->dest.y/(float)0xffff);
679         }
680         if(!tmp || tmp->type == T1_PATHTYPE_MOVE) {
681             if(valid && last) {
682                 if(last->type == T1_PATHTYPE_LINE && t1linelen(last)>line_width*2 &&
683                    lastwasline && line_cap != LINE_CAP_ROUND)
684                     drawShortPathWithStraightEnds(output, lastx, lasty, m, last, valid, line_cap, line_join, line_width);
685                 else
686                     drawShortPathWithEnds(output, lastx, lasty, m, last, valid, line_cap, line_join, line_width);
687             }
688             if(!tmp)
689                 break;
690             valid = 0;
691             last = 0;
692             lastx = x;
693             lasty = y;
694         } else {
695             if(!last)
696                 last = tmp;
697             valid++;
698         }
699
700         if(tmp && tmp->type == T1_PATHTYPE_LINE && t1linelen(tmp)>line_width*2)
701             lastwasline = 1;
702         else
703             lastwasline = 0;
704
705         if(tmp->link)
706             tmp->link->last = tmp; // make sure list is properly linked in both directions
707         tmp = tmp->link;
708     }
709 }
710
711 static inline int colorcompare(RGBA*a,RGBA*b)
712 {
713
714     if(a->r!=b->r ||
715        a->g!=b->g ||
716        a->b!=b->b ||
717        a->a!=b->a) {
718         return 0;
719     }
720     return 1;
721 }
722
723 static const int CHARDATAMAX = 1024;
724 struct chardata {
725     int charid;
726     int fontid;
727     int x;
728     int y;
729     int size;
730     RGBA color;
731 } chardata[CHARDATAMAX];
732 int chardatapos = 0;
733
734 static void putcharacters(TAG*tag)
735 {
736     int t;
737     SWFFONT font;
738     RGBA color;
739     color.r = chardata[0].color.r^255;
740     color.g = 0;
741     color.b = 0;
742     color.a = 0;
743     int lastfontid;
744     int lastx;
745     int lasty;
746     int lastsize;
747     int charids[128];
748     int charadvance[128];
749     int charstorepos;
750     int pass;
751     int glyphbits=1; //TODO: can this be zero?
752     int advancebits=1;
753
754     if(tag->id != ST_DEFINETEXT &&
755         tag->id != ST_DEFINETEXT2) {
756         logf("<error> internal error: putcharacters needs an text tag, not %d\n",tag->id);
757         exit(1);
758     }
759     if(!chardatapos) {
760         logf("<warning> putcharacters called with zero characters");
761     }
762
763     for(pass = 0; pass < 2; pass++)
764     {
765         charstorepos = 0;
766         lastfontid = -1;
767         lastx = CHARMIDX;
768         lasty = CHARMIDY;
769         lastsize = -1;
770
771         if(pass==1)
772         {
773             advancebits++; // add sign bit
774             swf_SetU8(tag, glyphbits);
775             swf_SetU8(tag, advancebits);
776         }
777
778         for(t=0;t<=chardatapos;t++)
779         {
780             if(lastfontid != chardata[t].fontid || 
781                     lastx!=chardata[t].x ||
782                     lasty!=chardata[t].y ||
783                     !colorcompare(&color, &chardata[t].color) ||
784                     charstorepos==127 ||
785                     lastsize != chardata[t].size ||
786                     t == chardatapos)
787             {
788                 if(charstorepos && pass==0)
789                 {
790                     int s;
791                     for(s=0;s<charstorepos;s++)
792                     {
793                         while(charids[s]>=(1<<glyphbits))
794                             glyphbits++;
795                         while(charadvance[s]>=(1<<advancebits))
796                             advancebits++;
797                     }
798                 }
799                 if(charstorepos && pass==1)
800                 {
801                     tag->writeBit = 0; // Q&D
802                     swf_SetBits(tag, 0, 1); // GLYPH Record
803                     swf_SetBits(tag, charstorepos, 7); // number of glyphs
804                     int s;
805                     for(s=0;s<charstorepos;s++)
806                     {
807                         swf_SetBits(tag, charids[s], glyphbits);
808                         swf_SetBits(tag, charadvance[s], advancebits);
809                     }
810                 }
811                 charstorepos = 0;
812
813                 if(pass == 1 && t<chardatapos)
814                 {
815                     RGBA*newcolor=0;
816                     SWFFONT*newfont=0;
817                     int newx = 0;
818                     int newy = 0;
819                     if(lastx != chardata[t].x ||
820                        lasty != chardata[t].y)
821                     {
822                         newx=chardata[t].x;
823                         newy=chardata[t].y;
824                     }
825                     if(!colorcompare(&color, &chardata[t].color)) 
826                     {
827                         color = chardata[t].color;
828                         newcolor = &color;
829                     }
830                     font.id = chardata[t].fontid;
831                     if(lastfontid != chardata[t].fontid || lastsize != chardata[t].size)
832                         newfont = &font;
833
834                     tag->writeBit = 0; // Q&D
835                     swf_TextSetInfoRecord(tag, newfont, chardata[t].size, newcolor, newx,newy);
836                 }
837
838                 lastfontid = chardata[t].fontid;
839                 lastx = chardata[t].x;
840                 lasty = chardata[t].y;
841                 lastsize = chardata[t].size;
842             }
843
844             if(t==chardatapos)
845                     break;
846
847             int advance;
848             int nextt = t==chardatapos-1?t:t+1;
849             int rel = chardata[nextt].x-chardata[t].x;
850             if(rel>=0 && (rel<(1<<(advancebits-1)) || pass==0)) {
851                advance = rel;
852                lastx=chardata[nextt].x;
853             }
854             else {
855                advance = 0;
856                lastx=chardata[t].x;
857             }
858             charids[charstorepos] = chardata[t].charid;
859             charadvance[charstorepos] = advance;
860             charstorepos ++;
861         }
862     }
863     chardatapos = 0;
864 }
865
866 static void putcharacter(struct swfoutput*obj, int fontid, int charid, 
867                     int x,int y, int size)
868 {
869     if(chardatapos == CHARDATAMAX)
870     {
871         endtext();
872         starttext(obj);
873     }
874     chardata[chardatapos].fontid = fontid;
875     chardata[chardatapos].charid = charid;
876     chardata[chardatapos].x = x;
877     chardata[chardatapos].y = y;
878     chardata[chardatapos].color = obj->fillrgb;
879     chardata[chardatapos].size = size;
880     chardatapos++;
881 }
882
883
884 /* process a character. */
885 static void drawchar(struct swfoutput*obj, SWFFont*font, char*character, int charnr, swfmatrix*m)
886 {
887     int usefonts=1;
888     if(m->m12!=0 || m->m21!=0)
889         usefonts=0;
890     if(m->m11 != m->m22)
891         usefonts=0;
892
893     if(usefonts && ! drawonlyshapes)
894     {
895         int charid = font->getSWFCharID(character, charnr);
896         if(shapeid>=0)
897             endshape();
898         if(textid<0)
899             starttext(obj);
900         putcharacter(obj, font->swfid, charid,(int)(m->m13*20),(int)(m->m23*20),
901                 (int)(m->m11*20/2+0.5)); //where does the /2 come from?
902     }
903     else
904     {
905         T1_OUTLINE*outline = font->getOutline(character, charnr);
906         char* charname = character;
907
908         if(!outline) {
909          logf("<warning> Didn't find %s in current charset (%s)", 
910                  FIXNULL(character),FIXNULL(font->getName()));
911          return;
912         }
913         
914         swfmatrix m2=*m;    
915         m2.m11/=100;
916         m2.m21/=100;
917         m2.m12/=100;
918         m2.m22/=100;
919
920         if(textid>=0)
921             endtext();
922         if(shapeid<0)
923             startshape(obj);
924
925         startFill();
926
927         int lf = fill;
928         fill = 1;
929         drawpath(tag, outline, &m2, 0);
930         fill = lf;
931     }
932 }
933
934 /* draw a curved polygon. */
935 void swfoutput_drawpath(swfoutput*output, T1_OUTLINE*outline, 
936                             struct swfmatrix*m)
937 {
938     if(textid>=0)
939         endtext();
940
941     /* Multiple polygons in one shape don't overlap correctly, 
942        so we better start a new shape here if the polygon is filled
943      */
944     if(shapeid>=0 && fill && !ignoredraworder) {
945         endshape();
946     }
947
948     if(shapeid<0)
949         startshape(output);
950
951     if(!fill)
952         stopFill();
953     else
954         startFill();
955
956     drawpath(tag, outline,m, 0); 
957 }
958
959 void swfoutput_drawpath2poly(struct swfoutput*output, T1_OUTLINE*outline, struct swfmatrix*m, int line_join, int line_cap, double line_width, double miter_limit)
960 {
961     if(textid>=0)
962         endtext();
963     if(shapeid>=0)
964         endshape();
965     assert(shapeid<0);
966     startshape(output);
967     stopFill();
968
969     drawpath2poly(output, outline, m, 0, line_join, line_cap, line_width, miter_limit); 
970 }
971
972 /* SWFFont: copy all t1 font outlines to a local 
973    array. */
974 SWFFont::SWFFont(char*name, int id, char*filename)
975 {
976     if(!T1_GetFontName(id))
977         T1_LoadFont(id);
978
979     this->name = strdup(T1_GetFontFileName(id));
980     this->fontid = strdup(name);
981     this->t1id = id;
982     
983     char**a= T1_GetAllCharNames(id);
984     int t, outlinepos=0;
985     char*map[256];
986
987     t=0;
988     while(a[t])
989         t++;
990     this->charnum = t;
991
992     if(!charnum) 
993         return;
994     logf("<verbose> Font %s(%d): Storing %d outlines.\n", FIXNULL(name), id, charnum);
995
996     this->standardtablesize = 256;
997     if(this->charnum < this->standardtablesize)
998         this->standardtablesize = this->charnum;
999     this->standardtable = (char**)malloc(standardtablesize*sizeof(char*));
1000
1001     for(t = 0; t < this->standardtablesize; t++) {
1002         char*name = T1_GetCharName(id,t);
1003         if(!name)
1004             name = "";
1005         standardtable[t] = strdup(name);
1006     }
1007     
1008     outline = (T1_OUTLINE**)malloc(charnum*sizeof(T1_OUTLINE*));
1009     charname = (char**)malloc(charnum*sizeof(char*));
1010     width = (int*)malloc(charnum*sizeof(int));
1011     memset(width, 0, charnum*sizeof(int));
1012     memset(charname, 0, charnum*sizeof(char*));
1013     used = (char*)malloc(charnum*sizeof(char));
1014     char2swfcharid = (U16*)malloc(charnum*2);
1015     swfcharid2char = (U16*)malloc(charnum*2);
1016     swfcharpos = 0;
1017
1018     memset(used,0,charnum*sizeof(char));
1019
1020     this->swfid = ++currentswfid;
1021     
1022     t=0;
1023     while(*a)
1024     {
1025         map[t] = *a;
1026         a++;
1027         t++;
1028         if(t==256 || !*a) {
1029             int s;
1030             for(s=t;s<256;s++)
1031                 map[s] = ".notdef";
1032
1033             int ret = T1_ReencodeFont(id, map);
1034             if(ret) {
1035              T1_DeleteFont(id);
1036              T1_LoadFont(id);
1037              int ret = T1_ReencodeFont(id, map);
1038              if(ret)
1039                fprintf(stderr,"Can't reencode font: (%s) ret:%d\n",filename, ret);
1040             }
1041
1042             // parsecharacters
1043             for(s=0;s<t;s++)
1044             {
1045                 char* name = T1_GetCharName(id, s);
1046                 if(!name) name = "";
1047                 this->outline[outlinepos] = T1_CopyOutline(T1_GetCharOutline(id, s, 100.0, 0));
1048                 this->width[outlinepos] = T1_GetCharWidth(id, s);
1049                 this->charname[outlinepos] = strdup(name);
1050                 outlinepos++;
1051             }
1052             t=0;
1053         }
1054     }
1055 }
1056
1057 /* free all tables, write out definefont tags */
1058 SWFFont::~SWFFont()
1059 {
1060     int t,usednum=0;
1061     int*ptr; 
1062
1063     if(storeallcharacters)
1064     {
1065         int t;
1066         for(t=0;t<this->charnum;t++) 
1067         {
1068             if(this->charname[t])
1069               getSWFCharID(this->charname[t], -1);
1070         }
1071     }
1072     
1073     ptr = (int*)malloc(swfcharpos*sizeof(int));
1074
1075     for(t=0;t<charnum;t++)
1076         if(used[t]) usednum++;
1077
1078     if(usednum && !drawonlyshapes)
1079     {
1080         logf("<verbose> Font %s has %d used characters",FIXNULL(fontid), usednum);
1081         TAG*ftag = swf_InsertTag(swf.firstTag,ST_DEFINEFONT);
1082         swf_SetU16(ftag, this->swfid);
1083         int initpos = swf_GetTagLen(ftag);
1084         swfmatrix m;
1085         m.m11 = m.m22 = 1;
1086         m.m21 = m.m12 = 0;
1087         m.m13 = CHARMIDX;
1088         m.m23 = CHARMIDY;
1089
1090         for(t=0;t<swfcharpos;t++) 
1091         {
1092             ptr[t] = swf_GetTagLen(ftag);
1093             swf_SetU16(ftag, 0x1234);
1094         }
1095         for(t=0;t<swfcharpos;t++)
1096         {
1097             *(U16*)&ftag->data[ptr[t]] = 
1098                 SWAP16(swf_GetTagLen(ftag)-initpos);
1099
1100             swflastx=0;
1101             swflasty=0;
1102             swf_SetU8(ftag,0x10); //1 fill bits, 0 linestyle bits
1103             SHAPE s;
1104             s.bits.fill = 1;
1105             s.bits.line = 0;
1106             swf_ShapeSetStyle(ftag,&s,0,1,0);
1107             fillstylechanged = 1;
1108             int lastfill = fill;
1109             fill = 1;
1110             storefont = 1;
1111             drawpath(ftag, outline[swfcharid2char[t]],&m, 0);
1112             storefont = 0;
1113             fill = lastfill;
1114             swf_ShapeSetEnd(ftag);
1115         }
1116         ftag = swf_InsertTag(ftag,ST_DEFINEFONTINFO);
1117         swf_SetU16(ftag, this->swfid);
1118         if(this->fontid) {
1119             swf_SetU8(ftag, strlen(this->fontid));
1120             swf_SetBlock(ftag, (U8*)this->fontid, strlen(this->fontid));
1121         } else {
1122             swf_SetU8(ftag, 0);
1123         }
1124         swf_SetU8(ftag, 0); //flags
1125         for(t=0;t<swfcharpos;t++)
1126         {
1127             int s;
1128             char * name = this->charname[this->swfcharid2char[t]];
1129             for(s=0;s<256;s++) {
1130                 if(standardEncodingNames[s] && 
1131                         !strcmp(name,standardEncodingNames[s]))
1132                     break;
1133             }
1134             swf_SetU8(ftag, (U8)s);
1135         }
1136     }
1137
1138     free(ptr);
1139     free(outline);
1140     for(t=0;t<charnum;t++)
1141         free(charname[t]);
1142     for(t=0;t<standardtablesize;t++)
1143         if(standardtable[t]) {
1144             free(standardtable[t]);
1145         }
1146     free(standardtable);
1147     free(charname);
1148     free(width);
1149     free(used);
1150     free(swfcharid2char);
1151     free(char2swfcharid);
1152 }
1153
1154 T1_OUTLINE*SWFFont::getOutline(char*name, int charnr)
1155 {
1156     int t;
1157     for(t=0;t<this->charnum;t++) {
1158         if(!strcmp(this->charname[t],name)) {
1159             return outline[t];
1160         }
1161     }
1162     
1163     /* if we didn't find the character, maybe
1164        we can find the capitalized version */
1165     for(t=0;t<this->charnum;t++) {
1166         if(!strcasecmp(this->charname[t],name))
1167             return outline[t];
1168     }
1169
1170     /* if we didn't find it by name, use the names of the first 256 characters
1171        of the font to try a new name based on charnr */
1172     if(this->standardtable && charnr>=0 && charnr < this->standardtablesize) {
1173         return getOutline(this->standardtable[charnr], -1);
1174     }
1175
1176     logf("<warning> Didn't find character '%s' in font '%s'", FIXNULL(name), this->name);
1177     return 0;
1178 }
1179
1180 int SWFFont::getSWFCharID(char*name, int charnr)
1181 {
1182     int t;
1183     for(t=0;t<this->charnum;t++) {
1184         if(!strcmp(this->charname[t],name)) {
1185             if(!used[t])
1186             {
1187                 swfcharid2char[swfcharpos] = t;
1188                 char2swfcharid[t] = swfcharpos++;
1189                 used[t] = 1;
1190             }
1191             return char2swfcharid[t];
1192         }
1193     }
1194
1195     /* if we didn't find the character, maybe
1196        we can find the capitalized version */
1197     for(t=0;t<this->charnum;t++) {
1198         if(!strcasecmp(this->charname[t],name)) {
1199             if(!used[t])
1200             {
1201                 swfcharid2char[swfcharpos] = t;
1202                 char2swfcharid[t] = swfcharpos++;
1203                 used[t] = 1;
1204             }
1205             return char2swfcharid[t];
1206         }
1207     }
1208
1209     /* if we didn't find it by name, use the names of the first 256 (or so) characters
1210        of the font to try a new name based on charnr */
1211     if(this->standardtable && charnr>=0 && charnr < this->standardtablesize) {
1212         return getSWFCharID(this->standardtable[charnr], -1);
1213     }
1214     logf("<warning> Didn't find character '%s' in font '%s'", FIXNULL(name), this->name);
1215     return 0;
1216 }
1217
1218 int SWFFont::getWidth(char*name)
1219 {
1220     int t;
1221     for(t=0;t<this->charnum;t++) {
1222         if(!strcmp(this->charname[t],name)) {
1223             return this->width[t];
1224         }
1225     }
1226     return 0;
1227 }
1228
1229 char*SWFFont::getName()
1230 {
1231     return this->name;
1232 }
1233
1234 struct fontlist_t 
1235 {
1236     SWFFont * font;
1237     fontlist_t*next;
1238 } *fontlist = 0;
1239
1240 /* set's the t1 font index of the font to use for swfoutput_drawchar(). */
1241 void swfoutput_setfont(struct swfoutput*obj, char*fontid, int t1id, char*filename)
1242 {
1243     fontlist_t*last=0,*iterator;
1244     if(obj->font && !strcmp(obj->font->fontid,fontid))
1245         return;
1246
1247     iterator = fontlist;
1248     while(iterator) {
1249         if(!strcmp(iterator->font->fontid,fontid))
1250             break;
1251         last = iterator;
1252         iterator = iterator->next;
1253     }
1254     if(iterator) 
1255     {
1256         obj->font = iterator->font;
1257         return ;
1258     }
1259
1260     if(t1id<0) {
1261         logf("<error> internal error: t1id:%d, fontid:%s\n", t1id,FIXNULL(fontid));
1262     }
1263     
1264     SWFFont*font = new SWFFont(fontid, t1id, filename);
1265     iterator = new fontlist_t;
1266     iterator->font = font;
1267     iterator->next = 0;
1268
1269     if(last) 
1270         last->next = iterator;
1271     else 
1272         fontlist = iterator;
1273     obj->font = font;
1274 }
1275
1276 int swfoutput_queryfont(struct swfoutput*obj, char*fontid)
1277 {
1278     fontlist_t *iterator = fontlist;
1279     while(iterator) {
1280         if(!strcmp(iterator->font->fontid,fontid))
1281             return 1;
1282         iterator = iterator->next;
1283     }
1284     return 0;
1285 }
1286
1287 /* set's the matrix which is to be applied to characters drawn by
1288    swfoutput_drawchar() */
1289 void swfoutput_setfontmatrix(struct swfoutput*obj,double m11,double m12,
1290                                                   double m21,double m22)
1291 {
1292     if(obj->fontm11 == m11 &&
1293        obj->fontm12 == m12 &&
1294        obj->fontm21 == m21 &&
1295        obj->fontm22 == m22)
1296         return;
1297 //    if(textid>=0)
1298 //      endtext();
1299     obj->fontm11 = m11;
1300     obj->fontm12 = m12;
1301     obj->fontm21 = m21;
1302     obj->fontm22 = m22;
1303 }
1304
1305 /* draws a character at x,y. */
1306 void swfoutput_drawchar(struct swfoutput* obj,double x,double y,char*character, int charnr) 
1307 {
1308     swfmatrix m;
1309     m.m11 = obj->fontm11;
1310     m.m12 = obj->fontm12;
1311     m.m21 = obj->fontm21;
1312     m.m22 = obj->fontm22;
1313     m.m13 = x;
1314     m.m23 = y;
1315     drawchar(obj, obj->font, character, charnr, &m);
1316 }
1317
1318 /* initialize the swf writer */
1319 void swfoutput_init(struct swfoutput* obj, char*_filename, int _sizex, int _sizey) 
1320 {
1321   GLYPH *glyph;
1322   RGBA rgb;
1323   SRECT r;
1324   memset(obj, 0, sizeof(struct swfoutput));
1325   filename = _filename;
1326   sizex = _sizex;
1327   sizey = _sizey;
1328
1329   logf("<verbose> initializing swf output for size %d*%d\n", sizex,sizey);
1330
1331   obj->font = 0;
1332   
1333   memset(&swf,0x00,sizeof(SWF));
1334
1335   swf.fileVersion    = flashversion;
1336   swf.frameRate      = 0x0040; // 1 frame per 4 seconds
1337   swf.movieSize.xmax = 20*sizex;
1338   swf.movieSize.ymax = 20*sizey;
1339   
1340   swf.firstTag = swf_InsertTag(NULL,ST_SETBACKGROUNDCOLOR);
1341   tag = swf.firstTag;
1342   rgb.r = 0xff;
1343   rgb.g = 0xff;
1344   rgb.b = 0xff;
1345   swf_SetRGB(tag,&rgb);
1346   if(flag_protected)  // good practice! /r
1347     tag = swf_InsertTag(tag, ST_PROTECT);
1348   depth = 1;
1349   startdepth = depth;
1350 }
1351
1352 void swfoutput_setprotected() //write PROTECT tag
1353 {
1354   flag_protected = 1;
1355 }
1356
1357 static void startshape(struct swfoutput*obj)
1358 {
1359   RGBA rgb;
1360   SRECT r;
1361
1362   if(textid>=0)
1363       endtext();
1364
1365   tag = swf_InsertTag(tag,ST_DEFINESHAPE);
1366
1367   swf_ShapeNew(&shape);
1368   linestyleid = swf_ShapeAddLineStyle(shape,obj->linewidth,&obj->strokergb);
1369   rgb.r = obj->fillrgb.r;
1370   rgb.g = obj->fillrgb.g;
1371   rgb.b = obj->fillrgb.b;
1372   fillstyleid = swf_ShapeAddSolidFillStyle(shape,&obj->fillrgb);
1373
1374   shapeid = ++currentswfid;
1375   swf_SetU16(tag,shapeid);  // ID
1376
1377   r.xmin = 0;
1378   r.ymin = 0;
1379   r.xmax = 20*sizex;
1380   r.ymax = 20*sizey;
1381   
1382   swf_SetRect(tag,&r);
1383
1384   swf_SetShapeStyles(tag,shape);
1385   swf_ShapeCountBits(shape,NULL,NULL);
1386   swf_SetShapeBits(tag,shape);
1387
1388   swf_ShapeSetAll(tag,shape,/*x*/0,/*y*/0,linestyleid,0,0);
1389   swflastx=swflasty=0;
1390   lastwasfill = 0;
1391 }
1392
1393 static void starttext(struct swfoutput*obj)
1394 {
1395   SRECT r;
1396   MATRIX m;
1397   if(shapeid>=0)
1398       endshape();
1399   tag = swf_InsertTag(tag,ST_DEFINETEXT);
1400   textid = ++currentswfid;
1401   swf_SetU16(tag, textid);
1402
1403   r.xmin = 0;
1404   r.ymin = 0;
1405   r.xmax = 20*sizex;
1406   r.ymax = 20*sizey;
1407   
1408   swf_SetRect(tag,&r);
1409
1410   m.sx = 65536;
1411   m.sy = 65536;
1412   m.r0 = 0;
1413   m.r1 = 0;
1414   m.tx = 0;
1415   m.ty = 0;
1416  
1417   swf_SetMatrix(tag,&m);
1418   swflastx=swflasty=0;
1419 }
1420
1421 static void endshape()
1422 {
1423     if(shapeid<0) 
1424         return;
1425     swf_ShapeSetEnd(tag);
1426     tag = swf_InsertTag(tag,ST_PLACEOBJECT2);
1427     swf_ObjectPlace(tag,shapeid,/*depth*/depth++,NULL,NULL,NULL);
1428     shapeid = -1;
1429 }
1430
1431 static void endtext()
1432 {
1433     if(textid<0)
1434         return;
1435     putcharacters(tag);
1436     swf_SetU8(tag,0);
1437     tag = swf_InsertTag(tag,ST_PLACEOBJECT2);
1438     swf_ObjectPlace(tag,textid,/*depth*/depth++,NULL,NULL,NULL);
1439     textid = -1;
1440 }
1441
1442 static void endpage(struct swfoutput*obj)
1443 {
1444     if(shapeid>=0)
1445       endshape();
1446     if(textid>=0)
1447       endtext();
1448     while(clippos)
1449         swfoutput_endclip(obj);
1450
1451     if(insertstoptag) {
1452         ActionTAG*atag=0;
1453         atag = action_Stop(atag);
1454         atag = action_End(atag);
1455         tag = swf_InsertTag(tag,ST_DOACTION);
1456         swf_ActionSet(tag,atag);
1457     }
1458     tag = swf_InsertTag(tag,ST_SHOWFRAME);
1459 }
1460
1461 void swfoutput_newpage(struct swfoutput*obj)
1462 {
1463     endpage(obj);
1464
1465     for(depth--;depth>=startdepth;depth--) {
1466         tag = swf_InsertTag(tag,ST_REMOVEOBJECT2);
1467         swf_SetU16(tag,depth);
1468     }
1469
1470     depth = 1;
1471     startdepth = depth;
1472 }
1473
1474 /* "destroy" like in (oo-terminology) "destructor". Perform cleaning
1475    up, complete the swf, and write it out. */
1476 void swfoutput_destroy(struct swfoutput* obj) 
1477 {
1478     endpage(obj);
1479     fontlist_t *tmp,*iterator = fontlist;
1480     while(iterator) {
1481         delete iterator->font;
1482         iterator->font = 0;
1483         tmp = iterator;
1484         iterator = iterator->next;
1485         delete tmp;
1486     }
1487
1488     T1_CloseLib();
1489     if(!filename) 
1490         return;
1491     if(filename)
1492      fi = open(filename, O_BINARY|O_CREAT|O_TRUNC|O_WRONLY, 0777);
1493     else
1494      fi = 1; // stdout
1495     
1496     if(fi<=0) {
1497      logf("<fatal> Could not create \"%s\". ", FIXNULL(filename));
1498      exit(1);
1499     }
1500  
1501     tag = swf_InsertTag(tag,ST_END);
1502
1503     if(enablezlib) {
1504       if FAILED(swf_WriteSWC(fi,&swf)) 
1505        logf("<error> WriteSWC() failed.\n");
1506     } else {
1507       if FAILED(swf_WriteSWF(fi,&swf)) 
1508        logf("<error> WriteSWF() failed.\n");
1509     }
1510
1511     if(filename)
1512      close(fi);
1513     logf("<notice> SWF written\n");
1514 }
1515
1516 void swfoutput_setdrawmode(swfoutput* obj, int mode)
1517 {
1518     drawmode = mode;
1519     if(mode == DRAWMODE_FILL)
1520      fill = 1;
1521     else if(mode == DRAWMODE_EOFILL)
1522      fill = 1;
1523     else if(mode == DRAWMODE_STROKE)
1524      fill = 0;
1525     else if(mode == DRAWMODE_CLIP)
1526      fill = 1;
1527     else if(mode == DRAWMODE_EOCLIP)
1528      fill = 1;
1529 }
1530
1531 void swfoutput_setfillcolor(swfoutput* obj, u8 r, u8 g, u8 b, u8 a)
1532 {
1533     if(obj->fillrgb.r == r &&
1534        obj->fillrgb.g == g &&
1535        obj->fillrgb.b == b &&
1536        obj->fillrgb.a == a) return;
1537     if(shapeid>=0)
1538      endshape();
1539
1540     obj->fillrgb.r = r;
1541     obj->fillrgb.g = g;
1542     obj->fillrgb.b = b;
1543     obj->fillrgb.a = a;
1544 }
1545
1546 void swfoutput_setstrokecolor(swfoutput* obj, u8 r, u8 g, u8 b, u8 a)
1547 {
1548     if(obj->strokergb.r == r &&
1549        obj->strokergb.g == g &&
1550        obj->strokergb.b == b &&
1551        obj->strokergb.a == a) return;
1552
1553     if(shapeid>=0)
1554      endshape();
1555     obj->strokergb.r = r;
1556     obj->strokergb.g = g;
1557     obj->strokergb.b = b;
1558     obj->strokergb.a = a;
1559 }
1560
1561 void swfoutput_setlinewidth(struct swfoutput*obj, double linewidth)
1562 {
1563     if(obj->linewidth == (u16)(linewidth*20))
1564         return;
1565
1566     if(shapeid>=0)
1567      endshape();
1568     obj->linewidth = (u16)(linewidth*20);
1569 }
1570
1571
1572 void swfoutput_startclip(swfoutput*obj, T1_OUTLINE*outline, struct swfmatrix*m)
1573 {
1574     if(textid>=0)
1575      endtext();
1576     if(shapeid>=0)
1577      endshape();
1578
1579     if(clippos >= 127)
1580     {
1581         logf("<warning> Too many clip levels.");
1582         clippos --;
1583     } 
1584     
1585     startshape(obj);
1586     int olddrawmode = drawmode;
1587     swfoutput_setdrawmode(obj, DRAWMODE_CLIP);
1588     swfoutput_drawpath(obj, outline, m);
1589     swf_ShapeSetEnd(tag);
1590     swfoutput_setdrawmode(obj, olddrawmode);
1591
1592     tag = swf_InsertTag(tag,ST_PLACEOBJECT2);
1593     cliptags[clippos] = tag;
1594     clipshapes[clippos] = shapeid;
1595     clipdepths[clippos] = depth++;
1596     clippos++;
1597     shapeid = -1;
1598 }
1599
1600 void swfoutput_endclip(swfoutput*obj)
1601 {
1602     if(textid>=0)
1603      endtext();
1604     if(shapeid>=0)
1605      endshape();
1606
1607     if(!clippos) {
1608         logf("<error> Invalid end of clipping region");
1609         return;
1610     }
1611     clippos--;
1612     swf_ObjectPlaceClip(cliptags[clippos],clipshapes[clippos],clipdepths[clippos],NULL,NULL,NULL,depth++);
1613 }
1614
1615 static void drawlink(struct swfoutput*obj, ActionTAG*,ActionTAG*, swfcoord*points, char mouseover);
1616
1617 void swfoutput_linktourl(struct swfoutput*obj, char*url, swfcoord*points)
1618 {
1619     ActionTAG* actions;
1620     if(!strncmp("http://pdf2swf:", url, 15)) {
1621      char*tmp = strdup(url);
1622      int l = strlen(tmp);
1623      if(tmp[l-1] == '/')
1624         tmp[l-1] = 0;
1625      swfoutput_namedlink(obj, tmp+15, points);
1626      free(tmp);
1627      return;
1628     }
1629     
1630     if(shapeid>=0)
1631      endshape();
1632     if(textid>=0)
1633      endtext();
1634     
1635     if(opennewwindow)
1636       actions = action_GetUrl(0, url, "_parent");
1637     else
1638       actions = action_GetUrl(0, url, "_this");
1639     actions = action_End(actions);
1640     
1641     drawlink(obj, actions, 0, points,0);
1642 }
1643 void swfoutput_linktopage(struct swfoutput*obj, int page, swfcoord*points)
1644 {
1645     ActionTAG* actions;
1646
1647     if(shapeid>=0)
1648      endshape();
1649     if(textid>=0)
1650      endtext();
1651    
1652       actions = action_GotoFrame(0, page);
1653       actions = action_End(actions);
1654
1655     drawlink(obj, actions, 0, points,0);
1656 }
1657
1658 /* Named Links (a.k.a. Acrobatmenu) are used to implement various gadgets
1659    of the viewer objects, like subtitles, index elements etc.
1660 */
1661 void swfoutput_namedlink(struct swfoutput*obj, char*name, swfcoord*points)
1662 {
1663     ActionTAG *actions1,*actions2;
1664     char*tmp = strdup(name);
1665     char mouseover = 1;
1666
1667     if(shapeid>=0)
1668      endshape();
1669     if(textid>=0)
1670      endtext();
1671
1672     if(!strncmp(tmp, "call:", 5))
1673     {
1674         char*x = strchr(&tmp[5], ':');
1675         if(!x) {
1676             actions1 = action_PushInt(0, 0); //number of parameters (0)
1677             actions1 = action_PushString(actions1, &tmp[5]); //function name
1678             actions1 = action_CallFunction(actions1);
1679         } else {
1680             *x = 0;
1681             actions1 = action_PushString(0, x+1); //parameter
1682             actions1 = action_PushInt(actions1, 1); //number of parameters (1)
1683             actions1 = action_PushString(actions1, &tmp[5]); //function name
1684             actions1 = action_CallFunction(actions1);
1685         }
1686         actions2 = action_End(0);
1687         mouseover = 0;
1688     }
1689     else
1690     {
1691         actions1 = action_PushString(0, "/:subtitle");
1692         actions1 = action_PushString(actions1, name);
1693         actions1 = action_SetVariable(actions1);
1694         actions1 = action_End(actions1);
1695
1696         actions2 = action_PushString(0, "/:subtitle");
1697         actions2 = action_PushString(actions2, "");
1698         actions2 = action_SetVariable(actions2);
1699         actions2 = action_End(actions2);
1700     }
1701
1702     drawlink(obj, actions1, actions2, points,mouseover);
1703
1704     swf_ActionFree(actions1);
1705     swf_ActionFree(actions2);
1706     free(tmp);
1707 }
1708
1709 static void drawlink(struct swfoutput*obj, ActionTAG*actions1, ActionTAG*actions2, swfcoord*points, char mouseover)
1710 {
1711     RGBA rgb;
1712     SRECT r;
1713     int lsid=0;
1714     int fsid;
1715     struct plotxy p1,p2,p3,p4;
1716     int myshapeid;
1717     int myshapeid2;
1718     double xmin,ymin;
1719     double xmax=xmin=points[0].x,ymax=ymin=points[0].y;
1720     double posx = 0;
1721     double posy = 0;
1722     int t;
1723     int buttonid = ++currentswfid;
1724     for(t=1;t<4;t++)
1725     {
1726         if(points[t].x>xmax) xmax=points[t].x;
1727         if(points[t].y>ymax) ymax=points[t].y;
1728         if(points[t].x<xmin) xmin=points[t].x;
1729         if(points[t].y<ymin) ymin=points[t].y;
1730     }
1731    
1732     p1.x=points[0].x; p1.y=points[0].y; p2.x=points[1].x; p2.y=points[1].y; 
1733     p3.x=points[2].x; p3.y=points[2].y; p4.x=points[3].x; p4.y=points[3].y;
1734    
1735     /* the following code subtracts the upper left edge from all coordinates,
1736        and set's posx,posy so that ST_PLACEOBJECT is used with a matrix.
1737        Necessary for preprocessing with swfcombine. */
1738     posx = xmin; posy = ymin;
1739     p1.x-=posx;p2.x-=posx;p3.x-=posx;p4.x-=posx;
1740     p1.y-=posy;p2.y-=posy;p3.y-=posy;p4.y-=posy;
1741     xmin -= posx; ymin -= posy;
1742     xmax -= posx; ymax -= posy;
1743     
1744     /* shape */
1745     myshapeid = ++currentswfid;
1746     tag = swf_InsertTag(tag,ST_DEFINESHAPE3);
1747     swf_ShapeNew(&shape);
1748     rgb.r = rgb.b = rgb.a = rgb.g = 0; 
1749     fsid = swf_ShapeAddSolidFillStyle(shape,&rgb);
1750     swf_SetU16(tag, myshapeid);
1751     r.xmin = (int)(xmin*20);
1752     r.ymin = (int)(ymin*20);
1753     r.xmax = (int)(xmax*20);
1754     r.ymax = (int)(ymax*20);
1755     swf_SetRect(tag,&r);
1756     swf_SetShapeStyles(tag,shape);
1757     swf_ShapeCountBits(shape,NULL,NULL);
1758     swf_SetShapeBits(tag,shape);
1759     swf_ShapeSetAll(tag,shape,/*x*/0,/*y*/0,0,fsid,0);
1760     swflastx = swflasty = 0;
1761     moveto(tag, p1);
1762     lineto(tag, p2);
1763     lineto(tag, p3);
1764     lineto(tag, p4);
1765     lineto(tag, p1);
1766     swf_ShapeSetEnd(tag);
1767
1768     /* shape2 */
1769     myshapeid2 = ++currentswfid;
1770     tag = swf_InsertTag(tag,ST_DEFINESHAPE3);
1771     swf_ShapeNew(&shape);
1772     rgb.r = rgb.b = rgb.a = rgb.g = 255;
1773     rgb.a = 40;
1774     fsid = swf_ShapeAddSolidFillStyle(shape,&rgb);
1775     swf_SetU16(tag, myshapeid2);
1776     r.xmin = (int)(xmin*20);
1777     r.ymin = (int)(ymin*20);
1778     r.xmax = (int)(xmax*20);
1779     r.ymax = (int)(ymax*20);
1780     swf_SetRect(tag,&r);
1781     swf_SetShapeStyles(tag,shape);
1782     swf_ShapeCountBits(shape,NULL,NULL);
1783     swf_SetShapeBits(tag,shape);
1784     swf_ShapeSetAll(tag,shape,/*x*/0,/*y*/0,0,fsid,0);
1785     swflastx = swflasty = 0;
1786     moveto(tag, p1);
1787     lineto(tag, p2);
1788     lineto(tag, p3);
1789     lineto(tag, p4);
1790     lineto(tag, p1);
1791     swf_ShapeSetEnd(tag);
1792
1793     if(!mouseover)
1794     {
1795         tag = swf_InsertTag(tag,ST_DEFINEBUTTON);
1796         swf_SetU16(tag,buttonid); //id
1797         swf_ButtonSetFlags(tag, 0); //menu=no
1798         swf_ButtonSetRecord(tag,0x01,myshapeid,depth,0,0);
1799         swf_ButtonSetRecord(tag,0x02,myshapeid2,depth,0,0);
1800         swf_ButtonSetRecord(tag,0x04,myshapeid2,depth,0,0);
1801         swf_ButtonSetRecord(tag,0x08,myshapeid,depth,0,0);
1802         swf_SetU8(tag,0);
1803         swf_ActionSet(tag,actions1);
1804         swf_SetU8(tag,0);
1805     }
1806     else
1807     {
1808         tag = swf_InsertTag(tag,ST_DEFINEBUTTON2);
1809         swf_SetU16(tag,buttonid); //id
1810         swf_ButtonSetFlags(tag, 0); //menu=no
1811         swf_ButtonSetRecord(tag,0x01,myshapeid,depth,0,0);
1812         swf_ButtonSetRecord(tag,0x02,myshapeid2,depth,0,0);
1813         swf_ButtonSetRecord(tag,0x04,myshapeid2,depth,0,0);
1814         swf_ButtonSetRecord(tag,0x08,myshapeid,depth,0,0);
1815         swf_SetU8(tag,0); // end of button records
1816         swf_ButtonSetCondition(tag, BC_IDLE_OVERUP);
1817         swf_ActionSet(tag,actions1);
1818         if(actions2) {
1819             swf_ButtonSetCondition(tag, BC_OVERUP_IDLE);
1820             swf_ActionSet(tag,actions2);
1821             swf_SetU8(tag,0);
1822             swf_ButtonPostProcess(tag, 2);
1823         } else {
1824             swf_SetU8(tag,0);
1825             swf_ButtonPostProcess(tag, 1);
1826         }
1827     }
1828     
1829     tag = swf_InsertTag(tag,ST_PLACEOBJECT2);
1830
1831     if(posx!=0 || posy!=0) {
1832         MATRIX m;
1833         swf_GetMatrix(0,&m);
1834         m.tx = (int)(posx*20);
1835         m.ty = (int)(posy*20);
1836         swf_ObjectPlace(tag, buttonid, depth++,&m,0,0);
1837     }
1838     else {
1839         swf_ObjectPlace(tag, buttonid, depth++,0,0,0);
1840     }
1841 }
1842
1843 static void drawimage(struct swfoutput*obj, int bitid, int sizex,int sizey, 
1844         double x1,double y1,
1845         double x2,double y2,
1846         double x3,double y3,
1847         double x4,double y4)
1848 {
1849     RGBA rgb;
1850     SRECT r;
1851     int lsid=0;
1852     int fsid;
1853     struct plotxy p1,p2,p3,p4;
1854     int myshapeid;
1855     double xmax=x1,ymax=y1,xmin=x1,ymin=y1;
1856     if(x2>xmax) xmax=x2;
1857     if(y2>ymax) ymax=y2;
1858     if(x2<xmin) xmin=x2;
1859     if(y2<ymin) ymin=y2;
1860     if(x3>xmax) xmax=x3;
1861     if(y3>ymax) ymax=y3;
1862     if(x3<xmin) xmin=x3;
1863     if(y3<ymin) ymin=y3;
1864     if(x4>xmax) xmax=x4;
1865     if(y4>ymax) ymax=y4;
1866     if(x4<xmin) xmin=x4;
1867     if(y4<ymin) ymin=y4;
1868     p1.x=x1; p1.y=y1;
1869     p2.x=x2; p2.y=y2;
1870     p3.x=x3; p3.y=y3;
1871     p4.x=x4; p4.y=y4;
1872
1873     {p1.x = (int)(p1.x*20)/20.0;
1874      p1.y = (int)(p1.y*20)/20.0;
1875      p2.x = (int)(p2.x*20)/20.0;
1876      p2.y = (int)(p2.y*20)/20.0;
1877      p3.x = (int)(p3.x*20)/20.0;
1878      p3.y = (int)(p3.y*20)/20.0;
1879      p4.x = (int)(p4.x*20)/20.0;
1880      p4.y = (int)(p4.y*20)/20.0;}
1881     
1882     MATRIX m;
1883     m.sx = (int)(65536*20*(p4.x-p1.x)/sizex);
1884     m.r1 = -(int)(65536*20*(p4.y-p1.y)/sizex);
1885     m.r0 = (int)(65536*20*(p1.x-p2.x)/sizey);
1886     m.sy = -(int)(65536*20*(p1.y-p2.y)/sizey);
1887
1888     m.tx = (int)(p1.x*20);
1889     m.ty = (int)(p1.y*20);
1890   
1891     /* shape */
1892     myshapeid = ++currentswfid;
1893     tag = swf_InsertTag(tag,ST_DEFINESHAPE);
1894     swf_ShapeNew(&shape);
1895     //lsid = ShapeAddLineStyle(shape,obj->linewidth,&obj->strokergb);
1896     //fsid = ShapeAddSolidFillStyle(shape,&obj->fillrgb);
1897     fsid = swf_ShapeAddBitmapFillStyle(shape,&m,bitid,1);
1898     swf_SetU16(tag, myshapeid);
1899     r.xmin = (int)(xmin*20);
1900     r.ymin = (int)(ymin*20);
1901     r.xmax = (int)(xmax*20);
1902     r.ymax = (int)(ymax*20);
1903     swf_SetRect(tag,&r);
1904     swf_SetShapeStyles(tag,shape);
1905     swf_ShapeCountBits(shape,NULL,NULL);
1906     swf_SetShapeBits(tag,shape);
1907     swf_ShapeSetAll(tag,shape,/*x*/0,/*y*/0,lsid,fsid,0);
1908     swflastx = swflasty = 0;
1909     moveto(tag, p1);
1910     lineto(tag, p2);
1911     lineto(tag, p3);
1912     lineto(tag, p4);
1913     lineto(tag, p1);
1914     /*
1915     ShapeMoveTo  (tag, shape, (int)(x1*20),(int)(y1*20));
1916     ShapeSetLine (tag, shape, (int)(x1*20);
1917     ShapeSetLine (tag, shape, x*20,0);
1918     ShapeSetLine (tag, shape, 0,-y*20);
1919     ShapeSetLine (tag, shape, -x*20,0);*/
1920     swf_ShapeSetEnd(tag);
1921
1922     /* instance */
1923     tag = swf_InsertTag(tag,ST_PLACEOBJECT2);
1924     swf_ObjectPlace(tag,myshapeid,/*depth*/depth++,NULL,NULL,NULL);
1925 }
1926
1927 int swfoutput_drawimagejpeg_old(struct swfoutput*obj, char*filename, int sizex,int sizey, 
1928         double x1,double y1,
1929         double x2,double y2,
1930         double x3,double y3,
1931         double x4,double y4)
1932 {
1933     TAG*oldtag;
1934     if(shapeid>=0)
1935      endshape();
1936     if(textid>=0)
1937      endtext();
1938
1939     int bitid = ++currentswfid;
1940     oldtag = tag;
1941     tag = swf_InsertTag(tag,ST_DEFINEBITSJPEG2);
1942     swf_SetU16(tag, bitid);
1943     if(swf_SetJPEGBits(tag, filename, jpegquality)<0) {
1944         swf_DeleteTag(tag);
1945         tag = oldtag;
1946         return -1;
1947     }
1948
1949     drawimage(obj, bitid, sizex, sizey, x1,y1,x2,y2,x3,y3,x4,y4);
1950     return bitid;
1951 }
1952
1953 int swfoutput_drawimagejpeg(struct swfoutput*obj, RGBA*mem, int sizex,int sizey, 
1954         double x1,double y1,
1955         double x2,double y2,
1956         double x3,double y3,
1957         double x4,double y4)
1958 {
1959     TAG*oldtag;
1960     JPEGBITS*jpeg;
1961
1962     if(shapeid>=0)
1963      endshape();
1964     if(textid>=0)
1965      endtext();
1966
1967     int bitid = ++currentswfid;
1968     oldtag = tag;
1969     tag = swf_InsertTag(tag,ST_DEFINEBITSJPEG2);
1970     swf_SetU16(tag, bitid);
1971     swf_SetJPEGBits2(tag,sizex,sizey,mem,jpegquality);
1972     drawimage(obj, bitid, sizex, sizey, x1,y1,x2,y2,x3,y3,x4,y4);
1973     return bitid;
1974 }
1975
1976 int swfoutput_drawimagelossless(struct swfoutput*obj, RGBA*mem, int sizex,int sizey, 
1977         double x1,double y1,
1978         double x2,double y2,
1979         double x3,double y3,
1980         double x4,double y4)
1981 {
1982     TAG*oldtag;
1983     if(shapeid>=0)
1984      endshape();
1985     if(textid>=0)
1986      endtext();
1987
1988     int bitid = ++currentswfid;
1989     oldtag = tag;
1990     tag = swf_InsertTag(tag,ST_DEFINEBITSLOSSLESS);
1991     swf_SetU16(tag, bitid);
1992     if(swf_SetLosslessBits(tag,sizex,sizey,mem, BMF_32BIT)<0) {
1993         swf_DeleteTag(tag);
1994         tag = oldtag;
1995         return -1;
1996     }
1997     
1998     drawimage(obj, bitid, sizex, sizey, x1,y1,x2,y2,x3,y3,x4,y4);
1999     return bitid;
2000 }
2001
2002 int swfoutput_drawimagelosslessN(struct swfoutput*obj, U8*mem, RGBA*pal, int sizex,int sizey, 
2003         double x1,double y1,
2004         double x2,double y2,
2005         double x3,double y3,
2006         double x4,double y4, int n)
2007 {
2008     TAG*oldtag;
2009     U8*mem2 = 0;
2010     if(shapeid>=0)
2011      endshape();
2012     if(textid>=0)
2013      endtext();
2014
2015     if(sizex&3)
2016     { 
2017         /* SWF expects scanlines to be 4 byte aligned */
2018         int x,y;
2019         U8*ptr;
2020         mem2 = (U8*)malloc(BYTES_PER_SCANLINE(sizex)*sizey);
2021         ptr = mem2;
2022         for(y=0;y<sizey;y++)
2023         {
2024             for(x=0;x<sizex;x++)
2025                 *ptr++ = mem[y*sizex+x];
2026             ptr+= BYTES_PER_SCANLINE(sizex)-sizex;
2027         }
2028         mem = mem2;
2029     }
2030
2031     int bitid = ++currentswfid;
2032     oldtag = tag;
2033     tag = swf_InsertTag(tag,ST_DEFINEBITSLOSSLESS2);
2034     swf_SetU16(tag, bitid);
2035     if(swf_SetLosslessBitsIndexed(tag,sizex,sizey,mem, pal, n)<0) {
2036         swf_DeleteTag(tag);
2037         tag = oldtag;
2038         return -1;
2039     }
2040     if(mem2)
2041         free(mem2);
2042   
2043     drawimage(obj, bitid, sizex, sizey, x1,y1,x2,y2,x3,y3,x4,y4);
2044     return bitid;
2045 }
2046
2047 void swfoutput_drawimageagain(struct swfoutput*obj, int id, int sizex,int sizey, 
2048         double x1,double y1,
2049         double x2,double y2,
2050         double x3,double y3,
2051         double x4,double y4)
2052 {
2053     if(id<0) return;
2054     if(shapeid>=0)
2055      endshape();
2056     if(textid>=0)
2057      endtext();
2058
2059     drawimage(obj, id, sizex, sizey, x1,y1,x2,y2,x3,y3,x4,y4);
2060 }
2061