fixed conversion for fonts with broken unicode indices.
[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 && font->encoding != 255) {
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     if(swffont->glyph2ascii) {
1297         int t;
1298         int bad = 0;
1299         /* check whether the Unicode indices look o.k.
1300            If they don't, disable the unicode lookup by setting
1301            the encoding to 255 */
1302         for(t=0;t<swffont->numchars;t++) {
1303             int c = swffont->glyph2ascii[t];
1304             if(c && c < 32 && swffont->glyph[t].shape->bitlen > 16) {
1305                 // the character maps into the unicode control character range
1306                 // between 0001-001f. Yet it is not empty. Treat the one
1307                 // mapping as broken, and look how many of those we find.
1308                 bad ++;
1309             }
1310         }
1311         if(bad>5) {
1312             msg("<warning> Font %s has bad unicode mapping", swffont->name);
1313             swffont->encoding = 255;
1314         }
1315     }
1316
1317     swf_FontSetID(swffont, ++i->currentswfid);
1318     
1319     if(getScreenLogLevel() >= LOGLEVEL_DEBUG)  {
1320         // print font information
1321         msg("<debug> Font %s (%s)",swffont->name, filename);
1322         msg("<debug> |   ID: %d", swffont->id);
1323         msg("<debug> |   Version: %d", swffont->version);
1324         msg("<debug> |   Name: %s", fontid);
1325         msg("<debug> |   Numchars: %d", swffont->numchars);
1326         msg("<debug> |   Maxascii: %d", swffont->maxascii);
1327         msg("<debug> |   Style: %d", swffont->style);
1328         msg("<debug> |   Encoding: %d", swffont->encoding);
1329         for(int iii=0; iii<swffont->numchars;iii++) {
1330             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, 
1331                     swffont->layout->bounds[iii].xmin/20.0,
1332                     swffont->layout->bounds[iii].ymin/20.0,
1333                     swffont->layout->bounds[iii].xmax/20.0,
1334                     swffont->layout->bounds[iii].ymax/20.0
1335                     );
1336             int t;
1337             for(t=0;t<swffont->maxascii;t++) {
1338                 if(swffont->ascii2glyph[t] == iii)
1339                     msg("<debug> | - maps to %d",t);
1340             }
1341         }
1342     }
1343
1344     /* set the font name to the ID we use here */
1345     if(swffont->name) free(swffont->name);
1346     swffont->name = (U8*)strdup(fontid);
1347
1348     iterator = new fontlist_t;
1349     iterator->swffont = swffont;
1350     iterator->next = 0;
1351
1352     if(last) 
1353         last->next = iterator;
1354     else 
1355         i->fontlist = iterator;
1356
1357     obj->swffont = swffont; 
1358 }
1359
1360 int swfoutput_queryfont(struct swfoutput*obj, char*fontid)
1361 {
1362     swfoutput_internal*i = (swfoutput_internal*)obj->internal;
1363     fontlist_t *iterator = i->fontlist;
1364     while(iterator) {
1365         if(!strcmp((char*)iterator->swffont->name,fontid))
1366             return 1;
1367         iterator = iterator->next;
1368     }
1369     return 0;
1370 }
1371
1372 /* set's the matrix which is to be applied to characters drawn by
1373    swfoutput_drawchar() */
1374 void swfoutput_setfontmatrix(struct swfoutput*obj,double m11,double m12,
1375                                                   double m21,double m22)
1376 {
1377     swfoutput_internal*i = (swfoutput_internal*)obj->internal;
1378     if(obj->fontm11 == m11 &&
1379        obj->fontm12 == m12 &&
1380        obj->fontm21 == m21 &&
1381        obj->fontm22 == m22)
1382         return;
1383    if(i->textid>=0)
1384         endtext(obj);
1385     obj->fontm11 = m11;
1386     obj->fontm12 = m12;
1387     obj->fontm21 = m21;
1388     obj->fontm22 = m22;
1389     
1390     MATRIX m;
1391     m.sx = (U32)(((obj->fontm11)*65536)/FONT_INTERNAL_SIZE); m.r1 = (U32)(((obj->fontm12)*65536)/FONT_INTERNAL_SIZE);
1392     m.r0 = (U32)(((obj->fontm21)*65536)/FONT_INTERNAL_SIZE); m.sy = (U32)(((obj->fontm22)*65536)/FONT_INTERNAL_SIZE); 
1393     m.tx = 0;
1394     m.ty = 0;
1395     obj->fontmatrix = m;
1396 }
1397
1398 /* draws a character at x,y. */
1399 int swfoutput_drawchar(struct swfoutput* obj,double x,double y,char*character, int charnr, int u) 
1400 {
1401     swfoutput_internal*i = (swfoutput_internal*)obj->internal;
1402     swfmatrix m;
1403     m.m11 = obj->fontm11;
1404     m.m12 = obj->fontm12;
1405     m.m21 = obj->fontm21;
1406     m.m22 = obj->fontm22;
1407     m.m13 = x;
1408     m.m23 = y;
1409     return drawchar(obj, obj->swffont, character, charnr, u, &m);
1410 }
1411
1412 static void endpage(struct swfoutput*obj)
1413 {
1414     swfoutput_internal*i = (swfoutput_internal*)obj->internal;
1415     if(i->shapeid>=0)
1416       endshape(obj,0);
1417     if(i->textid>=0)
1418       endtext(obj);
1419     while(i->clippos)
1420         swfoutput_endclip(obj);
1421
1422     if(config_insertstoptag) {
1423         ActionTAG*atag=0;
1424         atag = action_Stop(atag);
1425         atag = action_End(atag);
1426         i->tag = swf_InsertTag(i->tag,ST_DOACTION);
1427         swf_ActionSet(i->tag,atag);
1428     }
1429     i->tag = swf_InsertTag(i->tag,ST_SHOWFRAME);
1430 }
1431
1432 void swfoutput_newpage(struct swfoutput*obj, int pageNum, int movex, int movey, int x1, int y1, int x2, int y2)
1433 {
1434     swfoutput_internal*i = (swfoutput_internal*)obj->internal;
1435     if(!i->firstpage)
1436         endpage(obj);
1437
1438     swf_GetMatrix(0, &i->page_matrix);
1439     i->page_matrix.tx = movex*20;
1440     i->page_matrix.ty = movey*20;
1441
1442     for(i->depth--;i->depth>=i->startdepth;i->depth--) {
1443         i->tag = swf_InsertTag(i->tag,ST_REMOVEOBJECT2);
1444         swf_SetU16(i->tag,i->depth);
1445     }
1446     i->depth = i->startdepth = 3; /* leave room for clip and background rectangle */
1447
1448     i->sizex = x2;
1449     i->sizey = y2;
1450     x1*=20;y1*=20;x2*=20;y2*=20;
1451
1452     if(i->lastpagesize.xmin != x1 ||
1453        i->lastpagesize.xmax != x2 ||
1454        i->lastpagesize.ymin != y1 ||
1455        i->lastpagesize.ymax != y2)
1456     {/* add white clipping rectangle */
1457         msg("<notice> processing page %d (%dx%d)", pageNum,i->sizex,i->sizey);
1458
1459         if(!i->firstpage) {
1460             msg("<notice> Page has a different size than previous ones");
1461             i->tag = swf_InsertTag(i->tag,ST_REMOVEOBJECT2);
1462             swf_SetU16(i->tag,1);
1463             i->tag = swf_InsertTag(i->tag,ST_REMOVEOBJECT2);
1464             swf_SetU16(i->tag,2);
1465         }
1466
1467         RGBA rgb;
1468         rgb.a = rgb.r = rgb.g = rgb.b = 0xff;
1469         SRECT r;
1470         SHAPE* s;
1471         int ls1=0,fs1=0;
1472         int shapeid = ++i->currentswfid;
1473         r.xmin = x1;
1474         r.ymin = y1;
1475         r.xmax = x2;
1476         r.ymax = y2;
1477         i->tag = swf_InsertTag(i->tag, ST_DEFINESHAPE);
1478         swf_ShapeNew(&s);
1479         fs1 = swf_ShapeAddSolidFillStyle(s, &rgb);
1480         swf_SetU16(i->tag,shapeid);
1481         swf_SetRect(i->tag,&r);
1482         swf_SetShapeHeader(i->tag,s);
1483         swf_ShapeSetAll(i->tag,s,x1,y1,ls1,fs1,0);
1484         swf_ShapeSetLine(i->tag,s,(x2-x1),0);
1485         swf_ShapeSetLine(i->tag,s,0,(y2-y1));
1486         swf_ShapeSetLine(i->tag,s,(x1-x2),0);
1487         swf_ShapeSetLine(i->tag,s,0,(y1-y2));
1488         swf_ShapeSetEnd(i->tag);
1489         swf_ShapeFree(s);
1490         i->tag = swf_InsertTag(i->tag, ST_PLACEOBJECT2);
1491         swf_ObjectPlace(i->tag,shapeid,/*depth*/1,0,0,0);
1492         i->tag = swf_InsertTag(i->tag, ST_PLACEOBJECT2);
1493         swf_ObjectPlaceClip(i->tag,shapeid,/*depth*/2,0,0,0,65535);
1494     } else {
1495         msg("<notice> processing page %d", pageNum);
1496     }
1497
1498     i->lastpagesize.xmin = x1;
1499     i->lastpagesize.xmax = x2;
1500     i->lastpagesize.ymin = y1;
1501     i->lastpagesize.ymax = y2;
1502     swf_ExpandRect2(&obj->swf.movieSize, &i->lastpagesize);
1503
1504     i->firstpage = 0;
1505 }
1506
1507 /* initialize the swf writer */
1508 void swfoutput_init(struct swfoutput* obj)
1509 {
1510     memset(obj, 0, sizeof(struct swfoutput));
1511     obj->internal = init_internal_struct();
1512
1513     swfoutput_internal*i = (swfoutput_internal*)obj->internal;
1514
1515     SRECT r;
1516     RGBA rgb;
1517
1518     msg("<verbose> initializing swf output for size %d*%d\n", i->sizex,i->sizey);
1519
1520     obj->swffont = 0;
1521     obj->drawmode = -1;
1522     
1523     memset(&obj->swf,0x00,sizeof(SWF));
1524     memset(&i->lastpagesize,0x00,sizeof(SRECT));
1525
1526     obj->swf.fileVersion    = config_flashversion;
1527     obj->swf.frameRate      = 0x0040; // 1 frame per 4 seconds
1528     obj->swf.movieSize.xmin = 0;
1529     obj->swf.movieSize.ymin = 0;
1530     obj->swf.movieSize.xmax = 0;
1531     obj->swf.movieSize.ymax = 0;
1532     
1533     obj->swf.firstTag = swf_InsertTag(NULL,ST_SETBACKGROUNDCOLOR);
1534     i->tag = obj->swf.firstTag;
1535     rgb.a = rgb.r = rgb.g = rgb.b = 0xff;
1536     swf_SetRGB(i->tag,&rgb);
1537
1538     i->startdepth = i->depth = 0;
1539     
1540     if(config_protect)
1541       i->tag = swf_InsertTag(i->tag, ST_PROTECT);
1542 }
1543
1544 static void startshape(struct swfoutput*obj)
1545 {
1546     swfoutput_internal*i = (swfoutput_internal*)obj->internal;
1547     RGBA rgb;
1548     SRECT r;
1549
1550     if(i->textid>=0)
1551         endtext(obj);
1552
1553     i->tag = swf_InsertTag(i->tag,ST_DEFINESHAPE);
1554
1555     swf_ShapeNew(&i->shape);
1556     i->linestyleid = swf_ShapeAddLineStyle(i->shape,i->linewidth,&obj->strokergb);
1557     rgb.r = obj->fillrgb.r;
1558     rgb.g = obj->fillrgb.g;
1559     rgb.b = obj->fillrgb.b;
1560     i->fillstyleid = swf_ShapeAddSolidFillStyle(i->shape,&obj->fillrgb);
1561
1562     i->shapeid = ++i->currentswfid;
1563     swf_SetU16(i->tag,i->shapeid);  // ID
1564
1565     i->bboxrectpos = i->tag->len;
1566     r.xmin = 0;
1567     r.ymin = 0;
1568     r.xmax = 20*i->sizex;
1569     r.ymax = 20*i->sizey;
1570     swf_SetRect(i->tag,&r);
1571    
1572     memset(&i->bboxrect, 0, sizeof(i->bboxrect));
1573
1574     swf_SetShapeStyles(i->tag,i->shape);
1575     swf_ShapeCountBits(i->shape,NULL,NULL);
1576     swf_SetShapeBits(i->tag,i->shape);
1577
1578     /* TODO: do we really need this? */
1579     swf_ShapeSetAll(i->tag,i->shape,/*x*/0,/*y*/0,i->linestyleid,0,0);
1580     i->swflastx=i->swflasty=0;
1581     i->lastwasfill = 0;
1582     i->shapeisempty = 1;
1583 }
1584
1585 static void starttext(struct swfoutput*obj)
1586 {
1587     swfoutput_internal*i = (swfoutput_internal*)obj->internal;
1588     if(i->shapeid>=0)
1589         endshape(obj,0);
1590       
1591     i->textid = ++i->currentswfid;
1592
1593     i->swflastx=i->swflasty=0;
1594 }
1595             
1596
1597 /* TODO: move to ../lib/rfxswf */
1598 void changeRect(struct swfoutput*obj, TAG*tag, int pos, SRECT*newrect)
1599 {
1600     swfoutput_internal*i = (swfoutput_internal*)obj->internal;
1601     /* determine length of old rect */
1602     tag->pos = pos;
1603     tag->readBit = 0;
1604     SRECT old;
1605     swf_GetRect(tag, &old);
1606     swf_ResetReadBits(tag);
1607     int pos_end = tag->pos;
1608
1609     int len = tag->len - pos_end;
1610     U8*data = (U8*)malloc(len);
1611     memcpy(data, &tag->data[pos_end], len);
1612     tag->writeBit = 0;
1613     tag->len = pos;
1614     swf_SetRect(tag, newrect);
1615     swf_SetBlock(tag, data, len);
1616     free(data);
1617     tag->pos = tag->readBit = 0;
1618 }
1619
1620 void cancelshape(swfoutput*obj)
1621 {
1622     swfoutput_internal*i = (swfoutput_internal*)obj->internal;
1623     /* delete old shape tag */
1624     TAG*todel = i->tag;
1625     i->tag = i->tag->prev;
1626     swf_DeleteTag(todel);
1627     if(i->shape) {swf_ShapeFree(i->shape);i->shape=0;}
1628     i->shapeid = -1;
1629     i->bboxrectpos = -1;
1630 }
1631
1632 void fixAreas(swfoutput*obj)
1633 {
1634     swfoutput_internal*i = (swfoutput_internal*)obj->internal;
1635     if(!i->shapeisempty && i->fill &&
1636        (i->bboxrect.xmin == i->bboxrect.xmax ||
1637         i->bboxrect.ymin == i->bboxrect.ymax) &&
1638         config_minlinewidth >= 0.001
1639        ) {
1640         msg("<debug> Shape has size 0: width=%.2f height=%.2f",
1641                 (i->bboxrect.xmax-i->bboxrect.xmin)/20.0,
1642                 (i->bboxrect.ymax-i->bboxrect.ymin)/20.0
1643                 );
1644     
1645         SRECT r = i->bboxrect;
1646         
1647         if(r.xmin == r.xmax && r.ymin == r.ymax) {
1648             /* this thing comes down to a single dot- nothing to fix here */
1649             return;
1650         }
1651
1652         cancelshape(obj);
1653
1654         RGBA save_col = obj->strokergb;
1655         int  save_width = i->linewidth;
1656
1657         obj->strokergb = obj->fillrgb;
1658         i->linewidth = (int)(config_minlinewidth*20);
1659         if(i->linewidth==0) i->linewidth = 1;
1660         
1661         startshape(obj);
1662
1663         moveto(obj, i->tag, r.xmin/20.0,r.ymin/20.0);
1664         lineto(obj, i->tag, r.xmax/20.0,r.ymax/20.0);
1665
1666         obj->strokergb = save_col;
1667         i->linewidth = save_width;
1668     }
1669     
1670 }
1671
1672 static void endshape_noput(swfoutput*obj)
1673 {
1674     swfoutput_internal*i = (swfoutput_internal*)obj->internal;
1675     if(i->shapeid<0) 
1676         return;
1677     //changeRect(obj, i->tag, i->bboxrectpos, &i->bboxrect);
1678     i->shapeid = -1;
1679     if(i->shape) {
1680         swf_ShapeFree(i->shape);
1681         i->shape=0;
1682     }
1683 }
1684
1685 static void endshape(swfoutput*obj, int clipdepth)
1686 {
1687     swfoutput_internal*i = (swfoutput_internal*)obj->internal;
1688     if(i->shapeid<0) 
1689         return;
1690
1691     if(!clipdepth)
1692         fixAreas(obj);
1693         
1694     if(i->shapeisempty ||
1695        (i->bboxrect.xmin == i->bboxrect.xmax && 
1696         i->bboxrect.ymin == i->bboxrect.ymax)) 
1697     {
1698         // delete the shape again, we didn't do anything
1699         cancelshape(obj);
1700         return;
1701     }
1702     
1703     swf_ShapeSetEnd(i->tag);
1704
1705     changeRect(obj, i->tag, i->bboxrectpos, &i->bboxrect);
1706
1707     i->tag = swf_InsertTag(i->tag,ST_PLACEOBJECT2);
1708
1709     if(clipdepth)
1710         swf_ObjectPlaceClip(i->tag,i->shapeid,i->depth++,&i->page_matrix,NULL,NULL,clipdepth);
1711     else
1712         swf_ObjectPlace(i->tag,i->shapeid,/*depth*/i->depth++,&i->page_matrix,NULL,NULL);
1713
1714     swf_ShapeFree(i->shape);
1715     i->shape = 0;
1716     i->shapeid = -1;
1717     i->bboxrectpos = -1;
1718 }
1719
1720 int swfoutput_save(struct swfoutput* obj, char*filename) 
1721 {
1722     swfoutput_internal*i = (swfoutput_internal*)obj->internal;
1723     endpage(obj);
1724     fontlist_t *tmp,*iterator = i->fontlist;
1725     while(iterator) {
1726         TAG*mtag = obj->swf.firstTag;
1727         if(iterator->swffont) {
1728             mtag = swf_InsertTag(mtag, ST_DEFINEFONT2);
1729             /*if(!storeallcharacters)
1730                 swf_FontReduce(iterator->swffont);*/
1731             swf_FontSetDefine2(mtag, iterator->swffont);
1732         }
1733
1734         iterator = iterator->next;
1735     }
1736     int fi;
1737
1738     if(filename)
1739      fi = open(filename, O_BINARY|O_CREAT|O_TRUNC|O_WRONLY, 0777);
1740     else
1741      fi = 1; // stdout
1742     
1743     if(fi<=0) {
1744      msg("<fatal> Could not create \"%s\". ", FIXNULL(filename));
1745      return 0;
1746     }
1747  
1748     i->tag = swf_InsertTag(i->tag,ST_END);
1749
1750     if(config_enablezlib || config_flashversion>=6) {
1751       if FAILED(swf_WriteSWC(fi,&obj->swf)) 
1752        msg("<error> WriteSWC() failed.\n");
1753     } else {
1754       if FAILED(swf_WriteSWF(fi,&obj->swf)) 
1755        msg("<error> WriteSWF() failed.\n");
1756     }
1757
1758     if(filename)
1759      close(fi);
1760     msg("<notice> SWF written\n");
1761     return 1;
1762 }
1763
1764 /* Perform cleaning up, complete the swf, and write it out. */
1765 void swfoutput_destroy(struct swfoutput* obj) 
1766 {
1767     swfoutput_internal*i = (swfoutput_internal*)obj->internal;
1768     if(!i) {
1769         /* not initialized yet- nothing to destroy */
1770         return;
1771     }
1772
1773     fontlist_t *tmp,*iterator = i->fontlist;
1774     while(iterator) {
1775         if(iterator->swffont) {
1776             swf_FontFree(iterator->swffont);iterator->swffont=0;
1777         }
1778         tmp = iterator;
1779         iterator = iterator->next;
1780         delete tmp;
1781     }
1782     swf_FreeTags(&obj->swf);
1783
1784     free(i);i=0;
1785     memset(obj, 0, sizeof(swfoutput));
1786 }
1787
1788 void swfoutput_setdrawmode(swfoutput* obj, int mode)
1789 {
1790     swfoutput_internal*i = (swfoutput_internal*)obj->internal;
1791     obj->drawmode = mode;
1792     if(mode == DRAWMODE_FILL)
1793      i->fill = 1;
1794     else if(mode == DRAWMODE_EOFILL)
1795      i->fill = 1;
1796     else if(mode == DRAWMODE_STROKE)
1797      i->fill = 0;
1798     else if(mode == DRAWMODE_CLIP)
1799      i->fill = 1;
1800     else if(mode == DRAWMODE_EOCLIP)
1801      i->fill = 1;
1802 }
1803
1804 void swfoutput_setfillcolor(swfoutput* obj, U8 r, U8 g, U8 b, U8 a)
1805 {
1806     swfoutput_internal*i = (swfoutput_internal*)obj->internal;
1807     if(obj->fillrgb.r == r &&
1808        obj->fillrgb.g == g &&
1809        obj->fillrgb.b == b &&
1810        obj->fillrgb.a == a) return;
1811     if(i->shapeid>=0)
1812      endshape(obj,0);
1813
1814     obj->fillrgb.r = r;
1815     obj->fillrgb.g = g;
1816     obj->fillrgb.b = b;
1817     obj->fillrgb.a = a;
1818 }
1819
1820 void swfoutput_setstrokecolor(swfoutput* obj, U8 r, U8 g, U8 b, U8 a)
1821 {
1822     swfoutput_internal*i = (swfoutput_internal*)obj->internal;
1823     if(obj->strokergb.r == r &&
1824        obj->strokergb.g == g &&
1825        obj->strokergb.b == b &&
1826        obj->strokergb.a == a) return;
1827
1828     if(i->shapeid>=0)
1829      endshape(obj,0);
1830     obj->strokergb.r = r;
1831     obj->strokergb.g = g;
1832     obj->strokergb.b = b;
1833     obj->strokergb.a = a;
1834 }
1835
1836 void swfoutput_setlinewidth(struct swfoutput*obj, double _linewidth)
1837 {
1838     swfoutput_internal*i = (swfoutput_internal*)obj->internal;
1839     if(i->linewidth == (U16)(_linewidth*20))
1840         return;
1841
1842     if(i->shapeid>=0)
1843         endshape(obj,0);
1844     i->linewidth = (U16)(_linewidth*20);
1845 }
1846
1847
1848 void swfoutput_startclip(swfoutput*obj, SWF_OUTLINE*outline, struct swfmatrix*m)
1849 {
1850     swfoutput_internal*i = (swfoutput_internal*)obj->internal;
1851     if(i->textid>=0)
1852      endtext(obj);
1853     if(i->shapeid>=0)
1854      endshape(obj,0);
1855
1856     if(i->clippos >= 127)
1857     {
1858         msg("<warning> Too many clip levels.");
1859         i->clippos --;
1860     } 
1861     
1862     startshape(obj);
1863     int olddrawmode = obj->drawmode;
1864     swfoutput_setdrawmode(obj, DRAWMODE_CLIP);
1865     swfoutput_drawpath(obj, outline, m);
1866     swf_ShapeSetEnd(i->tag);
1867     swfoutput_setdrawmode(obj, olddrawmode);
1868
1869     i->tag = swf_InsertTag(i->tag,ST_PLACEOBJECT2);
1870     i->cliptags[i->clippos] = i->tag;
1871     i->clipshapes[i->clippos] = i->shapeid;
1872     i->clipdepths[i->clippos] = i->depth++;
1873     i->clippos++;
1874     
1875     endshape_noput(obj);
1876 }
1877
1878 void swfoutput_endclip(swfoutput*obj)
1879 {
1880     swfoutput_internal*i = (swfoutput_internal*)obj->internal;
1881     if(i->textid>=0)
1882         endtext(obj);
1883     if(i->shapeid>=0)
1884         endshape(obj,0);
1885
1886     if(!i->clippos) {
1887         msg("<error> Invalid end of clipping region");
1888         return;
1889     }
1890     i->clippos--;
1891     swf_ObjectPlaceClip(i->cliptags[i->clippos],i->clipshapes[i->clippos],i->clipdepths[i->clippos],&i->page_matrix,NULL,NULL,i->depth++);
1892 }
1893
1894 static void drawlink(struct swfoutput*obj, ActionTAG*,ActionTAG*, swfcoord*points, char mouseover);
1895
1896 void swfoutput_linktourl(struct swfoutput*obj, char*url, swfcoord*points)
1897 {
1898     swfoutput_internal*i = (swfoutput_internal*)obj->internal;
1899     ActionTAG* actions;
1900     if(!strncmp("http://pdf2swf:", url, 15)) {
1901      char*tmp = strdup(url);
1902      int l = strlen(tmp);
1903      if(tmp[l-1] == '/')
1904         tmp[l-1] = 0;
1905      swfoutput_namedlink(obj, tmp+15, points);
1906      free(tmp);
1907      return;
1908     }
1909     
1910     if(i->shapeid>=0)
1911         endshape(obj,0);
1912     if(i->textid>=0)
1913         endtext(obj);
1914     
1915     if(config_opennewwindow)
1916       actions = action_GetUrl(0, url, "_parent");
1917     else
1918       actions = action_GetUrl(0, url, "_this");
1919     actions = action_End(actions);
1920     
1921     drawlink(obj, actions, 0, points,0);
1922 }
1923 void swfoutput_linktopage(struct swfoutput*obj, int page, swfcoord*points)
1924 {
1925     swfoutput_internal*i = (swfoutput_internal*)obj->internal;
1926     ActionTAG* actions;
1927
1928     if(i->shapeid>=0)
1929         endshape(obj,0);
1930     if(i->textid>=0)
1931         endtext(obj);
1932    
1933       actions = action_GotoFrame(0, page);
1934       actions = action_End(actions);
1935
1936     drawlink(obj, actions, 0, points,0);
1937 }
1938
1939 /* Named Links (a.k.a. Acrobatmenu) are used to implement various gadgets
1940    of the viewer objects, like subtitles, index elements etc.
1941 */
1942 void swfoutput_namedlink(struct swfoutput*obj, char*name, swfcoord*points)
1943 {
1944     swfoutput_internal*i = (swfoutput_internal*)obj->internal;
1945     ActionTAG *actions1,*actions2;
1946     char*tmp = strdup(name);
1947     char mouseover = 1;
1948
1949     if(i->shapeid>=0)
1950         endshape(obj,0);
1951     if(i->textid>=0)
1952         endtext(obj);
1953
1954     if(!strncmp(tmp, "call:", 5))
1955     {
1956         char*x = strchr(&tmp[5], ':');
1957         if(!x) {
1958             actions1 = action_PushInt(0, 0); //number of parameters (0)
1959             actions1 = action_PushString(actions1, &tmp[5]); //function name
1960             actions1 = action_CallFunction(actions1);
1961         } else {
1962             *x = 0;
1963             actions1 = action_PushString(0, x+1); //parameter
1964             actions1 = action_PushInt(actions1, 1); //number of parameters (1)
1965             actions1 = action_PushString(actions1, &tmp[5]); //function name
1966             actions1 = action_CallFunction(actions1);
1967         }
1968         actions2 = action_End(0);
1969         mouseover = 0;
1970     }
1971     else
1972     {
1973         actions1 = action_PushString(0, "/:subtitle");
1974         actions1 = action_PushString(actions1, name);
1975         actions1 = action_SetVariable(actions1);
1976         actions1 = action_End(actions1);
1977
1978         actions2 = action_PushString(0, "/:subtitle");
1979         actions2 = action_PushString(actions2, "");
1980         actions2 = action_SetVariable(actions2);
1981         actions2 = action_End(actions2);
1982     }
1983
1984     drawlink(obj, actions1, actions2, points,mouseover);
1985
1986     swf_ActionFree(actions1);
1987     swf_ActionFree(actions2);
1988     free(tmp);
1989 }
1990
1991 static void drawlink(struct swfoutput*obj, ActionTAG*actions1, ActionTAG*actions2, swfcoord*points, char mouseover)
1992 {
1993     swfoutput_internal*i = (swfoutput_internal*)obj->internal;
1994     RGBA rgb;
1995     SRECT r;
1996     int lsid=0;
1997     int fsid;
1998     struct plotxy p1,p2,p3,p4;
1999     int myshapeid;
2000     int myshapeid2;
2001     double xmin,ymin;
2002     double xmax=xmin=points[0].x,ymax=ymin=points[0].y;
2003     double posx = 0;
2004     double posy = 0;
2005     int t;
2006     int buttonid = ++i->currentswfid;
2007     for(t=1;t<4;t++)
2008     {
2009         if(points[t].x>xmax) xmax=points[t].x;
2010         if(points[t].y>ymax) ymax=points[t].y;
2011         if(points[t].x<xmin) xmin=points[t].x;
2012         if(points[t].y<ymin) ymin=points[t].y;
2013     }
2014    
2015     p1.x=points[0].x; p1.y=points[0].y; p2.x=points[1].x; p2.y=points[1].y; 
2016     p3.x=points[2].x; p3.y=points[2].y; p4.x=points[3].x; p4.y=points[3].y;
2017    
2018     /* the following code subtracts the upper left edge from all coordinates,
2019        and set's posx,posy so that ST_PLACEOBJECT is used with a matrix.
2020        Necessary for preprocessing with swfcombine. */
2021     posx = xmin; posy = ymin;
2022     p1.x-=posx;p2.x-=posx;p3.x-=posx;p4.x-=posx;
2023     p1.y-=posy;p2.y-=posy;p3.y-=posy;p4.y-=posy;
2024     xmin -= posx; ymin -= posy;
2025     xmax -= posx; ymax -= posy;
2026     
2027     /* shape */
2028     myshapeid = ++i->currentswfid;
2029     i->tag = swf_InsertTag(i->tag,ST_DEFINESHAPE3);
2030     swf_ShapeNew(&i->shape);
2031     rgb.r = rgb.b = rgb.a = rgb.g = 0; 
2032     fsid = swf_ShapeAddSolidFillStyle(i->shape,&rgb);
2033     swf_SetU16(i->tag, myshapeid);
2034     r.xmin = (int)(xmin*20);
2035     r.ymin = (int)(ymin*20);
2036     r.xmax = (int)(xmax*20);
2037     r.ymax = (int)(ymax*20);
2038     swf_SetRect(i->tag,&r);
2039     swf_SetShapeStyles(i->tag,i->shape);
2040     swf_ShapeCountBits(i->shape,NULL,NULL);
2041     swf_SetShapeBits(i->tag,i->shape);
2042     swf_ShapeSetAll(i->tag,i->shape,/*x*/0,/*y*/0,0,fsid,0);
2043     i->swflastx = i->swflasty = 0;
2044     moveto(obj, i->tag, p1);
2045     lineto(obj, i->tag, p2);
2046     lineto(obj, i->tag, p3);
2047     lineto(obj, i->tag, p4);
2048     lineto(obj, i->tag, p1);
2049     swf_ShapeSetEnd(i->tag);
2050
2051     /* shape2 */
2052     myshapeid2 = ++i->currentswfid;
2053     i->tag = swf_InsertTag(i->tag,ST_DEFINESHAPE3);
2054     swf_ShapeNew(&i->shape);
2055     rgb.r = rgb.b = rgb.a = rgb.g = 255;
2056     rgb.a = 40;
2057     fsid = swf_ShapeAddSolidFillStyle(i->shape,&rgb);
2058     swf_SetU16(i->tag, myshapeid2);
2059     r.xmin = (int)(xmin*20);
2060     r.ymin = (int)(ymin*20);
2061     r.xmax = (int)(xmax*20);
2062     r.ymax = (int)(ymax*20);
2063     swf_SetRect(i->tag,&r);
2064     swf_SetShapeStyles(i->tag,i->shape);
2065     swf_ShapeCountBits(i->shape,NULL,NULL);
2066     swf_SetShapeBits(i->tag,i->shape);
2067     swf_ShapeSetAll(i->tag,i->shape,/*x*/0,/*y*/0,0,fsid,0);
2068     i->swflastx = i->swflasty = 0;
2069     moveto(obj, i->tag, p1);
2070     lineto(obj, i->tag, p2);
2071     lineto(obj, i->tag, p3);
2072     lineto(obj, i->tag, p4);
2073     lineto(obj, i->tag, p1);
2074     swf_ShapeSetEnd(i->tag);
2075
2076     if(!mouseover)
2077     {
2078         i->tag = swf_InsertTag(i->tag,ST_DEFINEBUTTON);
2079         swf_SetU16(i->tag,buttonid); //id
2080         swf_ButtonSetFlags(i->tag, 0); //menu=no
2081         swf_ButtonSetRecord(i->tag,0x01,myshapeid,i->depth,0,0);
2082         swf_ButtonSetRecord(i->tag,0x02,myshapeid2,i->depth,0,0);
2083         swf_ButtonSetRecord(i->tag,0x04,myshapeid2,i->depth,0,0);
2084         swf_ButtonSetRecord(i->tag,0x08,myshapeid,i->depth,0,0);
2085         swf_SetU8(i->tag,0);
2086         swf_ActionSet(i->tag,actions1);
2087         swf_SetU8(i->tag,0);
2088     }
2089     else
2090     {
2091         i->tag = swf_InsertTag(i->tag,ST_DEFINEBUTTON2);
2092         swf_SetU16(i->tag,buttonid); //id
2093         swf_ButtonSetFlags(i->tag, 0); //menu=no
2094         swf_ButtonSetRecord(i->tag,0x01,myshapeid,i->depth,0,0);
2095         swf_ButtonSetRecord(i->tag,0x02,myshapeid2,i->depth,0,0);
2096         swf_ButtonSetRecord(i->tag,0x04,myshapeid2,i->depth,0,0);
2097         swf_ButtonSetRecord(i->tag,0x08,myshapeid,i->depth,0,0);
2098         swf_SetU8(i->tag,0); // end of button records
2099         swf_ButtonSetCondition(i->tag, BC_IDLE_OVERUP);
2100         swf_ActionSet(i->tag,actions1);
2101         if(actions2) {
2102             swf_ButtonSetCondition(i->tag, BC_OVERUP_IDLE);
2103             swf_ActionSet(i->tag,actions2);
2104             swf_SetU8(i->tag,0);
2105             swf_ButtonPostProcess(i->tag, 2);
2106         } else {
2107             swf_SetU8(i->tag,0);
2108             swf_ButtonPostProcess(i->tag, 1);
2109         }
2110     }
2111     
2112     i->tag = swf_InsertTag(i->tag,ST_PLACEOBJECT2);
2113
2114     if(posx!=0 || posy!=0) {
2115         SPOINT p;
2116         p.x = (int)(posx*20);
2117         p.y = (int)(posy*20);
2118         p = swf_TurnPoint(p, &i->page_matrix);
2119         MATRIX m;
2120         m = i->page_matrix;
2121         m.tx = p.x;
2122         m.ty = p.y;
2123         swf_ObjectPlace(i->tag, buttonid, i->depth++,&m,0,0);
2124     }
2125     else {
2126         swf_ObjectPlace(i->tag, buttonid, i->depth++,&i->page_matrix,0,0);
2127     }
2128 }
2129
2130 static void drawimage(struct swfoutput*obj, int bitid, int sizex,int sizey, 
2131         double x1,double y1,
2132         double x2,double y2,
2133         double x3,double y3,
2134         double x4,double y4)
2135 {
2136     swfoutput_internal*i = (swfoutput_internal*)obj->internal;
2137     RGBA rgb;
2138     SRECT r;
2139     int lsid=0;
2140     int fsid;
2141     struct plotxy p1,p2,p3,p4;
2142     int myshapeid;
2143     double xmax=x1,ymax=y1,xmin=x1,ymin=y1;
2144     if(x2>xmax) xmax=x2;
2145     if(y2>ymax) ymax=y2;
2146     if(x2<xmin) xmin=x2;
2147     if(y2<ymin) ymin=y2;
2148     if(x3>xmax) xmax=x3;
2149     if(y3>ymax) ymax=y3;
2150     if(x3<xmin) xmin=x3;
2151     if(y3<ymin) ymin=y3;
2152     if(x4>xmax) xmax=x4;
2153     if(y4>ymax) ymax=y4;
2154     if(x4<xmin) xmin=x4;
2155     if(y4<ymin) ymin=y4;
2156     p1.x=x1; p1.y=y1;
2157     p2.x=x2; p2.y=y2;
2158     p3.x=x3; p3.y=y3;
2159     p4.x=x4; p4.y=y4;
2160
2161     {p1.x = (int)(p1.x*20)/20.0;
2162      p1.y = (int)(p1.y*20)/20.0;
2163      p2.x = (int)(p2.x*20)/20.0;
2164      p2.y = (int)(p2.y*20)/20.0;
2165      p3.x = (int)(p3.x*20)/20.0;
2166      p3.y = (int)(p3.y*20)/20.0;
2167      p4.x = (int)(p4.x*20)/20.0;
2168      p4.y = (int)(p4.y*20)/20.0;}
2169     
2170     MATRIX m;
2171     m.sx = (int)(65536*20*(p4.x-p1.x)/sizex);
2172     m.r1 = -(int)(65536*20*(p4.y-p1.y)/sizex);
2173     m.r0 = (int)(65536*20*(p1.x-p2.x)/sizey);
2174     m.sy = -(int)(65536*20*(p1.y-p2.y)/sizey);
2175
2176     m.tx = (int)(p1.x*20);
2177     m.ty = (int)(p1.y*20);
2178   
2179     /* shape */
2180     myshapeid = ++i->currentswfid;
2181     i->tag = swf_InsertTag(i->tag,ST_DEFINESHAPE);
2182     SHAPE*shape;
2183     swf_ShapeNew(&shape);
2184     //lsid = ShapeAddLineStyle(shape,linewidth,&obj->strokergb);
2185     //fsid = ShapeAddSolidFillStyle(shape,&obj->fillrgb);
2186     fsid = swf_ShapeAddBitmapFillStyle(shape,&m,bitid,1);
2187     swf_SetU16(i->tag, myshapeid);
2188     r.xmin = (int)(xmin*20);
2189     r.ymin = (int)(ymin*20);
2190     r.xmax = (int)(xmax*20);
2191     r.ymax = (int)(ymax*20);
2192     swf_SetRect(i->tag,&r);
2193     swf_SetShapeStyles(i->tag,shape);
2194     swf_ShapeCountBits(shape,NULL,NULL);
2195     swf_SetShapeBits(i->tag,shape);
2196     swf_ShapeSetAll(i->tag,shape,/*x*/0,/*y*/0,lsid,fsid,0);
2197     i->swflastx = i->swflasty = 0;
2198     moveto(obj, i->tag, p1);
2199     lineto(obj, i->tag, p2);
2200     lineto(obj, i->tag, p3);
2201     lineto(obj, i->tag, p4);
2202     lineto(obj, i->tag, p1);
2203     /*
2204     ShapeMoveTo  (tag, shape, (int)(x1*20),(int)(y1*20));
2205     ShapeSetLine (tag, shape, (int)(x1*20);
2206     ShapeSetLine (tag, shape, x*20,0);
2207     ShapeSetLine (tag, shape, 0,-y*20);
2208     ShapeSetLine (tag, shape, -x*20,0);*/
2209     swf_ShapeSetEnd(i->tag);
2210     swf_ShapeFree(shape);
2211
2212     /* instance */
2213     i->tag = swf_InsertTag(i->tag,ST_PLACEOBJECT2);
2214
2215     swf_ObjectPlace(i->tag,myshapeid,/*depth*/i->depth++,&i->page_matrix,NULL,NULL);
2216 }
2217
2218 int swfoutput_drawimagejpeg_old(struct swfoutput*obj, char*filename, int sizex,int sizey, 
2219         double x1,double y1,
2220         double x2,double y2,
2221         double x3,double y3,
2222         double x4,double y4)
2223 {
2224     swfoutput_internal*i = (swfoutput_internal*)obj->internal;
2225     TAG*oldtag;
2226     if(i->shapeid>=0)
2227         endshape(obj,0);
2228     if(i->textid>=0)
2229         endtext(obj);
2230
2231     int bitid = ++i->currentswfid;
2232     oldtag = i->tag;
2233     i->tag = swf_InsertTag(i->tag,ST_DEFINEBITSJPEG2);
2234     swf_SetU16(i->tag, bitid);
2235     if(swf_SetJPEGBits(i->tag, filename, config_jpegquality)<0) {
2236         swf_DeleteTag(i->tag);
2237         i->tag = oldtag;
2238         return -1;
2239     }
2240
2241     drawimage(obj, bitid, sizex, sizey, x1,y1,x2,y2,x3,y3,x4,y4);
2242     return bitid;
2243 }
2244
2245 int swfoutput_drawimagejpeg(struct swfoutput*obj, RGBA*mem, int sizex,int sizey, 
2246         double x1,double y1,
2247         double x2,double y2,
2248         double x3,double y3,
2249         double x4,double y4)
2250 {
2251     swfoutput_internal*i = (swfoutput_internal*)obj->internal;
2252     TAG*oldtag;
2253     JPEGBITS*jpeg;
2254
2255     if(i->shapeid>=0)
2256         endshape(obj,0);
2257     if(i->textid>=0)
2258         endtext(obj);
2259
2260     int bitid = ++i->currentswfid;
2261     oldtag = i->tag;
2262     i->tag = swf_InsertTag(i->tag,ST_DEFINEBITSJPEG2);
2263     swf_SetU16(i->tag, bitid);
2264     swf_SetJPEGBits2(i->tag,sizex,sizey,mem,config_jpegquality);
2265     drawimage(obj, bitid, sizex, sizey, x1,y1,x2,y2,x3,y3,x4,y4);
2266     return bitid;
2267 }
2268
2269 int swfoutput_drawimagelossless(struct swfoutput*obj, RGBA*mem, int sizex,int sizey, 
2270         double x1,double y1,
2271         double x2,double y2,
2272         double x3,double y3,
2273         double x4,double y4)
2274 {
2275     swfoutput_internal*i = (swfoutput_internal*)obj->internal;
2276     TAG*oldtag;
2277     if(i->shapeid>=0)
2278         endshape(obj,0);
2279     if(i->textid>=0)
2280         endtext(obj);
2281
2282     int bitid = ++i->currentswfid;
2283     oldtag = i->tag;
2284     i->tag = swf_InsertTag(i->tag,ST_DEFINEBITSLOSSLESS);
2285     swf_SetU16(i->tag, bitid);
2286     if(swf_SetLosslessBits(i->tag,sizex,sizey,mem, BMF_32BIT)<0) {
2287         swf_DeleteTag(i->tag);
2288         i->tag = oldtag;
2289         return -1;
2290     }
2291     
2292     drawimage(obj, bitid, sizex, sizey, x1,y1,x2,y2,x3,y3,x4,y4);
2293     return bitid;
2294 }
2295
2296 int swfoutput_drawimagelosslessN(struct swfoutput*obj, U8*mem, RGBA*pal, int sizex,int sizey, 
2297         double x1,double y1,
2298         double x2,double y2,
2299         double x3,double y3,
2300         double x4,double y4, int n)
2301 {
2302     swfoutput_internal*i = (swfoutput_internal*)obj->internal;
2303     TAG*oldtag;
2304     U8*mem2 = 0;
2305     if(i->shapeid>=0)
2306         endshape(obj,0);
2307     if(i->textid>=0)
2308         endtext(obj);
2309
2310     if(sizex&3)
2311     { 
2312         /* SWF expects scanlines to be 4 byte aligned */
2313         int x,y;
2314         U8*ptr;
2315         mem2 = (U8*)malloc(BYTES_PER_SCANLINE(sizex)*sizey);
2316         ptr = mem2;
2317         for(y=0;y<sizey;y++)
2318         {
2319             for(x=0;x<sizex;x++)
2320                 *ptr++ = mem[y*sizex+x];
2321             ptr+= BYTES_PER_SCANLINE(sizex)-sizex;
2322         }
2323         mem = mem2;
2324     }
2325
2326     int bitid = ++i->currentswfid;
2327     oldtag = i->tag;
2328     i->tag = swf_InsertTag(i->tag,ST_DEFINEBITSLOSSLESS2);
2329     swf_SetU16(i->tag, bitid);
2330     if(swf_SetLosslessBitsIndexed(i->tag,sizex,sizey,mem, pal, n)<0) {
2331         swf_DeleteTag(i->tag);
2332         i->tag = oldtag;
2333         return -1;
2334     }
2335     if(mem2)
2336         free(mem2);
2337   
2338     drawimage(obj, bitid, sizex, sizey, x1,y1,x2,y2,x3,y3,x4,y4);
2339     return bitid;
2340 }
2341
2342 void swfoutput_drawimageagain(struct swfoutput*obj, int id, int sizex,int sizey, 
2343         double x1,double y1,
2344         double x2,double y2,
2345         double x3,double y3,
2346         double x4,double y4)
2347 {
2348     swfoutput_internal*i = (swfoutput_internal*)obj->internal;
2349     if(id<0) return;
2350     if(i->shapeid>=0)
2351         endshape(obj,0);
2352     if(i->textid>=0)
2353         endtext(obj);
2354
2355     drawimage(obj, id, sizex, sizey, x1,y1,x2,y2,x3,y3,x4,y4);
2356 }
2357
2358 void swfoutput_setparameter(char*name, char*value)
2359 {
2360     if(!strcmp(name, "drawonlyshapes")) {
2361         config_drawonlyshapes = atoi(value);
2362     } else if(!strcmp(name, "ignoredraworder")) {
2363         config_ignoredraworder = atoi(value);
2364     } else if(!strcmp(name, "filloverlap")) {
2365         config_filloverlap = atoi(value);
2366     } else if(!strcmp(name, "linksopennewwindow")) {
2367         config_opennewwindow = atoi(value);
2368     } else if(!strcmp(name, "opennewwindow")) {
2369         config_opennewwindow = atoi(value);
2370     } else if(!strcmp(name, "storeallcharacters")) {
2371         config_storeallcharacters = atoi(value);
2372     } else if(!strcmp(name, "enablezlib")) {
2373         config_enablezlib = atoi(value);
2374     } else if(!strcmp(name, "insertstop")) {
2375         config_insertstoptag = atoi(value);
2376     } else if(!strcmp(name, "protected")) {
2377         config_protect = atoi(value);
2378     } else if(!strcmp(name, "flashversion")) {
2379         config_flashversion = atoi(value);
2380     } else if(!strcmp(name, "minlinewidth")) {
2381         config_minlinewidth = atof(value);
2382     } else if(!strcmp(name, "jpegquality")) {
2383         int val = atoi(value);
2384         if(val<0) val=0;
2385         if(val>100) val=100;
2386         config_jpegquality = val;
2387     } else if(!strcmp(name, "splinequality")) {
2388         int v = atoi(value);
2389         v = 500-(v*5); // 100% = 0.25 pixel, 0% = 25 pixel
2390         if(v<1) v = 1;
2391         config_splinemaxerror = v;
2392     } else if(!strcmp(name, "fontquality")) {
2393         int v = atoi(value);
2394         v = 500-(v*5); // 100% = 0.25 pixel, 0% = 25 pixel
2395         if(v<1) v = 1;
2396         config_fontsplinemaxerror = v;
2397     } else {
2398         fprintf(stderr, "unknown parameter: %s (=%s)\n", name, value);
2399     }
2400 }
2401