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