3 Part of the swftools package.
5 Copyright (c) 2001,2002,2003,2004,2005 Matthias Kramm <kramm@quiss.org>
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.
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.
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 */
24 #include "../../config.h"
40 #include "../rfxswf.h"
41 #include "../gfxdevice.h"
42 #include "../gfxtools.h"
44 #include "../gfxpoly.h"
47 #define CHARDATAMAX 8192
51 typedef struct _chardata {
53 int fontid; /* TODO: use a SWFFONT instead */
60 typedef struct _fontlist
63 struct _fontlist*next;
66 typedef long int twip;
68 typedef struct _swfmatrix {
69 double m11,m12,m21,m22,m31,m32;
72 typedef struct _swfoutput_internal
74 gfxdevice_t*dev; // the gfxdevice object where this internal struct resides
76 double config_dumpfonts;
77 double config_ppmsubpixels;
78 double config_jpegsubpixels;
81 int config_simpleviewer;
82 int config_opennewwindow;
83 int config_ignoredraworder;
84 int config_drawonlyshapes;
85 int config_frameresets;
86 int config_linknameurl;
87 int config_jpegquality;
88 int config_storeallcharacters;
89 int config_enablezlib;
90 int config_insertstoptag;
92 int config_flashversion;
93 int config_reordertags;
94 int config_showclipshapes;
95 int config_splinemaxerror;
96 int config_fontsplinemaxerror;
97 int config_filloverlap;
100 int config_disable_polygon_conversion;
101 int config_normalize_polygon_positions;
102 char config_disablelinks;
103 RGBA config_linkcolor;
104 float config_minlinewidth;
105 double config_caplinewidth;
106 char* config_linktarget;
107 char*config_internallinkfunction;
108 char*config_externallinkfunction;
110 double config_framerate;
114 fontlist_t* fontlist;
153 int pic_height[1024];
160 char fillstylechanged;
162 int jpeg; //next image type
169 chardata_t chardata[CHARDATAMAX];
176 int current_font_size;
178 double lastfontm11,lastfontm12,lastfontm21,lastfontm22;
189 } swfoutput_internal;
191 static void swf_fillbitmap(gfxdevice_t*driver, gfxline_t*line, gfximage_t*img, gfxmatrix_t*move, gfxcxform_t*cxform);
192 static int swf_setparameter(gfxdevice_t*driver, const char*key, const char*value);
193 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);
194 static void swf_startclip(gfxdevice_t*dev, gfxline_t*line);
195 static void swf_endclip(gfxdevice_t*dev);
196 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);
197 static void swf_fill(gfxdevice_t*dev, gfxline_t*line, gfxcolor_t*color);
198 static void swf_fillbitmap(gfxdevice_t*dev, gfxline_t*line, gfximage_t*img, gfxmatrix_t*matrix, gfxcxform_t*cxform);
199 static void swf_fillgradient(gfxdevice_t*dev, gfxline_t*line, gfxgradient_t*gradient, gfxgradienttype_t type, gfxmatrix_t*matrix);
200 static void swf_drawchar(gfxdevice_t*dev, gfxfont_t*font, int glyph, gfxcolor_t*color, gfxmatrix_t*matrix);
201 static void swf_addfont(gfxdevice_t*dev, gfxfont_t*font);
202 static void swf_drawlink(gfxdevice_t*dev, gfxline_t*line, const char*action);
203 static void swf_startframe(gfxdevice_t*dev, int width, int height);
204 static void swf_endframe(gfxdevice_t*dev);
205 static gfxresult_t* swf_finish(gfxdevice_t*driver);
207 static swfoutput_internal* init_internal_struct()
209 swfoutput_internal*i = (swfoutput_internal*)malloc(sizeof(swfoutput_internal));
210 memset(i, 0, sizeof(swfoutput_internal));
234 i->fillstylechanged = 0;
241 i->config_disablelinks=0;
242 i->config_dumpfonts=0;
243 i->config_ppmsubpixels=0;
244 i->config_jpegsubpixels=0;
245 i->config_opennewwindow=1;
246 i->config_ignoredraworder=0;
247 i->config_drawonlyshapes=0;
248 i->config_jpegquality=85;
249 i->config_storeallcharacters=0;
251 i->config_enablezlib=0;
252 i->config_insertstoptag=0;
253 i->config_flashversion=6;
254 i->config_framerate=0.25;
255 i->config_splinemaxerror=1;
256 i->config_fontsplinemaxerror=1;
257 i->config_filloverlap=0;
259 i->config_bboxvars=0;
260 i->config_showclipshapes=0;
261 i->config_minlinewidth=0.05;
262 i->config_caplinewidth=1;
263 i->config_linktarget=0;
264 i->config_internallinkfunction=0;
265 i->config_externallinkfunction=0;
266 i->config_reordertags=1;
267 i->config_linknameurl=0;
269 i->config_linkcolor.r = i->config_linkcolor.g = i->config_linkcolor.b = 255;
270 i->config_linkcolor.a = 0x40;
275 static int id_error = 0;
277 static U16 getNewID(gfxdevice_t* dev)
279 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
280 if(i->currentswfid == 65535) {
282 msg("<error> ID Table overflow");
283 msg("<error> This file is too complex to render- SWF only supports 65536 shapes at once");
289 return ++i->currentswfid;
291 static U16 getNewDepth(gfxdevice_t* dev)
293 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
294 if(i->depth == 65520) {
296 msg("<error> Depth Table overflow");
297 msg("<error> This file is too complex to render- SWF only supports 65536 shapes at once");
306 static void startshape(gfxdevice_t* dev);
307 static void starttext(gfxdevice_t* dev);
308 static void endshape(gfxdevice_t* dev);
309 static void endtext(gfxdevice_t* dev);
311 typedef struct _plotxy
316 // write a move-to command into the swf
317 static int movetoxy(gfxdevice_t*dev, TAG*tag, plotxy_t p0)
319 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
320 int rx = (int)(p0.x*20);
321 int ry = (int)(p0.y*20);
322 if(rx!=i->swflastx || ry!=i->swflasty || i->fillstylechanged) {
323 swf_ShapeSetMove (tag, i->shape, rx,ry);
324 i->fillstylechanged = 0;
331 static int moveto(gfxdevice_t*dev, TAG*tag, double x, double y)
333 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
337 return movetoxy(dev, tag, p);
339 static void addPointToBBox(gfxdevice_t*dev, int px, int py)
341 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
347 swf_ExpandRect(&i->bboxrect, p);
349 swf_ExpandRect3(&i->bboxrect, p, i->linewidth*3/2);
353 /*static void plot(gfxdevice_t*dev, int x, int y, TAG*tag)
355 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
356 int width = i->linewidth/4;
360 //swf_ShapeSetLine(tag, i->shape,-width,-width);
361 //swf_ShapeSetLine(tag, i->shape,width*2,0);
362 //swf_ShapeSetLine(tag, i->shape,0,width*2);
363 //swf_ShapeSetLine(tag, i->shape,-width*2,0);
364 //swf_ShapeSetLine(tag, i->shape,0,-width*2);
365 //swf_ShapeSetLine(tag, i->shape,width,width);
368 swf_ShapeSetLine(tag, i->shape,-width,0);
369 swf_ShapeSetLine(tag, i->shape,width,-width);
370 swf_ShapeSetLine(tag, i->shape,width,width);
371 swf_ShapeSetLine(tag, i->shape,-width,width);
372 swf_ShapeSetLine(tag, i->shape,-width,-width);
373 swf_ShapeSetLine(tag, i->shape,width,0);
375 addPointToBBox(dev, x-width ,y-width);
376 addPointToBBox(dev, x+width ,y+width);
379 // write a line-to command into the swf
380 static void linetoxy(gfxdevice_t*dev, TAG*tag, plotxy_t p0)
382 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
383 int px = (int)(p0.x*20);
384 int py = (int)(p0.y*20);
385 int rx = (px-i->swflastx);
386 int ry = (py-i->swflasty);
388 swf_ShapeSetLine (tag, i->shape, rx,ry);
389 addPointToBBox(dev, i->swflastx,i->swflasty);
390 addPointToBBox(dev, px,py);
391 } /* this is a nice idea, but doesn't work with current flash
392 players (the pixel will be invisible if they're not
393 precisely on a pixel boundary)
394 Besides, we should only do this if this lineto itself
395 is again followed by a "move".
396 else if(!i->fill && i->config_dots) {
397 // treat lines of length 0 as plots, making them
398 // at least 1 twip wide so Flash will display them
399 //plot(dev, i->swflastx, i->swflasty, tag);
400 swf_ShapeSetLine (tag, i->shape, rx+1,ry);
407 static void lineto(gfxdevice_t*dev, TAG*tag, double x, double y)
412 linetoxy(dev,tag, p);
415 // write a spline-to command into the swf
416 static void splineto(gfxdevice_t*dev, TAG*tag, plotxy_t control,plotxy_t end)
418 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
419 int lastlastx = i->swflastx;
420 int lastlasty = i->swflasty;
422 int cx = ((int)(control.x*20)-i->swflastx);
423 int cy = ((int)(control.y*20)-i->swflasty);
426 int ex = ((int)(end.x*20)-i->swflastx);
427 int ey = ((int)(end.y*20)-i->swflasty);
431 if((cx || cy) && (ex || ey)) {
432 swf_ShapeSetCurve(tag, i->shape, cx,cy,ex,ey);
433 addPointToBBox(dev, lastlastx ,lastlasty );
434 addPointToBBox(dev, lastlastx+cx,lastlasty+cy);
435 addPointToBBox(dev, lastlastx+cx+ex,lastlasty+cy+ey);
436 } else if(cx || cy || ex || ey) {
437 swf_ShapeSetLine(tag, i->shape, cx+ex,cy+ey);
438 addPointToBBox(dev, lastlastx ,lastlasty );
439 addPointToBBox(dev, lastlastx+cx,lastlasty+cy);
440 addPointToBBox(dev, lastlastx+cx+ex,lastlasty+cy+ey);
446 /* write a line, given two points and the transformation
448 /*static void line(gfxdevice_t*dev, TAG*tag, plotxy_t p0, plotxy_t p1)
450 moveto(dev, tag, p0);
451 lineto(dev, tag, p1);
454 void resetdrawer(gfxdevice_t*dev)
456 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
461 static void stopFill(gfxdevice_t*dev)
463 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
464 if(i->lastwasfill!=0)
466 swf_ShapeSetStyle(i->tag,i->shape,i->linestyleid,0x8000,0);
467 i->fillstylechanged = 1;
471 static void startFill(gfxdevice_t*dev)
473 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
474 if(i->lastwasfill!=1)
476 swf_ShapeSetStyle(i->tag,i->shape,0x8000,i->fillstyleid,0);
477 i->fillstylechanged = 1;
482 static inline int colorcompare(gfxdevice_t*dev, RGBA*a,RGBA*b)
494 static SRECT getcharacterbbox(gfxdevice_t*dev, SWFFONT*font, MATRIX* m)
496 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
499 memset(&r, 0, sizeof(r));
502 if(debug) printf("\n");
503 for(t=0;t<i->chardatapos;t++)
505 if(i->chardata[t].fontid != font->id) {
506 msg("<error> Internal error: fontid %d != fontid %d", i->chardata[t].fontid, font->id);
509 SRECT b = font->layout->bounds[i->chardata[t].charid];
510 b.xmin *= i->chardata[t].size;
511 b.ymin *= i->chardata[t].size;
512 b.xmax *= i->chardata[t].size;
513 b.ymax *= i->chardata[t].size;
515 /* divide by 1024, rounding xmax/ymax up */
516 b.xmax += 1023; b.ymax += 1023; b.xmin /= 1024; b.ymin /= 1024; b.xmax /= 1024; b.ymax /= 1024;
518 b.xmin += i->chardata[t].x;
519 b.ymin += i->chardata[t].y;
520 b.xmax += i->chardata[t].x;
521 b.ymax += i->chardata[t].y;
523 /* until we solve the INTERNAL_SCALING problem (see below)
524 make sure the bounding box is big enough */
530 b = swf_TurnRect(b, m);
532 if(debug) printf("(%f,%f,%f,%f) -> (%f,%f,%f,%f) [font %d/%d, char %d]\n",
533 font->layout->bounds[i->chardata[t].charid].xmin/20.0,
534 font->layout->bounds[i->chardata[t].charid].ymin/20.0,
535 font->layout->bounds[i->chardata[t].charid].xmax/20.0,
536 font->layout->bounds[i->chardata[t].charid].ymax/20.0,
541 i->chardata[t].fontid,
543 i->chardata[t].charid
545 swf_ExpandRect2(&r, &b);
547 if(debug) printf("-----> (%f,%f,%f,%f)\n",
555 static void putcharacters(gfxdevice_t*dev, TAG*tag)
557 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
561 color.r = i->chardata[0].color.r^255;
570 int charadvance[128];
573 int glyphbits=1; //TODO: can this be zero?
576 if(tag->id != ST_DEFINETEXT &&
577 tag->id != ST_DEFINETEXT2) {
578 msg("<error> internal error: putcharacters needs an text tag, not %d\n",tag->id);
581 if(!i->chardatapos) {
582 msg("<warning> putcharacters called with zero characters");
585 for(pass = 0; pass < 2; pass++)
595 advancebits++; // add sign bit
596 swf_SetU8(tag, glyphbits);
597 swf_SetU8(tag, advancebits);
600 for(t=0;t<=i->chardatapos;t++)
602 if(lastfontid != i->chardata[t].fontid ||
603 lastx!=i->chardata[t].x ||
604 lasty!=i->chardata[t].y ||
605 !colorcompare(dev,&color, &i->chardata[t].color) ||
607 lastsize != i->chardata[t].size ||
610 if(charstorepos && pass==0)
613 for(s=0;s<charstorepos;s++)
615 while(charids[s]>=(1<<glyphbits))
617 while(charadvance[s]>=(1<<advancebits))
621 if(charstorepos && pass==1)
623 tag->writeBit = 0; // Q&D
624 swf_SetBits(tag, 0, 1); // GLYPH Record
625 swf_SetBits(tag, charstorepos, 7); // number of glyphs
627 for(s=0;s<charstorepos;s++)
629 swf_SetBits(tag, charids[s], glyphbits);
630 swf_SetBits(tag, charadvance[s], advancebits);
635 if(pass == 1 && t<i->chardatapos)
641 if(lastx != i->chardata[t].x ||
642 lasty != i->chardata[t].y)
644 newx = i->chardata[t].x;
645 newy = i->chardata[t].y;
651 if(!colorcompare(dev,&color, &i->chardata[t].color))
653 color = i->chardata[t].color;
656 font.id = i->chardata[t].fontid;
657 if(lastfontid != i->chardata[t].fontid || lastsize != i->chardata[t].size)
660 tag->writeBit = 0; // Q&D
661 swf_TextSetInfoRecord(tag, newfont, i->chardata[t].size, newcolor, newx,newy);
664 lastfontid = i->chardata[t].fontid;
665 lastx = i->chardata[t].x;
666 lasty = i->chardata[t].y;
667 lastsize = i->chardata[t].size;
670 if(t==i->chardatapos)
674 int nextt = t==i->chardatapos-1?t:t+1;
675 int rel = i->chardata[nextt].x-i->chardata[t].x;
676 if(rel>=0 && (rel<(1<<(advancebits-1)) || pass==0)) {
678 lastx=i->chardata[nextt].x;
682 lastx=i->chardata[t].x;
684 charids[charstorepos] = i->chardata[t].charid;
685 charadvance[charstorepos] = advance;
692 static void putcharacter(gfxdevice_t*dev, int fontid, int charid, int x,int y, int size, RGBA color)
694 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
695 if(i->chardatapos == CHARDATAMAX)
697 msg("<warning> Character buffer too small. SWF will be slightly bigger");
701 i->chardata[i->chardatapos].fontid = fontid;
702 i->chardata[i->chardatapos].charid = charid;
703 i->chardata[i->chardatapos].x = x;
704 i->chardata[i->chardatapos].y = y;
705 i->chardata[i->chardatapos].color = color;
706 i->chardata[i->chardatapos].size = size;
710 /* Notice: we can only put chars in the range -1639,1638 (-32768/20,32768/20).
711 So if we set this value to high, the char coordinates will overflow.
712 If we set it to low, however, the char positions will be inaccurate */
713 #define GLYPH_SCALE 1
715 static void endtext(gfxdevice_t*dev)
717 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
721 i->tag = swf_InsertTag(i->tag,ST_DEFINETEXT2);
722 swf_SetU16(i->tag, i->textid);
725 r = getcharacterbbox(dev, i->swffont, &i->fontmatrix);
726 r = swf_ClipRect(i->pagebbox, r);
727 swf_SetRect(i->tag,&r);
729 swf_SetMatrix(i->tag,&i->fontmatrix);
731 msg("<trace> Placing text (%d characters) as ID %d", i->chardatapos, i->textid);
733 putcharacters(dev, i->tag);
736 if(i->swf->fileVersion >= 8) {
737 i->tag = swf_InsertTag(i->tag, ST_CSMTEXTSETTINGS);
738 swf_SetU16(i->tag, i->textid);
740 //swf_SetU8(i->tag, /*subpixel grid*/(2<<3)|/*flashtype*/0x40);
741 swf_SetU8(i->tag, /*grid*/(1<<3)|/*flashtype*/0x40);
742 //swf_SetU8(i->tag, /*no grid*/(0<<3)|/*flashtype*/0x40);
744 swf_SetU32(i->tag, 0);//thickness
745 swf_SetU32(i->tag, 0);//sharpness
746 swf_SetU8(i->tag, 0);//reserved
748 i->tag = swf_InsertTag(i->tag,ST_PLACEOBJECT2);
750 swf_ObjectPlace(i->tag,i->textid,getNewDepth(dev),&i->page_matrix,NULL,NULL);
754 /* set's the matrix which is to be applied to characters drawn by swfoutput_drawchar() */
755 static void setfontscale(gfxdevice_t*dev,double m11,double m12, double m21,double m22,double x, double y, char force)
761 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
762 if(i->lastfontm11 == m11 &&
763 i->lastfontm12 == m12 &&
764 i->lastfontm21 == m21 &&
765 i->lastfontm22 == m22 && !force)
770 i->lastfontm11 = m11;
771 i->lastfontm12 = m12;
772 i->lastfontm21 = m21;
773 i->lastfontm22 = m22;
775 double xsize = sqrt(m11*m11 + m12*m12);
776 double ysize = sqrt(m21*m21 + m22*m22);
777 i->current_font_size = (xsize>ysize?xsize:ysize)*1;
778 if(i->current_font_size < 1)
779 i->current_font_size = 1;
780 double ifs = 1.0 / (i->current_font_size*GLYPH_SCALE);
783 m.sx = (S32)((m11*ifs)*65536); m.r1 = (S32)((m21*ifs)*65536);
784 m.r0 = (S32)((m12*ifs)*65536); m.sy = (S32)((m22*ifs)*65536);
785 /* this is the position of the first char to set a new fontmatrix-
786 we hope that it's close enough to all other characters using the
787 font, so we use its position as origin for the matrix */
793 static int watermark2_width=47;
794 static int watermark2_height=11;
795 static int watermark2[47] = {95,1989,71,0,2015,337,1678,0,2015,5,1921,320,1938,25,2006,1024,
796 1042,21,13,960,1039,976,8,2000,1359,1088,31,1989,321,1728,0,1152,
797 1344,832,0,1984,0,896,1088,1088,896,0,1984,128,256,512,1984};
799 static void draw_watermark(gfxdevice_t*dev, gfxbbox_t r, char drawall)
801 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
802 double wx = r.xmax / 5.0;
803 double tx = r.xmax*4.0 / 5.0;
804 double ty = r.ymax-wx*watermark2_height/watermark2_width;
805 double sx = (r.xmax - tx) / watermark2_width;
806 double sy = (r.ymax - ty) / watermark2_height;
809 if(ty > 0 && px > 1.0 && py > 1.0) {
811 for(y=0;y<watermark2_height;y++)
812 for(x=0;x<watermark2_width;x++) {
813 if(((watermark2[x]>>y)&1)) {
814 if(!drawall && rand()%5)
816 unsigned int b = rand();
817 moveto(dev, i->tag, x*sx+tx+((b>>1)&1)/20.0, y*sy+ty+((b>>3)&1)/20.0);
818 lineto(dev, i->tag, x*sx+px+tx+((b>>2)&1)/20.0, y*sy+ty+((b>>3)&1)/20.0);
819 lineto(dev, i->tag, x*sx+px+tx+((b>>2)&1)/20.0, y*sy+py+ty+((b>>4)&1)/20.0);
820 lineto(dev, i->tag, x*sx+tx+((b>>1)&1)/20.0, y*sy+py+ty+((b>>4)&1)/20.0);
821 lineto(dev, i->tag, x*sx+tx+((b>>1)&1)/20.0, y*sy+ty+((b>>3)&1)/20.0);
827 static void swfoutput_setfillcolor(gfxdevice_t* dev, U8 r, U8 g, U8 b, U8 a)
829 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
830 if(i->fillrgb.r == r &&
833 i->fillrgb.a == a) return;
842 static void insert_watermark(gfxdevice_t*dev, char drawall)
844 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
845 if(!drawall && i->watermarks>20)
851 swfoutput_setfillcolor(dev, 0,0,255,192);
853 swfoutput_setfillcolor(dev, rand(),rand(),rand(),(rand()&127)+128);
858 gfxbbox_t r; r.xmin = r.ymin = 0;
861 draw_watermark(dev, r, drawall);
867 static void endpage(gfxdevice_t*dev)
869 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
880 if(i->config_watermark) {
881 insert_watermark(dev, 1);
887 static void addViewer(gfxdevice_t* dev)
889 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
892 RGBA button_colors[3]= {{0xbf,0x00,0x00,0x80},{0xbf,0x20,0x20,0xc0}, {0xbf,0xc0,0xc0,0xff}};
894 int button_sizex = 20;
895 int button_sizey = 20;
897 RGBA black = {255,0,0,0};
899 i->tag = swf_InsertTag(i->tag,ST_DEFINESHAPE3);
901 int ls1 = swf_ShapeAddLineStyle(s,40,&black);
902 int fs1 = swf_ShapeAddSolidFillStyle(s,&button_colors[t/2]);
903 int shapeid = ids[t] = getNewID(dev);
904 swf_SetU16(i->tag,shapeid);
906 r.xmin = -20*button_sizex;
907 r.xmax = 20*button_sizex;
909 r.ymax = 40*button_sizey;
910 swf_SetRect(i->tag,&r); // set shape bounds
911 swf_SetShapeHeader(i->tag,s); // write all styles to tag
912 swf_ShapeSetAll(i->tag,s,0*button_sizex,0,ls1,fs1,0);
913 swf_ShapeSetLine(i->tag,s,(1-(t&1)*2)*20*button_sizex,20*button_sizey);
914 swf_ShapeSetLine(i->tag,s,-(1-(t&1)*2)*20*button_sizex,20*button_sizey);
915 swf_ShapeSetLine(i->tag,s,0,-40*button_sizey);
916 swf_ShapeSetEnd(i->tag); // finish drawing
917 swf_ShapeFree(s); // clean shape structure (which isn't needed anymore after writing the tag)
919 ActionTAG*a1=0,*a2=0,*a3=0;
920 a1 = action_NextFrame(a1);
921 a1 = action_Stop(a1);
924 a2 = action_PreviousFrame(a2);
925 a2 = action_Stop(a2);
928 a3 = action_Stop(a3);
931 i->tag = swf_InsertTag(i->tag, ST_DOACTION);
932 swf_ActionSet(i->tag,a3);
934 i->tag = swf_InsertTag(i->tag,ST_DEFINEBUTTON);
935 int buttonid1 = getNewID(dev);
936 swf_SetU16(i->tag, buttonid1);
937 swf_ButtonSetRecord(i->tag,BS_UP|BS_HIT,ids[0],1,NULL,NULL);
938 swf_ButtonSetRecord(i->tag,BS_OVER,ids[2],1,NULL,NULL);
939 swf_ButtonSetRecord(i->tag,BS_DOWN,ids[4],1,NULL,NULL);
940 swf_SetU8(i->tag,0); // end of button records
941 swf_ActionSet(i->tag,a1);
943 i->tag = swf_InsertTag(i->tag,ST_DEFINEBUTTON);
944 int buttonid2 = getNewID(dev);
945 swf_SetU16(i->tag, buttonid2);
946 swf_ButtonSetRecord(i->tag,BS_UP|BS_HIT,ids[1],1,NULL,NULL);
947 swf_ButtonSetRecord(i->tag,BS_OVER,ids[3],1,NULL,NULL);
948 swf_ButtonSetRecord(i->tag,BS_DOWN,ids[5],1,NULL,NULL);
949 swf_SetU8(i->tag,0); // end of button records
950 swf_ActionSet(i->tag,a2);
952 i->tag = swf_InsertTag(i->tag,ST_PLACEOBJECT2);
954 swf_GetMatrix(0, &m);
955 m.tx = button_sizex*20+200;
956 swf_ObjectPlace(i->tag, buttonid2, 65534,&m,0,0);
957 i->tag = swf_InsertTag(i->tag,ST_PLACEOBJECT2);
958 m.tx = button_sizex*20+200+200;
959 swf_ObjectPlace(i->tag, buttonid1, 65535,&m,0,0);
963 void swf_startframe(gfxdevice_t*dev, int width, int height)
965 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
967 if(i->config_protect) {
968 i->tag = swf_InsertTag(i->tag, ST_PROTECT);
969 i->config_protect = 0;
971 if(i->config_simpleviewer) {
976 if(!i->firstpage && !i->pagefinished)
979 msg("<verbose> Starting new SWF page of size %dx%d", width, height);
981 swf_GetMatrix(0, &i->page_matrix);
982 i->page_matrix.tx = 0;
983 i->page_matrix.ty = 0;
990 /* create a bbox structure with the page size. This is used
991 for clipping shape and text bounding boxes. As we don't want to
992 generate bounding boxes which extend beyond the movie size (in
993 order to not confuse Flash), we clip everything against i->pagebbox */
994 i->pagebbox.xmin = 0;
995 i->pagebbox.ymin = 0;
996 i->pagebbox.xmax = width*20;
997 i->pagebbox.ymax = height*20;
999 /* increase SWF's bounding box */
1000 swf_ExpandRect2(&i->swf->movieSize, &i->pagebbox);
1002 i->lastframeno = i->frameno;
1004 i->pagefinished = 0;
1007 void swf_endframe(gfxdevice_t*dev)
1009 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1011 if(!i->pagefinished)
1014 if( (i->swf->fileVersion <= 8) && (i->config_insertstoptag) ) {
1016 atag = action_Stop(atag);
1017 atag = action_End(atag);
1018 i->tag = swf_InsertTag(i->tag,ST_DOACTION);
1019 swf_ActionSet(i->tag,atag);
1021 i->tag = swf_InsertTag(i->tag,ST_SHOWFRAME);
1024 for(i->depth;i->depth>i->startdepth;i->depth--) {
1025 i->tag = swf_InsertTag(i->tag,ST_REMOVEOBJECT2);
1026 swf_SetU16(i->tag,i->depth);
1028 i->depth = i->startdepth;
1030 if(i->config_frameresets) {
1031 for(i->currentswfid;i->currentswfid>i->startids;i->currentswfid--) {
1032 i->tag = swf_InsertTag(i->tag,ST_FREECHARACTER);
1033 swf_SetU16(i->tag,i->currentswfid);
1035 i->currentswfid = i->startids;
1039 static void setBackground(gfxdevice_t*dev, int x1, int y1, int x2, int y2)
1041 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1043 rgb.a = rgb.r = rgb.g = rgb.b = 0xff;
1047 int shapeid = getNewID(dev);
1052 i->tag = swf_InsertTag(i->tag, ST_DEFINESHAPE);
1054 fs1 = swf_ShapeAddSolidFillStyle(s, &rgb);
1055 swf_SetU16(i->tag,shapeid);
1056 swf_SetRect(i->tag,&r);
1057 swf_SetShapeHeader(i->tag,s);
1058 swf_ShapeSetAll(i->tag,s,x1,y1,ls1,fs1,0);
1059 swf_ShapeSetLine(i->tag,s,(x2-x1),0);
1060 swf_ShapeSetLine(i->tag,s,0,(y2-y1));
1061 swf_ShapeSetLine(i->tag,s,(x1-x2),0);
1062 swf_ShapeSetLine(i->tag,s,0,(y1-y2));
1063 swf_ShapeSetEnd(i->tag);
1065 i->tag = swf_InsertTag(i->tag, ST_PLACEOBJECT2);
1066 swf_ObjectPlace(i->tag,shapeid,getNewDepth(dev),0,0,0);
1067 i->tag = swf_InsertTag(i->tag, ST_PLACEOBJECT2);
1068 swf_ObjectPlaceClip(i->tag,shapeid,getNewDepth(dev),0,0,0,65535);
1071 /* initialize the swf writer */
1072 void gfxdevice_swf_init(gfxdevice_t* dev)
1074 memset(dev, 0, sizeof(gfxdevice_t));
1078 dev->internal = init_internal_struct(); // set config to default values
1080 dev->startpage = swf_startframe;
1081 dev->endpage = swf_endframe;
1082 dev->finish = swf_finish;
1083 dev->fillbitmap = swf_fillbitmap;
1084 dev->setparameter = swf_setparameter;
1085 dev->stroke = swf_stroke;
1086 dev->startclip = swf_startclip;
1087 dev->endclip = swf_endclip;
1088 dev->fill = swf_fill;
1089 dev->fillbitmap = swf_fillbitmap;
1090 dev->fillgradient = swf_fillgradient;
1091 dev->addfont = swf_addfont;
1092 dev->drawchar = swf_drawchar;
1093 dev->drawlink = swf_drawlink;
1095 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1098 msg("<verbose> initializing swf output\n", i->max_x,i->max_y);
1102 i->swf = (SWF*)rfx_calloc(sizeof(SWF));
1103 i->swf->fileVersion = 0;
1104 i->swf->frameRate = 0x80;
1105 i->swf->movieSize.xmin = 0;
1106 i->swf->movieSize.ymin = 0;
1107 i->swf->movieSize.xmax = 0;
1108 i->swf->movieSize.ymax = 0;
1109 i->swf->fileAttributes = 9; // as3, local-with-network
1111 i->swf->firstTag = swf_InsertTag(NULL,ST_SETBACKGROUNDCOLOR);
1112 i->tag = i->swf->firstTag;
1114 rgb.a = rgb.r = rgb.g = rgb.b = 0xff;
1116 swf_SetRGB(i->tag,&rgb);
1118 i->startdepth = i->depth = 0;
1119 i->startids = i->currentswfid = 0;
1122 static void startshape(gfxdevice_t*dev)
1124 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1129 //if(i->chardatapos && i->chardata[i->chardatapos-1].color.a)
1132 i->tag = swf_InsertTag(i->tag,ST_DEFINESHAPE3);
1134 swf_ShapeNew(&i->shape);
1135 i->linestyleid = swf_ShapeAddLineStyle(i->shape,i->linewidth,&i->strokergb);
1136 i->fillstyleid = swf_ShapeAddSolidFillStyle(i->shape,&i->fillrgb);
1138 RGBA markcol = {0,i->mark[0],i->mark[1],i->mark[2]};
1139 swf_ShapeAddSolidFillStyle(i->shape,&markcol);
1142 i->shapeid = getNewID(dev);
1144 msg("<debug> Using shape id %d", i->shapeid);
1146 swf_SetU16(i->tag,i->shapeid); // ID
1148 i->bboxrectpos = i->tag->len;
1150 swf_SetRect(i->tag,&i->pagebbox);
1152 memset(&i->bboxrect, 0, sizeof(i->bboxrect));
1154 swf_SetShapeStyles(i->tag,i->shape);
1155 swf_ShapeCountBits(i->shape,NULL,NULL);
1156 swf_SetShapeBits(i->tag,i->shape);
1158 /* TODO: do we really need this? */
1159 //swf_ShapeSetAll(i->tag,i->shape,/*x*/0,/*y*/0,i->linestyleid,0,0);
1160 //swf_ShapeSetAll(i->tag,i->shape,/*x*/UNDEFINED_COORD,/*y*/UNDEFINED_COORD,i->linestyleid,0,0);
1161 i->swflastx=i->swflasty=UNDEFINED_COORD;
1162 i->lastwasfill = -1;
1163 i->shapeisempty = 1;
1166 static void starttext(gfxdevice_t*dev)
1168 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1172 if(i->config_watermark) {
1173 insert_watermark(dev, 0);
1175 i->textid = getNewID(dev);
1176 i->swflastx=i->swflasty=0;
1180 /* TODO: move to ../lib/rfxswf */
1181 void changeRect(gfxdevice_t*dev, TAG*tag, int pos, SRECT*newrect)
1183 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1184 /* determine length of old rect */
1188 swf_GetRect(tag, &old);
1189 swf_ResetReadBits(tag);
1190 int pos_end = tag->pos;
1192 int len = tag->len - pos_end;
1193 U8*data = (U8*)malloc(len);
1194 memcpy(data, &tag->data[pos_end], len);
1197 swf_SetRect(tag, newrect);
1198 swf_SetBlock(tag, data, len);
1200 tag->pos = tag->readBit = 0;
1203 void cancelshape(gfxdevice_t*dev)
1205 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1206 /* delete old shape tag */
1208 i->tag = i->tag->prev;
1209 swf_DeleteTag(0, todel);
1210 if(i->shape) {swf_ShapeFree(i->shape);i->shape=0;}
1212 i->bboxrectpos = -1;
1214 // i->currentswfid--; // doesn't work, for some reason
1217 void fixAreas(gfxdevice_t*dev)
1219 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1220 if(!i->shapeisempty && i->fill &&
1221 (i->bboxrect.xmin == i->bboxrect.xmax ||
1222 i->bboxrect.ymin == i->bboxrect.ymax) &&
1223 i->config_minlinewidth >= 0.001
1225 msg("<debug> Shape has size 0: width=%.2f height=%.2f",
1226 (i->bboxrect.xmax-i->bboxrect.xmin)/20.0,
1227 (i->bboxrect.ymax-i->bboxrect.ymin)/20.0
1230 SRECT r = i->bboxrect;
1232 if(r.xmin == r.xmax && r.ymin == r.ymax) {
1233 /* this thing comes down to a single dot- nothing to fix here */
1239 RGBA save_col = i->strokergb;
1240 int save_width = i->linewidth;
1242 i->strokergb = i->fillrgb;
1243 i->linewidth = (int)(i->config_minlinewidth*20);
1244 if(i->linewidth==0) i->linewidth = 1;
1249 moveto(dev, i->tag, r.xmin/20.0,r.ymin/20.0);
1250 lineto(dev, i->tag, r.xmax/20.0,r.ymax/20.0);
1252 i->strokergb = save_col;
1253 i->linewidth = save_width;
1258 static void endshape_noput(gfxdevice_t*dev)
1260 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1263 //changeRect(dev, i->tag, i->bboxrectpos, &i->bboxrect);
1266 swf_ShapeFree(i->shape);
1274 static void endshape(gfxdevice_t*dev)
1276 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1282 if(i->shapeisempty ||
1284 (i->bboxrect.xmin == i->bboxrect.xmax &&
1285 i->bboxrect.ymin == i->bboxrect.ymax))
1287 // delete the shape again, we didn't do anything
1288 msg("<debug> cancelling shape: bbox is (%f,%f,%f,%f)",
1289 i->bboxrect.xmin /20.0,
1290 i->bboxrect.ymin /20.0,
1291 i->bboxrect.xmax /20.0,
1292 i->bboxrect.ymax /20.0
1298 swf_ShapeSetEnd(i->tag);
1300 SRECT r = swf_ClipRect(i->pagebbox, i->bboxrect);
1301 changeRect(dev, i->tag, i->bboxrectpos, &r);
1303 msg("<trace> Placing shape ID %d", i->shapeid);
1305 i->tag = swf_InsertTag(i->tag,ST_PLACEOBJECT2);
1306 MATRIX m = i->page_matrix;
1307 m.tx += i->shapeposx;
1308 m.ty += i->shapeposy;
1309 swf_ObjectPlace(i->tag,i->shapeid,getNewDepth(dev),&m,NULL,NULL);
1311 if(i->config_animate) {
1312 i->tag = swf_InsertTag(i->tag,ST_SHOWFRAME);
1315 swf_ShapeFree(i->shape);
1318 i->bboxrectpos = -1;
1325 void wipeSWF(SWF*swf)
1327 TAG*tag = swf->firstTag;
1329 TAG*next = tag->next;
1330 if(tag->id != ST_SETBACKGROUNDCOLOR &&
1331 tag->id != ST_END &&
1332 tag->id != ST_DOACTION &&
1333 tag->id != ST_SHOWFRAME) {
1334 swf_DeleteTag(swf, tag);
1340 void swfoutput_finalize(gfxdevice_t*dev)
1342 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1344 if(i->tag && i->tag->id == ST_END)
1345 return; //already done
1347 i->swf->fileVersion = i->config_flashversion;
1348 i->swf->frameRate = i->config_framerate*0x100;
1350 if(i->config_bboxvars) {
1351 TAG* tag = swf_InsertTag(i->swf->firstTag, ST_DOACTION);
1353 a = action_PushString(a, "xmin");
1354 a = action_PushFloat(a, i->swf->movieSize.xmin / 20.0);
1355 a = action_SetVariable(a);
1356 a = action_PushString(a, "ymin");
1357 a = action_PushFloat(a, i->swf->movieSize.ymin / 20.0);
1358 a = action_SetVariable(a);
1359 a = action_PushString(a, "xmax");
1360 a = action_PushFloat(a, i->swf->movieSize.xmax / 20.0);
1361 a = action_SetVariable(a);
1362 a = action_PushString(a, "ymax");
1363 a = action_PushFloat(a, i->swf->movieSize.ymax / 20.0);
1364 a = action_SetVariable(a);
1365 a = action_PushString(a, "width");
1366 a = action_PushFloat(a, (i->swf->movieSize.xmax - i->swf->movieSize.xmin) / 20.0);
1367 a = action_SetVariable(a);
1368 a = action_PushString(a, "height");
1369 a = action_PushFloat(a, (i->swf->movieSize.ymax - i->swf->movieSize.ymin) / 20.0);
1370 a = action_SetVariable(a);
1372 swf_ActionSet(tag, a);
1377 free(i->mark);i->mark = 0;
1381 fontlist_t *iterator = i->fontlist;
1383 TAG*mtag = i->swf->firstTag;
1384 if(iterator->swffont) {
1385 if(!i->config_storeallcharacters) {
1386 msg("<debug> Reducing font %s", iterator->swffont->name);
1387 swf_FontReduce(iterator->swffont);
1389 int used = iterator->swffont->use && iterator->swffont->use->used_glyphs;
1391 mtag = swf_InsertTag(mtag, ST_DEFINEFONT2);
1392 swf_FontSetDefine2(mtag, iterator->swffont);
1396 iterator = iterator->next;
1399 i->tag = swf_InsertTag(i->tag,ST_END);
1400 TAG* tag = i->tag->prev;
1402 /* remove the removeobject2 tags between the last ST_SHOWFRAME
1403 and the ST_END- they confuse the flash player */
1404 while(tag->id == ST_REMOVEOBJECT2) {
1405 TAG* prev = tag->prev;
1406 swf_DeleteTag(i->swf, tag);
1413 if(i->config_enablezlib || i->config_flashversion>=6) {
1414 i->swf->compressed = 1;
1417 /* Add AVM2 actionscript */
1418 if(i->config_flashversion>=9 &&
1419 (i->config_insertstoptag || i->hasbuttons)) {
1420 swf_AddButtonLinks(i->swf, i->config_insertstoptag,
1421 i->config_internallinkfunction||i->config_externallinkfunction);
1423 // if(i->config_reordertags)
1424 // swf_Optimize(i->swf);
1427 int swfresult_save(gfxresult_t*gfx, const char*filename)
1429 SWF*swf = (SWF*)gfx->internal;
1432 fi = open(filename, O_BINARY|O_CREAT|O_TRUNC|O_WRONLY, 0777);
1437 msg("<fatal> Could not create \"%s\". ", FIXNULL(filename));
1441 if FAILED(swf_WriteSWF(fi,swf))
1442 msg("<error> WriteSWF() failed.\n");
1448 void* swfresult_get(gfxresult_t*gfx, const char*name)
1450 SWF*swf = (SWF*)gfx->internal;
1451 if(!strcmp(name, "swf")) {
1452 return (void*)swf_CopySWF(swf);
1453 } else if(!strcmp(name, "xmin")) {
1454 return (void*)(ptroff_t)(swf->movieSize.xmin/20);
1455 } else if(!strcmp(name, "ymin")) {
1456 return (void*)(ptroff_t)(swf->movieSize.ymin/20);
1457 } else if(!strcmp(name, "xmax")) {
1458 return (void*)(ptroff_t)(swf->movieSize.xmax/20);
1459 } else if(!strcmp(name, "ymax")) {
1460 return (void*)(ptroff_t)(swf->movieSize.ymax/20);
1461 } else if(!strcmp(name, "width")) {
1462 return (void*)(ptroff_t)((swf->movieSize.xmax - swf->movieSize.xmin)/20);
1463 } else if(!strcmp(name, "height")) {
1464 return (void*)(ptroff_t)((swf->movieSize.ymax - swf->movieSize.ymin)/20);
1468 void swfresult_destroy(gfxresult_t*gfx)
1471 swf_FreeTags((SWF*)gfx->internal);
1472 free(gfx->internal);
1475 memset(gfx, 0, sizeof(gfxresult_t));
1479 static void swfoutput_destroy(gfxdevice_t* dev);
1481 gfxresult_t* swf_finish(gfxdevice_t* dev)
1483 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1486 if(i->config_linktarget) {
1487 free(i->config_linktarget);
1488 i->config_linktarget = 0;
1491 swfoutput_finalize(dev);
1492 SWF* swf = i->swf;i->swf = 0;
1493 swfoutput_destroy(dev);
1495 result = (gfxresult_t*)rfx_calloc(sizeof(gfxresult_t));
1496 result->internal = swf;
1497 result->save = swfresult_save;
1499 result->get = swfresult_get;
1500 result->destroy = swfresult_destroy;
1504 /* Perform cleaning up */
1505 static void swfoutput_destroy(gfxdevice_t* dev)
1507 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1509 /* not initialized yet- nothing to destroy */
1513 fontlist_t *tmp,*iterator = i->fontlist;
1515 if(iterator->swffont) {
1516 swf_FontFree(iterator->swffont);iterator->swffont=0;
1519 iterator = iterator->next;
1522 if(i->swf) {swf_FreeTags(i->swf);free(i->swf);i->swf = 0;}
1525 memset(dev, 0, sizeof(gfxdevice_t));
1528 static void swfoutput_setstrokecolor(gfxdevice_t* dev, U8 r, U8 g, U8 b, U8 a)
1530 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1531 if(i->strokergb.r == r &&
1532 i->strokergb.g == g &&
1533 i->strokergb.b == b &&
1534 i->strokergb.a == a) return;
1544 //#define ROUND_UP 19
1545 //#define ROUND_UP 10
1547 static void swfoutput_setlinewidth(gfxdevice_t*dev, double _linewidth)
1549 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1550 if(i->linewidth == (U16)(_linewidth*20+19.0/20.0))
1554 i->linewidth = (U16)(_linewidth*20+19.0/20.0);
1558 static void drawlink(gfxdevice_t*dev, ActionTAG*,ActionTAG*, gfxline_t*points, char mouseover, const char*url);
1559 static void swfoutput_namedlink(gfxdevice_t*dev, char*name, gfxline_t*points);
1560 static void swfoutput_linktopage(gfxdevice_t*dev, int page, gfxline_t*points);
1561 static void swfoutput_linktourl(gfxdevice_t*dev, const char*url, gfxline_t*points);
1563 /*void swfoutput_drawlink(gfxdevice_t*dev, char*url, gfxline_t*points)
1565 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1566 dev->drawlink(dev, points, url);
1569 void swf_drawlink(gfxdevice_t*dev, gfxline_t*points, const char*url)
1571 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1573 if(i->config_disablelinks)
1576 if(!strncmp("http://pdf2swf:", url, 15)) {
1577 char*tmp = strdup(url);
1578 int l = strlen(tmp);
1581 swfoutput_namedlink(dev, tmp+15, points);
1584 } else if(!strncmp("page", url, 4)) {
1587 if(url[t]<'0' || url[t]>'9')
1590 int page = atoi(&url[4]);
1591 if(page<0) page = 0;
1592 swfoutput_linktopage(dev, page, points);
1595 swfoutput_linktourl(dev, url, points);
1598 void swfoutput_linktourl(gfxdevice_t*dev, const char*url, gfxline_t*points)
1600 ActionTAG* actions = 0;
1601 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1607 /* TODO: escape special characters in url */
1609 if(i->config_externallinkfunction && i->config_flashversion<=8) {
1610 actions = action_PushString(actions, url); //parameter
1611 actions = action_PushInt(actions, 1); //number of parameters (1)
1612 actions = action_PushString(actions, i->config_externallinkfunction); //function name
1613 actions = action_CallFunction(actions);
1614 } else if(!i->config_linktarget) {
1615 if(!i->config_opennewwindow)
1616 actions = action_GetUrl(actions, url, "_parent");
1618 actions = action_GetUrl(actions, url, "_this");
1620 actions = action_GetUrl(actions, url, i->config_linktarget);
1622 actions = action_End(actions);
1624 drawlink(dev, actions, 0, points, 0, url);
1626 swf_ActionFree(actions);
1628 void swfoutput_linktopage(gfxdevice_t*dev, int page, gfxline_t*points)
1630 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1631 ActionTAG* actions = 0;
1638 if(!i->config_internallinkfunction || i->config_flashversion>=9) {
1639 actions = action_GotoFrame(actions, page-1);
1640 actions = action_End(actions);
1642 actions = action_PushInt(actions, page); //parameter
1643 actions = action_PushInt(actions, 1); //number of parameters (1)
1644 actions = action_PushString(actions, i->config_internallinkfunction); //function name
1645 actions = action_CallFunction(actions);
1646 actions = action_End(actions);
1650 sprintf(name, "page%d", page);
1652 drawlink(dev, actions, 0, points, 0, name);
1654 swf_ActionFree(actions);
1657 /* Named Links (a.k.a. Acrobatmenu) are used to implement various gadgets
1658 of the viewer objects, like subtitles, index elements etc.
1660 void swfoutput_namedlink(gfxdevice_t*dev, char*name, gfxline_t*points)
1662 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1663 ActionTAG *actions1,*actions2;
1664 char*tmp = strdup(name);
1672 if(!strncmp(tmp, "call:", 5))
1674 char*x = strchr(&tmp[5], ':');
1676 actions1 = action_PushInt(0, 0); //number of parameters (0)
1677 actions1 = action_PushString(actions1, &tmp[5]); //function name
1678 actions1 = action_CallFunction(actions1);
1679 actions1 = action_End(actions1);
1682 actions1 = action_PushString(0, x+1); //parameter
1683 actions1 = action_PushInt(actions1, 1); //number of parameters (1)
1684 actions1 = action_PushString(actions1, &tmp[5]); //function name
1685 actions1 = action_CallFunction(actions1);
1686 actions1 = action_End(actions1);
1688 actions2 = action_End(0);
1693 actions1 = action_PushString(0, "/:subtitle");
1694 actions1 = action_PushString(actions1, name);
1695 actions1 = action_SetVariable(actions1);
1696 actions1 = action_End(actions1);
1698 actions2 = action_PushString(0, "/:subtitle");
1699 actions2 = action_PushString(actions2, "");
1700 actions2 = action_SetVariable(actions2);
1701 actions2 = action_End(actions2);
1704 drawlink(dev, actions1, actions2, points, mouseover, name);
1706 swf_ActionFree(actions1);
1707 swf_ActionFree(actions2);
1711 static void drawgfxline(gfxdevice_t*dev, gfxline_t*line, int fill)
1713 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1714 gfxcoord_t lastx=0,lasty=0,px=0,py=0;
1716 int lines= 0, splines=0;
1723 /* check whether the next segment is zero */
1724 if(line->type == gfx_moveTo) {
1725 moveto(dev, i->tag, line->x, line->y);
1726 px = lastx = line->x;
1727 py = lasty = line->y;
1729 } if(line->type == gfx_lineTo) {
1730 lineto(dev, i->tag, line->x, line->y);
1735 } else if(line->type == gfx_splineTo) {
1737 s.x = line->sx;p.x = line->x;
1738 s.y = line->sy;p.y = line->y;
1739 splineto(dev, i->tag, s, p);
1747 msg("<trace> drawgfxline, %d lines, %d splines", lines, splines);
1751 static void drawlink(gfxdevice_t*dev, ActionTAG*actions1, ActionTAG*actions2, gfxline_t*points, char mouseover, const char*url)
1753 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1762 int buttonid = getNewID(dev);
1763 gfxbbox_t bbox = gfxline_getbbox(points);
1768 myshapeid = getNewID(dev);
1769 i->tag = swf_InsertTag(i->tag,ST_DEFINESHAPE3);
1770 swf_ShapeNew(&i->shape);
1771 rgb.r = rgb.b = rgb.a = rgb.g = 0;
1772 fsid = swf_ShapeAddSolidFillStyle(i->shape,&rgb);
1773 swf_SetU16(i->tag, myshapeid);
1774 r.xmin = (int)(bbox.xmin*20);
1775 r.ymin = (int)(bbox.ymin*20);
1776 r.xmax = (int)(bbox.xmax*20);
1777 r.ymax = (int)(bbox.ymax*20);
1778 r = swf_ClipRect(i->pagebbox, r);
1779 swf_SetRect(i->tag,&r);
1780 swf_SetShapeStyles(i->tag,i->shape);
1781 swf_ShapeCountBits(i->shape,NULL,NULL);
1782 swf_SetShapeBits(i->tag,i->shape);
1783 swf_ShapeSetAll(i->tag,i->shape,/*x*/0,/*y*/0,0,fsid,0);
1784 i->swflastx = i->swflasty = 0;
1785 drawgfxline(dev, points, 1);
1786 swf_ShapeSetEnd(i->tag);
1787 swf_ShapeFree(i->shape);
1790 myshapeid2 = getNewID(dev);
1791 i->tag = swf_InsertTag(i->tag,ST_DEFINESHAPE3);
1792 swf_ShapeNew(&i->shape);
1794 rgb = i->config_linkcolor;
1796 fsid = swf_ShapeAddSolidFillStyle(i->shape,&rgb);
1797 swf_SetU16(i->tag, myshapeid2);
1798 r.xmin = (int)(bbox.xmin*20);
1799 r.ymin = (int)(bbox.ymin*20);
1800 r.xmax = (int)(bbox.xmax*20);
1801 r.ymax = (int)(bbox.ymax*20);
1802 r = swf_ClipRect(i->pagebbox, r);
1803 swf_SetRect(i->tag,&r);
1804 swf_SetShapeStyles(i->tag,i->shape);
1805 swf_ShapeCountBits(i->shape,NULL,NULL);
1806 swf_SetShapeBits(i->tag,i->shape);
1807 swf_ShapeSetAll(i->tag,i->shape,/*x*/0,/*y*/0,0,fsid,0);
1808 i->swflastx = i->swflasty = 0;
1809 drawgfxline(dev, points, 1);
1810 swf_ShapeSetEnd(i->tag);
1811 swf_ShapeFree(i->shape);
1815 i->tag = swf_InsertTag(i->tag,ST_DEFINEBUTTON);
1816 swf_SetU16(i->tag,buttonid); //id
1817 swf_ButtonSetFlags(i->tag, 0); //menu=no
1818 swf_ButtonSetRecord(i->tag,0x01,myshapeid,i->depth,0,0);
1819 swf_ButtonSetRecord(i->tag,0x02,myshapeid2,i->depth,0,0);
1820 swf_ButtonSetRecord(i->tag,0x04,myshapeid2,i->depth,0,0);
1821 swf_ButtonSetRecord(i->tag,0x08,myshapeid,i->depth,0,0);
1822 swf_SetU8(i->tag,0);
1823 swf_ActionSet(i->tag,actions1);
1824 swf_SetU8(i->tag,0);
1828 i->tag = swf_InsertTag(i->tag,ST_DEFINEBUTTON2);
1829 swf_SetU16(i->tag,buttonid); //id
1830 swf_ButtonSetFlags(i->tag, 0); //menu=no
1831 swf_ButtonSetRecord(i->tag,0x01,myshapeid,i->depth,0,0);
1832 swf_ButtonSetRecord(i->tag,0x02,myshapeid2,i->depth,0,0);
1833 swf_ButtonSetRecord(i->tag,0x04,myshapeid2,i->depth,0,0);
1834 swf_ButtonSetRecord(i->tag,0x08,myshapeid,i->depth,0,0);
1835 swf_SetU8(i->tag,0); // end of button records
1836 swf_ButtonSetCondition(i->tag, BC_IDLE_OVERUP);
1837 swf_ActionSet(i->tag,actions1);
1839 swf_ButtonSetCondition(i->tag, BC_OVERUP_IDLE);
1840 swf_ActionSet(i->tag,actions2);
1841 swf_SetU8(i->tag,0);
1842 swf_ButtonPostProcess(i->tag, 2);
1844 swf_SetU8(i->tag,0);
1845 swf_ButtonPostProcess(i->tag, 1);
1849 const char* name = 0;
1850 if(i->config_linknameurl) {
1854 sprintf(buf, "button%d", buttonid);
1857 msg("<trace> Placing link ID %d", buttonid);
1858 i->tag = swf_InsertTag(i->tag,ST_PLACEOBJECT2);
1860 if(posx!=0 || posy!=0) {
1862 p.x = (int)(posx*20);
1863 p.y = (int)(posy*20);
1864 p = swf_TurnPoint(p, &i->page_matrix);
1869 swf_ObjectPlace(i->tag, buttonid, getNewDepth(dev),&m,0,name);
1871 swf_ObjectPlace(i->tag, buttonid, getNewDepth(dev),&i->page_matrix,0,name);
1878 for(t=0;t<picpos;t++)
1880 if(pic_xids[t] == xid &&
1881 pic_yids[t] == yid) {
1882 width = pic_width[t];
1883 height = pic_height[t];
1887 pic_ids[picpos] = swfoutput_drawimagelosslessN(&output, pic, pal, width, height, x1,y1,x2,y2,x3,y3,x4,y4, numpalette);
1888 pic_xids[picpos] = xid;
1889 pic_yids[picpos] = yid;
1890 pic_width[picpos] = width;
1891 pic_height[picpos] = height;
1894 pic[width*y+x] = buf[0];
1898 xid += pal[1].r*3 + pal[1].g*11 + pal[1].b*17;
1899 yid += pal[1].r*7 + pal[1].g*5 + pal[1].b*23;
1903 xid += x*r+x*b*3+x*g*7+x*a*11;
1904 yid += y*r*3+y*b*17+y*g*19+y*a*11;
1906 for(t=0;t<picpos;t++)
1908 if(pic_xids[t] == xid &&
1909 pic_yids[t] == yid) {
1918 int swf_setparameter(gfxdevice_t*dev, const char*name, const char*value)
1920 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1922 msg("<trace> swfdevice: %s=%s", name, value);
1923 if(!strcmp(name, "jpegsubpixels")) {
1924 i->config_jpegsubpixels = atof(value);
1925 } else if(!strcmp(name, "ppmsubpixels")) {
1926 i->config_ppmsubpixels = atof(value);
1927 } else if(!strcmp(name, "subpixels")) {
1928 i->config_ppmsubpixels = i->config_jpegsubpixels = atof(value);
1929 } else if(!strcmp(name, "drawonlyshapes")) {
1930 i->config_drawonlyshapes = atoi(value);
1931 } else if(!strcmp(name, "ignoredraworder")) {
1932 i->config_ignoredraworder = atoi(value);
1933 } else if(!strcmp(name, "mark")) {
1934 if(!value || !value[0]) {
1935 if(i->mark) free(i->mark);
1939 i->mark = strdup("...");
1940 for(t=0;t<3;t++) if(value[t]) i->mark[t] = value[t];
1942 } else if(!strcmp(name, "filloverlap")) {
1943 i->config_filloverlap = atoi(value);
1944 } else if(!strcmp(name, "linksopennewwindow")) {
1945 i->config_opennewwindow = atoi(value);
1946 } else if(!strcmp(name, "opennewwindow")) {
1947 i->config_opennewwindow = atoi(value);
1948 } else if(!strcmp(name, "storeallcharacters")) {
1949 i->config_storeallcharacters = atoi(value);
1950 } else if(!strcmp(name, "enablezlib")) {
1951 i->config_enablezlib = atoi(value);
1952 } else if(!strcmp(name, "bboxvars")) {
1953 i->config_bboxvars = atoi(value);
1954 } else if(!strcmp(name, "dots")) {
1955 i->config_dots = atoi(value);
1956 } else if(!strcmp(name, "frameresets")) {
1957 i->config_frameresets = atoi(value);
1958 } else if(!strcmp(name, "showclipshapes")) {
1959 i->config_showclipshapes = atoi(value);
1960 } else if(!strcmp(name, "reordertags")) {
1961 i->config_reordertags = atoi(value);
1962 } else if(!strcmp(name, "internallinkfunction")) {
1963 i->config_internallinkfunction = strdup(value);
1964 } else if(!strcmp(name, "externallinkfunction")) {
1965 i->config_externallinkfunction = strdup(value);
1966 } else if(!strcmp(name, "linkfunction")) { //sets both internallinkfunction and externallinkfunction
1967 i->config_internallinkfunction = strdup(value);
1968 i->config_externallinkfunction = strdup(value);
1969 } else if(!strcmp(name, "disable_polygon_conversion")) {
1970 i->config_disable_polygon_conversion = atoi(value);
1971 } else if(!strcmp(name, "normalize_polygon_positions")) {
1972 i->config_normalize_polygon_positions = atoi(value);
1973 } else if(!strcmp(name, "wxwindowparams")) {
1974 i->config_watermark = atoi(value);
1975 } else if(!strcmp(name, "insertstop")) {
1976 i->config_insertstoptag = atoi(value);
1977 } else if(!strcmp(name, "protect")) {
1978 i->config_protect = atoi(value);
1979 if(i->config_protect && i->tag) {
1980 i->tag = swf_InsertTag(i->tag, ST_PROTECT);
1982 } else if(!strcmp(name, "flashversion")) {
1983 i->config_flashversion = atoi(value);
1985 i->swf->fileVersion = i->config_flashversion;
1987 } else if(!strcmp(name, "framerate")) {
1988 i->config_framerate = atof(value);
1990 i->swf->frameRate = i->config_framerate*0x100;
1992 } else if(!strcmp(name, "minlinewidth")) {
1993 i->config_minlinewidth = atof(value);
1994 } else if(!strcmp(name, "caplinewidth")) {
1995 i->config_caplinewidth = atof(value);
1996 } else if(!strcmp(name, "linktarget")) {
1997 i->config_linktarget = strdup(value);
1998 } else if(!strcmp(name, "dumpfonts")) {
1999 i->config_dumpfonts = atoi(value);
2000 } else if(!strcmp(name, "animate")) {
2001 i->config_animate = atoi(value);
2002 } else if(!strcmp(name, "disablelinks")) {
2003 i->config_disablelinks = atoi(value);
2004 } else if(!strcmp(name, "simpleviewer")) {
2005 i->config_simpleviewer = atoi(value);
2006 } else if(!strcmp(name, "next_bitmap_is_jpeg")) {
2008 } else if(!strcmp(name, "jpegquality")) {
2009 int val = atoi(value);
2011 if(val>101) val=101;
2012 i->config_jpegquality = val;
2013 } else if(!strcmp(name, "splinequality")) {
2014 int v = atoi(value);
2015 v = 500-(v*5); // 100% = 0.25 pixel, 0% = 25 pixel
2017 i->config_splinemaxerror = v;
2018 } else if(!strcmp(name, "fontquality")) {
2019 int v = atoi(value);
2020 v = 500-(v*5); // 100% = 0.25 pixel, 0% = 25 pixel
2022 i->config_fontsplinemaxerror = v;
2023 } else if(!strcmp(name, "linkcolor")) {
2024 if(strlen(value)!=8) {
2025 fprintf(stderr, "Unknown format for option 'linkcolor'. (%s <-> RRGGBBAA)\n", value);
2028 # define NIBBLE(s) (((s)>='0' && (s)<='9')?((s)-'0'):((s)&0x0f)+9)
2029 i->config_linkcolor.r = NIBBLE(value[0])<<4 | NIBBLE(value[1]);
2030 i->config_linkcolor.g = NIBBLE(value[2])<<4 | NIBBLE(value[3]);
2031 i->config_linkcolor.b = NIBBLE(value[4])<<4 | NIBBLE(value[5]);
2032 i->config_linkcolor.a = NIBBLE(value[6])<<4 | NIBBLE(value[7]);
2033 } else if(!strcmp(name, "help")) {
2034 printf("\nSWF layer options:\n");
2035 printf("jpegsubpixels=<pixels> resolution adjustment for jpeg images (same as jpegdpi, but in pixels)\n");
2036 printf("ppmsubpixels=<pixels resolution adjustment for lossless images (same as ppmdpi, but in pixels)\n");
2037 printf("subpixels=<pixels> shortcut for setting both jpegsubpixels and ppmsubpixels\n");
2038 printf("drawonlyshapes convert everything to shapes (currently broken)\n");
2039 printf("ignoredraworder allow to perform a few optimizations for creating smaller SWFs\n");
2040 printf("linksopennewwindow make links open a new browser window\n");
2041 printf("linktarget target window name of new links\n");
2042 printf("linkcolor=<color) color of links (format: RRGGBBAA)\n");
2043 printf("linknameurl Link buttons will be named like the URL they refer to (handy for iterating through links with actionscript)\n");
2044 printf("storeallcharacters don't reduce the fonts to used characters in the output file\n");
2045 printf("enablezlib switch on zlib compression (also done if flashversion>=7)\n");
2046 printf("bboxvars store the bounding box of the SWF file in actionscript variables\n");
2047 printf("dots Take care to handle dots correctly\n");
2048 printf("reordertags=0/1 (default: 1) perform some tag optimizations\n");
2049 printf("internallinkfunction=<name> when the user clicks a internal link (to a different page) in the converted file, this actionscript function is called\n");
2050 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");
2051 printf("disable_polygon_conversion never convert strokes to polygons (will remove capstyles and joint styles)\n");
2052 printf("caplinewidth=<width> the minimum thichness a line needs to have so that capstyles become visible (and are converted)\n");
2053 printf("insertstop put an ActionScript \"STOP\" tag in every frame\n");
2054 printf("protect add a \"protect\" tag to the file, to prevent loading in the Flash editor\n");
2055 printf("flashversion=<version> the SWF fileversion (6)\n");
2056 printf("framerate=<fps> SWF framerate\n");
2057 printf("minlinewidth=<width> convert horizontal/vertical boxes smaller than this width to lines (0.05) \n");
2058 printf("simpleviewer Add next/previous buttons to the SWF\n");
2059 printf("animate insert a showframe tag after each placeobject (animate draw order of PDF files)\n");
2060 printf("jpegquality=<quality> set compression quality of jpeg images\n");
2061 printf("splinequality=<value> Set the quality of spline convertion to value (0-100, default: 100).\n");
2062 printf("disablelinks Disable links.\n");
2069 // --------------------------------------------------------------------
2071 static CXFORM gfxcxform_to_cxform(gfxcxform_t* c)
2074 swf_GetCXForm(0, &cx, 1);
2077 if(c->rg!=0 || c->rb!=0 || c->ra!=0 ||
2078 c->gr!=0 || c->gb!=0 || c->ga!=0 ||
2079 c->br!=0 || c->bg!=0 || c->ba!=0 ||
2080 c->ar!=0 || c->ag!=0 || c->ab!=0)
2081 msg("<warning> CXForm not SWF-compatible");
2083 cx.a0 = (S16)(c->aa*256);
2084 cx.r0 = (S16)(c->rr*256);
2085 cx.g0 = (S16)(c->gg*256);
2086 cx.b0 = (S16)(c->bb*256);
2095 static int imageInCache(gfxdevice_t*dev, void*data, int width, int height)
2099 static void addImageToCache(gfxdevice_t*dev, void*data, int width, int height)
2103 static int add_image(swfoutput_internal*i, gfximage_t*img, int targetwidth, int targetheight, int* newwidth, int* newheight)
2105 gfxdevice_t*dev = i->dev;
2107 RGBA*mem = (RGBA*)img->data;
2109 int sizex = img->width;
2110 int sizey = img->height;
2111 int is_jpeg = i->jpeg;
2114 int newsizex=sizex, newsizey=sizey;
2117 if(is_jpeg && i->config_jpegsubpixels) {
2118 newsizex = (int)(targetwidth*i->config_jpegsubpixels + 0.5);
2119 newsizey = (int)(targetheight*i->config_jpegsubpixels + 0.5);
2120 } else if(!is_jpeg && i->config_ppmsubpixels) {
2121 newsizex = (int)(targetwidth*i->config_ppmsubpixels + 0.5);
2122 newsizey = (int)(targetheight*i->config_ppmsubpixels + 0.5);
2126 if(sizex<=0 || sizey<=0)
2133 /* TODO: cache images */
2135 if(newsizex<sizex || newsizey<sizey) {
2136 msg("<verbose> Scaling %dx%d image to %dx%d", sizex, sizey, newsizex, newsizey);
2137 newpic = swf_ImageScale(mem, sizex, sizey, newsizex, newsizey);
2138 *newwidth = sizex = newsizex;
2139 *newheight = sizey = newsizey;
2142 *newwidth = newsizex = sizex;
2143 *newheight = newsizey = sizey;
2146 int num_colors = swf_ImageGetNumberOfPaletteEntries(mem,sizex,sizey,0);
2147 int has_alpha = swf_ImageHasAlpha(mem,sizex,sizey);
2149 msg("<verbose> Drawing %dx%d %s%simage (id %d) at size %dx%d (%dx%d), %s%d colors",
2151 has_alpha?(has_alpha==2?"semi-transparent ":"transparent "):"",
2152 is_jpeg?"jpeg-":"", i->currentswfid+1,
2154 targetwidth, targetheight,
2155 /*newsizex, newsizey,*/
2156 num_colors>256?">":"", num_colors>256?256:num_colors);
2158 /*RGBA* pal = (RGBA*)rfx_alloc(sizeof(RGBA)*num_colors);
2159 swf_ImageGetNumberOfPaletteEntries(mem,sizex,sizey,pal);
2161 for(t=0;t<num_colors;t++) {
2162 printf("%02x%02x%02x%02x ",
2163 pal[t].r, pal[t].g, pal[t].b, pal[t].a);
2170 int cacheid = imageInCache(dev, mem, sizex, sizey);
2173 bitid = getNewID(dev);
2175 i->tag = swf_AddImage(i->tag, bitid, mem, sizex, sizey, i->config_jpegquality);
2176 addImageToCache(dev, mem, sizex, sizey);
2186 static SRECT gfxline_getSWFbbox(gfxline_t*line)
2188 gfxbbox_t bbox = gfxline_getbbox(line);
2190 r.xmin = (int)(bbox.xmin*20);
2191 r.ymin = (int)(bbox.ymin*20);
2192 r.xmax = (int)(bbox.xmax*20);
2193 r.ymax = (int)(bbox.ymax*20);
2197 int line_is_empty(gfxline_t*line)
2200 if(line->type != gfx_moveTo)
2207 static void swf_fillbitmap(gfxdevice_t*dev, gfxline_t*line, gfximage_t*img, gfxmatrix_t*matrix, gfxcxform_t*cxform)
2209 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
2211 if(line_is_empty(line))
2217 int targetx = (int)(sqrt(matrix->m00*matrix->m00 + matrix->m01*matrix->m01)*img->width);
2218 int targety = (int)(sqrt(matrix->m10*matrix->m10 + matrix->m11*matrix->m11)*img->height);
2220 int newwidth=0,newheight=0;
2221 int bitid = add_image(i, img, targetx, targety, &newwidth, &newheight);
2224 double fx = (double)img->width / (double)newwidth;
2225 double fy = (double)img->height / (double)newheight;
2228 m.sx = (int)(65536*20*matrix->m00*fx); m.r1 = (int)(65536*20*matrix->m10*fy);
2229 m.r0 = (int)(65536*20*matrix->m01*fx); m.sy = (int)(65536*20*matrix->m11*fy);
2230 m.tx = (int)(matrix->tx*20);
2231 m.ty = (int)(matrix->ty*20);
2234 int myshapeid = getNewID(dev);
2235 i->tag = swf_InsertTag(i->tag,ST_DEFINESHAPE);
2237 swf_ShapeNew(&shape);
2238 int fsid = swf_ShapeAddBitmapFillStyle(shape,&m,bitid,1);
2239 swf_SetU16(i->tag, myshapeid);
2240 SRECT r = gfxline_getSWFbbox(line);
2241 r = swf_ClipRect(i->pagebbox, r);
2242 swf_SetRect(i->tag,&r);
2243 swf_SetShapeStyles(i->tag,shape);
2244 swf_ShapeCountBits(shape,NULL,NULL);
2245 swf_SetShapeBits(i->tag,shape);
2246 swf_ShapeSetAll(i->tag,shape,UNDEFINED_COORD,UNDEFINED_COORD,0,fsid,0);
2247 i->swflastx = i->swflasty = UNDEFINED_COORD;
2248 drawgfxline(dev, line, 1);
2249 swf_ShapeSetEnd(i->tag);
2250 swf_ShapeFree(shape);
2252 msg("<trace> Placing image, shape ID %d, bitmap ID %d", myshapeid, bitid);
2253 i->tag = swf_InsertTag(i->tag,ST_PLACEOBJECT2);
2254 CXFORM cxform2 = gfxcxform_to_cxform(cxform);
2255 swf_ObjectPlace(i->tag,myshapeid,getNewDepth(dev),&i->page_matrix,&cxform2,NULL);
2258 static RGBA col_black = {255,0,0,0};
2260 static void drawoutline(gfxdevice_t*dev, gfxline_t*line)
2262 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
2264 int myshapeid = getNewID(dev);
2265 i->tag = swf_InsertTag(i->tag,ST_DEFINESHAPE3);
2268 swf_ShapeNew(&shape);
2269 int lsid = swf_ShapeAddLineStyle(shape,1,&col_black);
2271 swf_SetU16(i->tag,myshapeid);
2272 SRECT r = gfxline_getSWFbbox(line);
2273 r = swf_ClipRect(i->pagebbox, r);
2274 swf_SetRect(i->tag,&r);
2275 swf_SetShapeStyles(i->tag,shape);
2276 swf_ShapeCountBits(shape,NULL,NULL);
2277 swf_SetShapeBits(i->tag,shape);
2278 swf_ShapeSetAll(i->tag,shape,UNDEFINED_COORD,UNDEFINED_COORD,lsid,0,0);
2279 drawgfxline(dev, line, 1);
2280 swf_ShapeSetEnd(i->tag);
2281 swf_ShapeFree(shape);
2283 i->tag = swf_InsertTag(i->tag,ST_PLACEOBJECT2);
2284 swf_ObjectPlace(i->tag, myshapeid, getNewDepth(dev), 0,0,0);
2287 static void swf_startclip(gfxdevice_t*dev, gfxline_t*line)
2289 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
2294 if(i->clippos >= 127)
2296 msg("<warning> Too many clip levels.");
2300 if(i->config_showclipshapes)
2301 drawoutline(dev, line);
2303 int myshapeid = getNewID(dev);
2304 i->tag = swf_InsertTag(i->tag,ST_DEFINESHAPE3);
2306 memset(&col, 0, sizeof(RGBA));
2309 swf_ShapeNew(&shape);
2310 int fsid = swf_ShapeAddSolidFillStyle(shape,&col);
2312 RGBA markcol = {0,i->mark[0],i->mark[1],i->mark[2]};
2313 swf_ShapeAddSolidFillStyle(shape,&markcol);
2315 swf_SetU16(i->tag,myshapeid);
2316 SRECT r = gfxline_getSWFbbox(line);
2317 r = swf_ClipRect(i->pagebbox, r);
2318 swf_SetRect(i->tag,&r);
2319 swf_SetShapeStyles(i->tag,shape);
2320 swf_ShapeCountBits(shape,NULL,NULL);
2321 swf_SetShapeBits(i->tag,shape);
2322 swf_ShapeSetAll(i->tag,shape,UNDEFINED_COORD,UNDEFINED_COORD,0,fsid,0);
2323 i->swflastx = i->swflasty = UNDEFINED_COORD;
2324 i->shapeisempty = 1;
2325 drawgfxline(dev, line, 1);
2326 if(i->shapeisempty) {
2327 /* an empty clip shape is equivalent to a shape with no area */
2328 int x = line?line->x:0;
2329 int y = line?line->y:0;
2330 moveto(dev, i->tag, x,y);
2331 lineto(dev, i->tag, x,y);
2332 lineto(dev, i->tag, x,y);
2334 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)) {
2335 if(i->config_watermark) {
2336 gfxbbox_t r; r.xmin = r.ymin = 0;r.xmax = i->max_x;r.ymax = i->max_y;
2337 draw_watermark(dev, r, 1);
2340 swf_ShapeSetEnd(i->tag);
2341 swf_ShapeFree(shape);
2343 /* TODO: remember the bbox, and check all shapes against it */
2345 msg("<trace> Placing clip ID %d", myshapeid);
2346 i->tag = swf_InsertTag(i->tag,ST_PLACEOBJECT2);
2347 i->cliptags[i->clippos] = i->tag;
2348 i->clipshapes[i->clippos] = myshapeid;
2349 i->clipdepths[i->clippos] = getNewDepth(dev);
2353 static void swf_endclip(gfxdevice_t*dev)
2355 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
2362 msg("<error> Invalid end of clipping region");
2366 /*swf_ObjectPlaceClip(i->cliptags[i->clippos],i->clipshapes[i->clippos],i->clipdepths[i->clippos],&i->page_matrix,NULL,NULL,
2367 / * clip to depth: * / i->depth <= i->clipdepths[i->clippos]? i->depth : i->depth - 1);
2369 swf_ObjectPlaceClip(i->cliptags[i->clippos],i->clipshapes[i->clippos],i->clipdepths[i->clippos],&i->page_matrix,NULL,NULL,i->depth);
2371 static int gfxline_type(gfxline_t*line)
2377 int haszerosegments=0;
2380 if(line->type == gfx_moveTo) {
2383 } else if(line->type == gfx_lineTo) {
2387 } else if(line->type == gfx_splineTo) {
2389 if(tmpsplines>lines)
2397 if(lines==0 && splines==0) return 0;
2398 else if(lines==1 && splines==0) return 1;
2399 else if(lines==0 && splines==1) return 2;
2400 else if(splines==0) return 3;
2404 static int gfxline_has_dots(gfxline_t*line)
2412 if(line->type == gfx_moveTo) {
2413 /* test the length of the preceding line, and assume it is a dot if
2414 it's length is less than 1.0. But *only* if there's a noticable
2415 gap between the previous line and the next moveTo. (I've come
2416 across a PDF where thousands of "dots" were stringed together,
2418 int last_short_gap = short_gap;
2419 if((fabs(line->x - x) + fabs(line->y - y)) < 1.0) {
2424 if(isline && dist < 1 && !short_gap && !last_short_gap) {
2429 } else if(line->type == gfx_lineTo) {
2430 dist += fabs(line->x - x) + fabs(line->y - y);
2432 } else if(line->type == gfx_splineTo) {
2433 dist += fabs(line->sx - x) + fabs(line->sy - y) +
2434 fabs(line->x - line->sx) + fabs(line->y - line->sy);
2441 if(isline && dist < 1 && !short_gap) {
2447 static int gfxline_fix_short_edges(gfxline_t*line)
2451 if(line->type == gfx_lineTo) {
2452 if(fabs(line->x - x) + fabs(line->y - y) < 0.01) {
2455 } else if(line->type == gfx_splineTo) {
2456 if(fabs(line->sx - x) + fabs(line->sy - y) +
2457 fabs(line->x - line->sx) + fabs(line->y - line->sy) < 0.01) {
2468 static char is_inside_page(gfxdevice_t*dev, gfxcoord_t x, gfxcoord_t y)
2470 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
2471 if(x<i->min_x || x>i->max_x) return 0;
2472 if(y<i->min_y || y>i->max_y) return 0;
2476 gfxline_t* gfxline_move(gfxline_t*line, double x, double y)
2478 gfxline_t*l = line = gfxline_clone(line);
2490 //#define NORMALIZE_POLYGON_POSITIONS
2492 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)
2494 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
2495 if(line_is_empty(line))
2497 int type = gfxline_type(line);
2498 int has_dots = gfxline_has_dots(line);
2499 gfxbbox_t r = gfxline_getbbox(line);
2500 int is_outside_page = !is_inside_page(dev, r.xmin, r.ymin) || !is_inside_page(dev, r.xmax, r.ymax);
2502 /* TODO: * split line into segments, and perform this check for all segments */
2504 if(i->config_disable_polygon_conversion || /*type>=5 ||*/
2506 (width <= i->config_caplinewidth
2507 || (cap_style == gfx_capRound && joint_style == gfx_joinRound)
2508 || (cap_style == gfx_capRound && type<=2))))
2512 /* convert line to polygon */
2513 msg("<trace> draw as polygon, type=%d dots=%d", type, has_dots);
2515 gfxline_fix_short_edges(line);
2516 /* we need to convert the line into a polygon */
2517 gfxpoly_t* poly = gfxpoly_strokeToPoly(line, width, cap_style, joint_style, miterLimit);
2518 gfxline_t*gfxline = gfxpoly_to_gfxline(poly);
2519 dev->fill(dev, gfxline, color);
2520 gfxline_free(gfxline);
2525 msg("<trace> draw as stroke, type=%d dots=%d", type, has_dots);
2528 if(i->config_normalize_polygon_positions) {
2530 double startx = 0, starty = 0;
2531 if(line && line->type == gfx_moveTo) {
2535 line = gfxline_move(line, -startx, -starty);
2536 i->shapeposx = (int)(startx*20);
2537 i->shapeposy = (int)(starty*20);
2540 swfoutput_setstrokecolor(dev, color->r, color->g, color->b, color->a);
2541 swfoutput_setlinewidth(dev, width);
2544 drawgfxline(dev, line, 0);
2546 if(i->config_normalize_polygon_positions) {
2547 free(line); //account for _move
2552 static void swf_fill(gfxdevice_t*dev, gfxline_t*line, gfxcolor_t*color)
2554 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
2555 if(line_is_empty(line))
2559 gfxbbox_t r = gfxline_getbbox(line);
2560 int is_outside_page = !is_inside_page(dev, r.xmin, r.ymin) || !is_inside_page(dev, r.xmax, r.ymax);
2562 //if(i->chardatapos && i->chardata[i->chardatapos-1].color.a) {
2565 if(!i->config_ignoredraworder)
2568 if(i->config_normalize_polygon_positions) {
2570 double startx = 0, starty = 0;
2571 if(line && line->type == gfx_moveTo) {
2575 line = gfxline_move(line, -startx, -starty);
2576 i->shapeposx = (int)(startx*20);
2577 i->shapeposy = (int)(starty*20);
2580 swfoutput_setfillcolor(dev, color->r, color->g, color->b, color->a);
2583 drawgfxline(dev, line, 1);
2585 if(i->currentswfid==2 && r.xmin==0 && r.ymin==0 && r.xmax==i->max_x && r.ymax==i->max_y) {
2586 if(i->config_watermark) {
2587 draw_watermark(dev, r, 1);
2591 msg("<trace> end of swf_fill (shapeid=%d)", i->shapeid);
2593 if(i->config_normalize_polygon_positions) {
2594 free(line); //account for _move
2598 static GRADIENT* gfxgradient_to_GRADIENT(gfxgradient_t*gradient)
2601 gfxgradient_t*g = gradient;
2606 GRADIENT* swfgradient = malloc(sizeof(GRADIENT));
2607 swfgradient->num = num;
2608 swfgradient->rgba = malloc(sizeof(swfgradient->rgba[0])*num);
2609 swfgradient->ratios = malloc(sizeof(swfgradient->ratios[0])*num);
2614 swfgradient->ratios[num] = g->pos*255;
2615 swfgradient->rgba[num] = *(RGBA*)&g->color;
2622 static void swf_fillgradient(gfxdevice_t*dev, gfxline_t*line, gfxgradient_t*gradient, gfxgradienttype_t type, gfxmatrix_t*matrix)
2624 if(line_is_empty(line))
2626 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
2628 if(line_is_empty(line))
2631 GRADIENT* swfgradient = gfxgradient_to_GRADIENT(gradient);
2638 double f = type==gfxgradient_radial?4:4;
2640 m.sx = (int)(matrix->m00*20*f); m.r1 = (int)(matrix->m10*20*f);
2641 m.r0 = (int)(matrix->m01*20*f); m.sy = (int)(matrix->m11*20*f);
2642 m.tx = (int)(matrix->tx*20);
2643 m.ty = (int)(matrix->ty*20);
2646 int myshapeid = getNewID(dev);
2647 i->tag = swf_InsertTag(i->tag, ST_DEFINESHAPE2);
2649 swf_ShapeNew(&shape);
2650 int fsid = swf_ShapeAddGradientFillStyle(shape,&m,swfgradient,type==gfxgradient_radial);
2651 swf_SetU16(i->tag, myshapeid);
2652 SRECT r = gfxline_getSWFbbox(line);
2653 r = swf_ClipRect(i->pagebbox, r);
2654 swf_SetRect(i->tag,&r);
2655 swf_SetShapeStyles(i->tag,shape);
2656 swf_ShapeCountBits(shape,NULL,NULL);
2657 swf_SetShapeBits(i->tag,shape);
2658 swf_ShapeSetAll(i->tag,shape,UNDEFINED_COORD,UNDEFINED_COORD,0,fsid,0);
2659 i->swflastx = i->swflasty = UNDEFINED_COORD;
2660 drawgfxline(dev, line, 1);
2661 swf_ShapeSetEnd(i->tag);
2662 swf_ShapeFree(shape);
2664 int depth = getNewDepth(dev);
2665 msg("<trace> Placing gradient, shape ID %d, depth %d", myshapeid, depth);
2666 i->tag = swf_InsertTag(i->tag,ST_PLACEOBJECT2);
2667 swf_ObjectPlace(i->tag,myshapeid,depth,&i->page_matrix,NULL,NULL);
2669 swf_FreeGradient(swfgradient);free(swfgradient);
2672 static SWFFONT* gfxfont_to_swffont(gfxfont_t*font, const char* id)
2674 SWFFONT*swffont = (SWFFONT*)rfx_calloc(sizeof(SWFFONT));
2676 SRECT bounds = {0,0,0,0};
2678 swffont->version = 2;
2679 swffont->name = (U8*)strdup(id);
2680 swffont->layout = (SWFLAYOUT*)rfx_calloc(sizeof(SWFLAYOUT));
2681 swffont->layout->ascent = 0;
2682 swffont->layout->descent = 0;
2683 swffont->layout->leading = 0;
2684 swffont->layout->bounds = (SRECT*)rfx_calloc(sizeof(SRECT)*font->num_glyphs);
2685 swffont->encoding = FONT_ENCODING_UNICODE;
2686 swffont->numchars = font->num_glyphs;
2687 swffont->maxascii = font->max_unicode;
2688 swffont->ascii2glyph = (int*)rfx_calloc(sizeof(int)*swffont->maxascii);
2689 swffont->glyph2ascii = (U16*)rfx_calloc(sizeof(U16)*swffont->numchars);
2690 swffont->glyph = (SWFGLYPH*)rfx_calloc(sizeof(SWFGLYPH)*swffont->numchars);
2691 swffont->glyphnames = (char**)rfx_calloc(sizeof(char*)*swffont->numchars);
2692 for(t=0;t<font->max_unicode;t++) {
2693 swffont->ascii2glyph[t] = font->unicode2glyph[t];
2695 SRECT max = {0,0,0,0};
2696 for(t=0;t<font->num_glyphs;t++) {
2700 swffont->glyph2ascii[t] = font->glyphs[t].unicode;
2701 if(swffont->glyph2ascii[t] == 0xffff || swffont->glyph2ascii[t] == 0x0000) {
2702 /* flash 8 flashtype requires unique unicode IDs for each character.
2703 We use the Unicode private user area to assign characters, hoping that
2704 the font doesn't contain more than 2048 glyphs */
2705 swffont->glyph2ascii[t] = 0xe000 + (t&0x1fff);
2708 if(font->glyphs[t].name) {
2709 swffont->glyphnames[t] = strdup(font->glyphs[t].name);
2711 swffont->glyphnames[t] = 0;
2713 advance = font->glyphs[t].advance;
2715 swf_Shape01DrawerInit(&draw, 0);
2716 line = font->glyphs[t].line;
2719 c.x = line->sx * GLYPH_SCALE; c.y = line->sy * GLYPH_SCALE;
2720 to.x = line->x * GLYPH_SCALE; to.y = line->y * GLYPH_SCALE;
2721 if(line->type == gfx_moveTo) {
2722 draw.moveTo(&draw, &to);
2723 } else if(line->type == gfx_lineTo) {
2724 draw.lineTo(&draw, &to);
2725 } else if(line->type == gfx_splineTo) {
2726 draw.splineTo(&draw, &c, &to);
2731 swffont->glyph[t].shape = swf_ShapeDrawerToShape(&draw);
2733 SRECT bbox = swf_ShapeDrawerGetBBox(&draw);
2734 swf_ExpandRect2(&max, &bbox);
2736 swffont->layout->bounds[t] = bbox;
2738 if(advance<32768.0/20) {
2739 swffont->glyph[t].advance = (int)(advance*20);
2741 //msg("<warning> Advance value overflow in glyph %d", t);
2742 swffont->glyph[t].advance = 32767;
2745 draw.dealloc(&draw);
2747 swf_ExpandRect2(&bounds, &swffont->layout->bounds[t]);
2749 for(t=0;t<font->num_glyphs;t++) {
2750 SRECT bbox = swffont->layout->bounds[t];
2752 /* if the glyph doesn't have a bounding box, use the
2753 combined bounding box (necessary e.g. for space characters) */
2754 if(!(bbox.xmin|bbox.ymin|bbox.xmax|bbox.ymax)) {
2755 swffont->layout->bounds[t] = bbox = max;
2758 /* check that the advance value is reasonable, by comparing it
2759 with the bounding box */
2760 if(bbox.xmax>0 && (bbox.xmax*10 < swffont->glyph[t].advance || !swffont->glyph[t].advance)) {
2761 if(swffont->glyph[t].advance)
2762 msg("<warning> fix bad advance value for char %d: bbox=%.2f, advance=%.2f\n", t, bbox.xmax/20.0, swffont->glyph[t].advance/20.0);
2763 swffont->glyph[t].advance = bbox.xmax;
2765 //swffont->glyph[t].advance = bbox.xmax - bbox.xmin;
2769 /* Flash player will use the advance value from the char, and the ascent/descent values
2770 from the layout for text selection.
2771 ascent will extend the char into negative y direction, from the baseline, while descent
2772 will extend in positive y direction, also from the baseline.
2773 The baseline is defined as the y-position zero
2776 swffont->layout->ascent = -bounds.ymin;
2777 if(swffont->layout->ascent < 0)
2778 swffont->layout->ascent = 0;
2779 swffont->layout->descent = bounds.ymax;
2780 if(swffont->layout->descent < 0)
2781 swffont->layout->descent = 0;
2782 swffont->layout->leading = bounds.ymax - bounds.ymin;
2784 /* if the font has proper ascent/descent values (>0) and those define
2785 greater line spacing that what we estimated from the bounding boxes,
2786 use the font's parameters */
2787 if(font->ascent*20 > swffont->layout->ascent)
2788 swffont->layout->ascent = font->ascent*20;
2789 if(font->descent*20 > swffont->layout->descent)
2790 swffont->layout->descent = font->descent*20;
2795 static void swf_addfont(gfxdevice_t*dev, gfxfont_t*font)
2797 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
2799 if(i->swffont && i->swffont->name && !strcmp((char*)i->swffont->name,font->id))
2800 return; // the requested font is the current font
2802 fontlist_t*last=0,*l = i->fontlist;
2805 if(!strcmp((char*)l->swffont->name, font->id)) {
2806 return; // we already know this font
2810 l = (fontlist_t*)rfx_calloc(sizeof(fontlist_t));
2811 l->swffont = gfxfont_to_swffont(font, font->id);
2818 swf_FontSetID(l->swffont, getNewID(i->dev));
2820 if(getScreenLogLevel() >= LOGLEVEL_DEBUG) {
2822 // print font information
2823 msg("<debug> Font %s",font->id);
2824 msg("<debug> | ID: %d", l->swffont->id);
2825 msg("<debug> | Version: %d", l->swffont->version);
2826 msg("<debug> | Name: %s", l->swffont->name);
2827 msg("<debug> | Numchars: %d", l->swffont->numchars);
2828 msg("<debug> | Maxascii: %d", l->swffont->maxascii);
2829 msg("<debug> | Style: %d", l->swffont->style);
2830 msg("<debug> | Encoding: %d", l->swffont->encoding);
2831 for(iii=0; iii<l->swffont->numchars;iii++) {
2832 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,
2833 l->swffont->layout->bounds[iii].xmin/20.0,
2834 l->swffont->layout->bounds[iii].ymin/20.0,
2835 l->swffont->layout->bounds[iii].xmax/20.0,
2836 l->swffont->layout->bounds[iii].ymax/20.0
2839 for(t=0;t<l->swffont->maxascii;t++) {
2840 if(l->swffont->ascii2glyph[t] == iii)
2841 msg("<debug> | - maps to %d",t);
2847 static void swf_switchfont(gfxdevice_t*dev, const char*fontid)
2849 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
2851 if(i->swffont && i->swffont->name && !strcmp((char*)i->swffont->name,fontid))
2852 return; // the requested font is the current font
2854 fontlist_t*l = i->fontlist;
2856 if(!strcmp((char*)l->swffont->name, fontid)) {
2857 i->swffont = l->swffont;
2862 msg("<error> Unknown font id: %s", fontid);
2866 static void swf_drawchar(gfxdevice_t*dev, gfxfont_t*font, int glyph, gfxcolor_t*color, gfxmatrix_t*matrix)
2868 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
2870 msg("<error> swf_drawchar called (glyph %d) without font", glyph);
2874 if(i->config_drawonlyshapes) {
2875 gfxglyph_t*g = &font->glyphs[glyph];
2876 gfxline_t*line2 = gfxline_clone(g->line);
2877 gfxline_transform(line2, matrix);
2878 dev->fill(dev, line2, color);
2879 gfxline_free(line2);
2883 if(!i->swffont || !i->swffont->name || strcmp((char*)i->swffont->name,font->id)) // not equal to current font
2885 /* TODO: remove the need for this (enhance getcharacterbbox so that it can cope
2886 with multiple fonts */
2888 swf_switchfont(dev, font->id); // set the current font
2891 msg("<warning> swf_drawchar: Font is NULL");
2894 if(glyph<0 || glyph>=i->swffont->numchars) {
2895 msg("<warning> No character %d in font %s (%d chars)", glyph, FIXNULL((char*)i->swffont->name), i->swffont->numchars);
2899 setfontscale(dev, matrix->m00, matrix->m01, matrix->m10, matrix->m11, matrix->tx, matrix->ty, 0);
2901 double det = i->fontmatrix.sx/65536.0 * i->fontmatrix.sy/65536.0 -
2902 i->fontmatrix.r0/65536.0 * i->fontmatrix.r1/65536.0;
2903 if(fabs(det) < 0.0005) {
2904 /* x direction equals y direction- the text is invisible */
2905 msg("<verbose> Not drawing invisible character character %d (det=%f, m=[%f %f;%f %f]\n", glyph,
2907 i->fontmatrix.sx/65536.0, i->fontmatrix.r1/65536.0,
2908 i->fontmatrix.r0/65536.0, i->fontmatrix.sy/65536.0);
2912 /*if(i->swffont->glyph[glyph].shape->bitlen <= 16) {
2913 msg("<warning> Glyph %d in current charset (%s, %d characters) is empty",
2914 glyph, FIXNULL((char*)i->swffont->name), i->swffont->numchars);
2918 /* calculate character position with respect to the current font matrix */
2919 double s = 20 * GLYPH_SCALE / det;
2920 double px = matrix->tx - i->fontmatrix.tx/20.0;
2921 double py = matrix->ty - i->fontmatrix.ty/20.0;
2922 int x = (SCOORD)(( px * i->fontmatrix.sy/65536.0 - py * i->fontmatrix.r1/65536.0)*s);
2923 int y = (SCOORD)((- px * i->fontmatrix.r0/65536.0 + py * i->fontmatrix.sx/65536.0)*s);
2924 if(x>32767 || x<-32768 || y>32767 || y<-32768) {
2925 msg("<verbose> Moving character origin to %f %f\n", matrix->tx, matrix->ty);
2927 setfontscale(dev, matrix->m00, matrix->m01, matrix->m10, matrix->m11, matrix->tx, matrix->ty, 1);
2928 /* since we just moved the char origin to the current char's position,
2929 it now has the relative position (0,0) */
2938 msg("<trace> Drawing char %d in font %d at %d,%d in color %02x%02x%02x%02x",
2939 glyph, i->swffont->id, x, y, color->r, color->g, color->b, color->a);
2941 putcharacter(dev, i->swffont->id, glyph, x, y, i->current_font_size, *(RGBA*)color);
2942 swf_FontUseGlyph(i->swffont, glyph);