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