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