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