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