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