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