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