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