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