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