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