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"
43 #ifdef INTERNAL_LIBART
44 #include "../art/libart.h"
46 #include <libart_lgpl/libart.h>
49 #include "../gfxpoly.h"
52 #define CHARDATAMAX 8192
56 typedef struct _chardata {
58 int fontid; /* TODO: use a SWFFONT instead */
65 typedef struct _fontlist
68 struct _fontlist*next;
71 typedef long int twip;
73 typedef struct _swfmatrix {
74 double m11,m12,m21,m22,m31,m32;
77 typedef struct _swfoutput_internal
79 gfxdevice_t*dev; // the gfxdevice object where this internal struct resides
81 double config_dumpfonts;
82 double config_ppmsubpixels;
83 double config_jpegsubpixels;
84 int config_simpleviewer;
85 int config_opennewwindow;
86 int config_ignoredraworder;
87 int config_drawonlyshapes;
88 int config_frameresets;
89 int config_linknameurl;
90 int config_jpegquality;
91 int config_storeallcharacters;
92 int config_enablezlib;
93 int config_insertstoptag;
95 int config_flashversion;
96 int config_reordertags;
97 int config_showclipshapes;
98 int config_splinemaxerror;
99 int config_fontsplinemaxerror;
100 int config_filloverlap;
103 int config_disable_polygon_conversion;
104 int config_normalize_polygon_positions;
105 RGBA config_linkcolor;
106 float config_minlinewidth;
107 double config_caplinewidth;
108 char* config_linktarget;
109 char*config_internallinkfunction;
110 char*config_externallinkfunction;
112 double config_framerate;
116 fontlist_t* fontlist;
155 int pic_height[1024];
162 char fillstylechanged;
164 int jpeg; //next image type
171 chardata_t chardata[CHARDATAMAX];
178 int current_font_size;
180 double lastfontm11,lastfontm12,lastfontm21,lastfontm22;
191 } swfoutput_internal;
193 static void swf_fillbitmap(gfxdevice_t*driver, gfxline_t*line, gfximage_t*img, gfxmatrix_t*move, gfxcxform_t*cxform);
194 static int swf_setparameter(gfxdevice_t*driver, const char*key, const char*value);
195 static void swf_drawstroke(gfxdevice_t*dev, gfxline_t*line, gfxcoord_t width, gfxcolor_t*color, gfx_capType cap_style, gfx_joinType joint_style, gfxcoord_t miterLimit);
196 static void swf_startclip(gfxdevice_t*dev, gfxline_t*line);
197 static void swf_endclip(gfxdevice_t*dev);
198 static void swf_stroke(gfxdevice_t*dev, gfxline_t*line, gfxcoord_t width, gfxcolor_t*color, gfx_capType cap_style, gfx_joinType joint_style, gfxcoord_t miterLimit);
199 static void swf_fill(gfxdevice_t*dev, gfxline_t*line, gfxcolor_t*color);
200 static void swf_fillbitmap(gfxdevice_t*dev, gfxline_t*line, gfximage_t*img, gfxmatrix_t*matrix, gfxcxform_t*cxform);
201 static void swf_fillgradient(gfxdevice_t*dev, gfxline_t*line, gfxgradient_t*gradient, gfxgradienttype_t type, gfxmatrix_t*matrix);
202 static void swf_drawchar(gfxdevice_t*dev, gfxfont_t*font, int glyph, gfxcolor_t*color, gfxmatrix_t*matrix);
203 static void swf_addfont(gfxdevice_t*dev, gfxfont_t*font);
204 static void swf_drawlink(gfxdevice_t*dev, gfxline_t*line, const char*action);
205 static void swf_startframe(gfxdevice_t*dev, int width, int height);
206 static void swf_endframe(gfxdevice_t*dev);
207 static gfxresult_t* swf_finish(gfxdevice_t*driver);
209 static swfoutput_internal* init_internal_struct()
211 swfoutput_internal*i = (swfoutput_internal*)malloc(sizeof(swfoutput_internal));
212 memset(i, 0, sizeof(swfoutput_internal));
236 i->fillstylechanged = 0;
243 i->config_dumpfonts=0;
244 i->config_ppmsubpixels=0;
245 i->config_jpegsubpixels=0;
246 i->config_opennewwindow=1;
247 i->config_ignoredraworder=0;
248 i->config_drawonlyshapes=0;
249 i->config_jpegquality=85;
250 i->config_storeallcharacters=0;
251 i->config_enablezlib=0;
252 i->config_insertstoptag=0;
253 i->config_flashversion=6;
254 i->config_framerate=0.25;
255 i->config_splinemaxerror=1;
256 i->config_fontsplinemaxerror=1;
257 i->config_filloverlap=0;
259 i->config_bboxvars=0;
260 i->config_showclipshapes=0;
261 i->config_minlinewidth=0.05;
262 i->config_caplinewidth=1;
263 i->config_linktarget=0;
264 i->config_internallinkfunction=0;
265 i->config_externallinkfunction=0;
266 i->config_reordertags=1;
267 i->config_linknameurl=1;
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 }/* else if(!i->fill) {
392 // treat lines of length 0 as plots, making them
393 // at least 1 twip wide so Flash will display them
394 plot(dev, i->swflastx, i->swflasty, tag);
401 static void lineto(gfxdevice_t*dev, TAG*tag, double x, double y)
406 linetoxy(dev,tag, p);
409 // write a spline-to command into the swf
410 static void splineto(gfxdevice_t*dev, TAG*tag, plotxy_t control,plotxy_t end)
412 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
413 int lastlastx = i->swflastx;
414 int lastlasty = i->swflasty;
416 int cx = ((int)(control.x*20)-i->swflastx);
417 int cy = ((int)(control.y*20)-i->swflasty);
420 int ex = ((int)(end.x*20)-i->swflastx);
421 int ey = ((int)(end.y*20)-i->swflasty);
425 if((cx || cy) && (ex || ey)) {
426 swf_ShapeSetCurve(tag, i->shape, cx,cy,ex,ey);
427 addPointToBBox(dev, lastlastx ,lastlasty );
428 addPointToBBox(dev, lastlastx+cx,lastlasty+cy);
429 addPointToBBox(dev, lastlastx+cx+ex,lastlasty+cy+ey);
430 } else if(cx || cy || ex || ey) {
431 swf_ShapeSetLine(tag, i->shape, cx+ex,cy+ey);
432 addPointToBBox(dev, lastlastx ,lastlasty );
433 addPointToBBox(dev, lastlastx+cx,lastlasty+cy);
434 addPointToBBox(dev, lastlastx+cx+ex,lastlasty+cy+ey);
440 /* write a line, given two points and the transformation
442 /*static void line(gfxdevice_t*dev, TAG*tag, plotxy_t p0, plotxy_t p1)
444 moveto(dev, tag, p0);
445 lineto(dev, tag, p1);
448 void resetdrawer(gfxdevice_t*dev)
450 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
455 static void stopFill(gfxdevice_t*dev)
457 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
458 if(i->lastwasfill!=0)
460 swf_ShapeSetStyle(i->tag,i->shape,i->linestyleid,0x8000,0);
461 i->fillstylechanged = 1;
465 static void startFill(gfxdevice_t*dev)
467 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
468 if(i->lastwasfill!=1)
470 swf_ShapeSetStyle(i->tag,i->shape,0x8000,i->fillstyleid,0);
471 i->fillstylechanged = 1;
476 static inline int colorcompare(gfxdevice_t*dev, RGBA*a,RGBA*b)
488 static SRECT getcharacterbbox(gfxdevice_t*dev, SWFFONT*font, MATRIX* m)
490 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
493 memset(&r, 0, sizeof(r));
496 if(debug) printf("\n");
497 for(t=0;t<i->chardatapos;t++)
499 if(i->chardata[t].fontid != font->id) {
500 msg("<error> Internal error: fontid %d != fontid %d", i->chardata[t].fontid, font->id);
503 SRECT b = font->layout->bounds[i->chardata[t].charid];
504 b.xmin *= i->chardata[t].size;
505 b.ymin *= i->chardata[t].size;
506 b.xmax *= i->chardata[t].size;
507 b.ymax *= i->chardata[t].size;
509 /* divide by 1024, rounding xmax/ymax up */
510 b.xmax += 1023; b.ymax += 1023; b.xmin /= 1024; b.ymin /= 1024; b.xmax /= 1024; b.ymax /= 1024;
512 b.xmin += i->chardata[t].x;
513 b.ymin += i->chardata[t].y;
514 b.xmax += i->chardata[t].x;
515 b.ymax += i->chardata[t].y;
517 /* until we solve the INTERNAL_SCALING problem (see below)
518 make sure the bounding box is big enough */
524 b = swf_TurnRect(b, m);
526 if(debug) printf("(%f,%f,%f,%f) -> (%f,%f,%f,%f) [font %d/%d, char %d]\n",
527 font->layout->bounds[i->chardata[t].charid].xmin/20.0,
528 font->layout->bounds[i->chardata[t].charid].ymin/20.0,
529 font->layout->bounds[i->chardata[t].charid].xmax/20.0,
530 font->layout->bounds[i->chardata[t].charid].ymax/20.0,
535 i->chardata[t].fontid,
537 i->chardata[t].charid
539 swf_ExpandRect2(&r, &b);
541 if(debug) printf("-----> (%f,%f,%f,%f)\n",
549 static void putcharacters(gfxdevice_t*dev, TAG*tag)
551 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
555 color.r = i->chardata[0].color.r^255;
564 int charadvance[128];
567 int glyphbits=1; //TODO: can this be zero?
570 if(tag->id != ST_DEFINETEXT &&
571 tag->id != ST_DEFINETEXT2) {
572 msg("<error> internal error: putcharacters needs an text tag, not %d\n",tag->id);
575 if(!i->chardatapos) {
576 msg("<warning> putcharacters called with zero characters");
579 for(pass = 0; pass < 2; pass++)
589 advancebits++; // add sign bit
590 swf_SetU8(tag, glyphbits);
591 swf_SetU8(tag, advancebits);
594 for(t=0;t<=i->chardatapos;t++)
596 if(lastfontid != i->chardata[t].fontid ||
597 lastx!=i->chardata[t].x ||
598 lasty!=i->chardata[t].y ||
599 !colorcompare(dev,&color, &i->chardata[t].color) ||
601 lastsize != i->chardata[t].size ||
604 if(charstorepos && pass==0)
607 for(s=0;s<charstorepos;s++)
609 while(charids[s]>=(1<<glyphbits))
611 while(charadvance[s]>=(1<<advancebits))
615 if(charstorepos && pass==1)
617 tag->writeBit = 0; // Q&D
618 swf_SetBits(tag, 0, 1); // GLYPH Record
619 swf_SetBits(tag, charstorepos, 7); // number of glyphs
621 for(s=0;s<charstorepos;s++)
623 swf_SetBits(tag, charids[s], glyphbits);
624 swf_SetBits(tag, charadvance[s], advancebits);
629 if(pass == 1 && t<i->chardatapos)
635 if(lastx != i->chardata[t].x ||
636 lasty != i->chardata[t].y)
638 newx = i->chardata[t].x;
639 newy = i->chardata[t].y;
645 if(!colorcompare(dev,&color, &i->chardata[t].color))
647 color = i->chardata[t].color;
650 font.id = i->chardata[t].fontid;
651 if(lastfontid != i->chardata[t].fontid || lastsize != i->chardata[t].size)
654 tag->writeBit = 0; // Q&D
655 swf_TextSetInfoRecord(tag, newfont, i->chardata[t].size, newcolor, newx,newy);
658 lastfontid = i->chardata[t].fontid;
659 lastx = i->chardata[t].x;
660 lasty = i->chardata[t].y;
661 lastsize = i->chardata[t].size;
664 if(t==i->chardatapos)
668 int nextt = t==i->chardatapos-1?t:t+1;
669 int rel = i->chardata[nextt].x-i->chardata[t].x;
670 if(rel>=0 && (rel<(1<<(advancebits-1)) || pass==0)) {
672 lastx=i->chardata[nextt].x;
676 lastx=i->chardata[t].x;
678 charids[charstorepos] = i->chardata[t].charid;
679 charadvance[charstorepos] = advance;
686 static void putcharacter(gfxdevice_t*dev, int fontid, int charid, int x,int y, int size, RGBA color)
688 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
689 if(i->chardatapos == CHARDATAMAX)
691 msg("<warning> Character buffer too small. SWF will be slightly bigger");
695 i->chardata[i->chardatapos].fontid = fontid;
696 i->chardata[i->chardatapos].charid = charid;
697 i->chardata[i->chardatapos].x = x;
698 i->chardata[i->chardatapos].y = y;
699 i->chardata[i->chardatapos].color = color;
700 i->chardata[i->chardatapos].size = size;
704 /* Notice: we can only put chars in the range -1639,1638 (-32768/20,32768/20).
705 So if we set this value to high, the char coordinates will overflow.
706 If we set it to low, however, the char positions will be inaccurate */
707 #define GLYPH_SCALE 1
709 static void endtext(gfxdevice_t*dev)
711 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
715 i->tag = swf_InsertTag(i->tag,ST_DEFINETEXT2);
716 swf_SetU16(i->tag, i->textid);
719 r = getcharacterbbox(dev, i->swffont, &i->fontmatrix);
720 r = swf_ClipRect(i->pagebbox, r);
721 swf_SetRect(i->tag,&r);
723 swf_SetMatrix(i->tag,&i->fontmatrix);
725 msg("<trace> Placing text (%d characters) as ID %d", i->chardatapos, i->textid);
727 putcharacters(dev, i->tag);
730 if(i->swf->fileVersion >= 8) {
731 i->tag = swf_InsertTag(i->tag, ST_CSMTEXTSETTINGS);
732 swf_SetU16(i->tag, i->textid);
734 //swf_SetU8(i->tag, /*subpixel grid*/(2<<3)|/*flashtype*/0x40);
735 swf_SetU8(i->tag, /*grid*/(1<<3)|/*flashtype*/0x40);
736 //swf_SetU8(i->tag, /*no grid*/(0<<3)|/*flashtype*/0x40);
738 swf_SetU32(i->tag, 0);//thickness
739 swf_SetU32(i->tag, 0);//sharpness
740 swf_SetU8(i->tag, 0);//reserved
742 i->tag = swf_InsertTag(i->tag,ST_PLACEOBJECT2);
744 swf_ObjectPlace(i->tag,i->textid,getNewDepth(dev),&i->page_matrix,NULL,NULL);
748 /* set's the matrix which is to be applied to characters drawn by swfoutput_drawchar() */
749 static void setfontscale(gfxdevice_t*dev,double m11,double m12, double m21,double m22,double x, double y, char force)
755 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
756 if(i->lastfontm11 == m11 &&
757 i->lastfontm12 == m12 &&
758 i->lastfontm21 == m21 &&
759 i->lastfontm22 == m22 && !force)
764 i->lastfontm11 = m11;
765 i->lastfontm12 = m12;
766 i->lastfontm21 = m21;
767 i->lastfontm22 = m22;
769 double xsize = sqrt(m11*m11 + m12*m12);
770 double ysize = sqrt(m21*m21 + m22*m22);
771 i->current_font_size = (xsize>ysize?xsize:ysize)*1;
772 if(i->current_font_size < 1)
773 i->current_font_size = 1;
774 double ifs = 1.0 / (i->current_font_size*GLYPH_SCALE);
777 m.sx = (S32)((m11*ifs)*65536); m.r1 = (S32)((m21*ifs)*65536);
778 m.r0 = (S32)((m12*ifs)*65536); m.sy = (S32)((m22*ifs)*65536);
779 /* this is the position of the first char to set a new fontmatrix-
780 we hope that it's close enough to all other characters using the
781 font, so we use its position as origin for the matrix */
787 static int watermark2_width=47;
788 static int watermark2_height=11;
789 static int watermark2[47] = {95,1989,71,0,2015,337,1678,0,2015,5,1921,320,1938,25,2006,1024,
790 1042,21,13,960,1039,976,8,2000,1359,1088,31,1989,321,1728,0,1152,
791 1344,832,0,1984,0,896,1088,1088,896,0,1984,128,256,512,1984};
793 static void draw_watermark(gfxdevice_t*dev, gfxbbox_t r, char drawall)
795 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
796 double wx = r.xmax / 5.0;
797 double tx = r.xmax*4.0 / 5.0;
798 double ty = r.ymax-wx*watermark2_height/watermark2_width;
799 double sx = (r.xmax - tx) / watermark2_width;
800 double sy = (r.ymax - ty) / watermark2_height;
803 if(ty > 0 && px > 1.0 && py > 1.0) {
805 for(y=0;y<watermark2_height;y++)
806 for(x=0;x<watermark2_width;x++) {
807 if(((watermark2[x]>>y)&1)) {
808 if(!drawall && rand()%5)
810 unsigned int b = rand();
811 moveto(dev, i->tag, x*sx+tx+((b>>1)&1)/20.0, y*sy+ty+((b>>3)&1)/20.0);
812 lineto(dev, i->tag, x*sx+px+tx+((b>>2)&1)/20.0, y*sy+ty+((b>>3)&1)/20.0);
813 lineto(dev, i->tag, x*sx+px+tx+((b>>2)&1)/20.0, y*sy+py+ty+((b>>4)&1)/20.0);
814 lineto(dev, i->tag, x*sx+tx+((b>>1)&1)/20.0, y*sy+py+ty+((b>>4)&1)/20.0);
815 lineto(dev, i->tag, x*sx+tx+((b>>1)&1)/20.0, y*sy+ty+((b>>3)&1)/20.0);
821 static void swfoutput_setfillcolor(gfxdevice_t* dev, U8 r, U8 g, U8 b, U8 a)
823 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
824 if(i->fillrgb.r == r &&
827 i->fillrgb.a == a) return;
836 static void insert_watermark(gfxdevice_t*dev, char drawall)
838 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
839 if(!drawall && i->watermarks>20)
845 swfoutput_setfillcolor(dev, 0,0,255,192);
847 swfoutput_setfillcolor(dev, rand(),rand(),rand(),(rand()&127)+128);
852 gfxbbox_t r; r.xmin = r.ymin = 0;
855 draw_watermark(dev, r, drawall);
861 static void endpage(gfxdevice_t*dev)
863 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
874 if(i->config_watermark) {
875 insert_watermark(dev, 1);
881 static void addViewer(gfxdevice_t* dev)
883 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
886 RGBA button_colors[3]= {{0xbf,0x00,0x00,0x80},{0xbf,0x20,0x20,0xc0}, {0xbf,0xc0,0xc0,0xff}};
888 int button_sizex = 20;
889 int button_sizey = 20;
891 RGBA black = {255,0,0,0};
893 i->tag = swf_InsertTag(i->tag,ST_DEFINESHAPE3);
895 int ls1 = swf_ShapeAddLineStyle(s,40,&black);
896 int fs1 = swf_ShapeAddSolidFillStyle(s,&button_colors[t/2]);
897 int shapeid = ids[t] = getNewID(dev);
898 swf_SetU16(i->tag,shapeid);
900 r.xmin = -20*button_sizex;
901 r.xmax = 20*button_sizex;
903 r.ymax = 40*button_sizey;
904 swf_SetRect(i->tag,&r); // set shape bounds
905 swf_SetShapeHeader(i->tag,s); // write all styles to tag
906 swf_ShapeSetAll(i->tag,s,0*button_sizex,0,ls1,fs1,0);
907 swf_ShapeSetLine(i->tag,s,(1-(t&1)*2)*20*button_sizex,20*button_sizey);
908 swf_ShapeSetLine(i->tag,s,-(1-(t&1)*2)*20*button_sizex,20*button_sizey);
909 swf_ShapeSetLine(i->tag,s,0,-40*button_sizey);
910 swf_ShapeSetEnd(i->tag); // finish drawing
911 swf_ShapeFree(s); // clean shape structure (which isn't needed anymore after writing the tag)
913 ActionTAG*a1=0,*a2=0,*a3=0;
914 a1 = action_NextFrame(a1);
915 a1 = action_Stop(a1);
918 a2 = action_PreviousFrame(a2);
919 a2 = action_Stop(a2);
922 a3 = action_Stop(a3);
925 i->tag = swf_InsertTag(i->tag, ST_DOACTION);
926 swf_ActionSet(i->tag,a3);
928 i->tag = swf_InsertTag(i->tag,ST_DEFINEBUTTON);
929 int buttonid1 = getNewID(dev);
930 swf_SetU16(i->tag, buttonid1);
931 swf_ButtonSetRecord(i->tag,BS_UP|BS_HIT,ids[0],1,NULL,NULL);
932 swf_ButtonSetRecord(i->tag,BS_OVER,ids[2],1,NULL,NULL);
933 swf_ButtonSetRecord(i->tag,BS_DOWN,ids[4],1,NULL,NULL);
934 swf_SetU8(i->tag,0); // end of button records
935 swf_ActionSet(i->tag,a1);
937 i->tag = swf_InsertTag(i->tag,ST_DEFINEBUTTON);
938 int buttonid2 = getNewID(dev);
939 swf_SetU16(i->tag, buttonid2);
940 swf_ButtonSetRecord(i->tag,BS_UP|BS_HIT,ids[1],1,NULL,NULL);
941 swf_ButtonSetRecord(i->tag,BS_OVER,ids[3],1,NULL,NULL);
942 swf_ButtonSetRecord(i->tag,BS_DOWN,ids[5],1,NULL,NULL);
943 swf_SetU8(i->tag,0); // end of button records
944 swf_ActionSet(i->tag,a2);
946 i->tag = swf_InsertTag(i->tag,ST_PLACEOBJECT2);
948 swf_GetMatrix(0, &m);
949 m.tx = button_sizex*20+200;
950 swf_ObjectPlace(i->tag, buttonid2, 65534,&m,0,0);
951 i->tag = swf_InsertTag(i->tag,ST_PLACEOBJECT2);
952 m.tx = button_sizex*20+200+200;
953 swf_ObjectPlace(i->tag, buttonid1, 65535,&m,0,0);
957 void swf_startframe(gfxdevice_t*dev, int width, int height)
959 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
961 if(i->config_protect) {
962 i->tag = swf_InsertTag(i->tag, ST_PROTECT);
963 i->config_protect = 0;
965 if(i->config_simpleviewer) {
970 if(!i->firstpage && !i->pagefinished)
973 msg("<verbose> Starting new SWF page of size %dx%d", width, height);
975 swf_GetMatrix(0, &i->page_matrix);
976 i->page_matrix.tx = 0;
977 i->page_matrix.ty = 0;
984 /* create a bbox structure with the page size. This is used
985 for clipping shape and text bounding boxes. As we don't want to
986 generate bounding boxes which extend beyond the movie size (in
987 order to not confuse Flash), we clip everything against i->pagebbox */
988 i->pagebbox.xmin = 0;
989 i->pagebbox.ymin = 0;
990 i->pagebbox.xmax = width*20;
991 i->pagebbox.ymax = height*20;
993 /* increase SWF's bounding box */
994 swf_ExpandRect2(&i->swf->movieSize, &i->pagebbox);
996 i->lastframeno = i->frameno;
1001 void swf_endframe(gfxdevice_t*dev)
1003 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1005 if(!i->pagefinished)
1008 if( (i->swf->fileVersion <= 8) && (i->config_insertstoptag) ) {
1010 atag = action_Stop(atag);
1011 atag = action_End(atag);
1012 i->tag = swf_InsertTag(i->tag,ST_DOACTION);
1013 swf_ActionSet(i->tag,atag);
1015 i->tag = swf_InsertTag(i->tag,ST_SHOWFRAME);
1018 for(i->depth;i->depth>i->startdepth;i->depth--) {
1019 i->tag = swf_InsertTag(i->tag,ST_REMOVEOBJECT2);
1020 swf_SetU16(i->tag,i->depth);
1022 i->depth = i->startdepth;
1024 if(i->config_frameresets) {
1025 for(i->currentswfid;i->currentswfid>i->startids;i->currentswfid--) {
1026 i->tag = swf_InsertTag(i->tag,ST_FREECHARACTER);
1027 swf_SetU16(i->tag,i->currentswfid);
1029 i->currentswfid = i->startids;
1033 static void setBackground(gfxdevice_t*dev, int x1, int y1, int x2, int y2)
1035 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1037 rgb.a = rgb.r = rgb.g = rgb.b = 0xff;
1041 int shapeid = getNewID(dev);
1046 i->tag = swf_InsertTag(i->tag, ST_DEFINESHAPE);
1048 fs1 = swf_ShapeAddSolidFillStyle(s, &rgb);
1049 swf_SetU16(i->tag,shapeid);
1050 swf_SetRect(i->tag,&r);
1051 swf_SetShapeHeader(i->tag,s);
1052 swf_ShapeSetAll(i->tag,s,x1,y1,ls1,fs1,0);
1053 swf_ShapeSetLine(i->tag,s,(x2-x1),0);
1054 swf_ShapeSetLine(i->tag,s,0,(y2-y1));
1055 swf_ShapeSetLine(i->tag,s,(x1-x2),0);
1056 swf_ShapeSetLine(i->tag,s,0,(y1-y2));
1057 swf_ShapeSetEnd(i->tag);
1059 i->tag = swf_InsertTag(i->tag, ST_PLACEOBJECT2);
1060 swf_ObjectPlace(i->tag,shapeid,getNewDepth(dev),0,0,0);
1061 i->tag = swf_InsertTag(i->tag, ST_PLACEOBJECT2);
1062 swf_ObjectPlaceClip(i->tag,shapeid,getNewDepth(dev),0,0,0,65535);
1065 /* initialize the swf writer */
1066 void gfxdevice_swf_init(gfxdevice_t* dev)
1068 memset(dev, 0, sizeof(gfxdevice_t));
1072 dev->internal = init_internal_struct(); // set config to default values
1074 dev->startpage = swf_startframe;
1075 dev->endpage = swf_endframe;
1076 dev->finish = swf_finish;
1077 dev->fillbitmap = swf_fillbitmap;
1078 dev->setparameter = swf_setparameter;
1079 dev->stroke = swf_stroke;
1080 dev->startclip = swf_startclip;
1081 dev->endclip = swf_endclip;
1082 dev->fill = swf_fill;
1083 dev->fillbitmap = swf_fillbitmap;
1084 dev->fillgradient = swf_fillgradient;
1085 dev->addfont = swf_addfont;
1086 dev->drawchar = swf_drawchar;
1087 dev->drawlink = swf_drawlink;
1089 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1092 msg("<verbose> initializing swf output\n", i->max_x,i->max_y);
1096 i->swf = (SWF*)rfx_calloc(sizeof(SWF));
1097 i->swf->fileVersion = 0;
1098 i->swf->frameRate = 0x80;
1099 i->swf->movieSize.xmin = 0;
1100 i->swf->movieSize.ymin = 0;
1101 i->swf->movieSize.xmax = 0;
1102 i->swf->movieSize.ymax = 0;
1104 i->swf->firstTag = swf_InsertTag(NULL,ST_SETBACKGROUNDCOLOR);
1105 i->tag = i->swf->firstTag;
1107 rgb.a = rgb.r = rgb.g = rgb.b = 0xff;
1109 swf_SetRGB(i->tag,&rgb);
1111 i->startdepth = i->depth = 0;
1112 i->startids = i->currentswfid = 0;
1115 static void startshape(gfxdevice_t*dev)
1117 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1122 //if(i->chardatapos && i->chardata[i->chardatapos-1].color.a)
1125 i->tag = swf_InsertTag(i->tag,ST_DEFINESHAPE3);
1127 swf_ShapeNew(&i->shape);
1128 i->linestyleid = swf_ShapeAddLineStyle(i->shape,i->linewidth,&i->strokergb);
1129 i->fillstyleid = swf_ShapeAddSolidFillStyle(i->shape,&i->fillrgb);
1131 RGBA markcol = {0,i->mark[0],i->mark[1],i->mark[2]};
1132 swf_ShapeAddSolidFillStyle(i->shape,&markcol);
1135 i->shapeid = getNewID(dev);
1137 msg("<debug> Using shape id %d", i->shapeid);
1139 swf_SetU16(i->tag,i->shapeid); // ID
1141 i->bboxrectpos = i->tag->len;
1143 swf_SetRect(i->tag,&i->pagebbox);
1145 memset(&i->bboxrect, 0, sizeof(i->bboxrect));
1147 swf_SetShapeStyles(i->tag,i->shape);
1148 swf_ShapeCountBits(i->shape,NULL,NULL);
1149 swf_SetShapeBits(i->tag,i->shape);
1151 /* TODO: do we really need this? */
1152 //swf_ShapeSetAll(i->tag,i->shape,/*x*/0,/*y*/0,i->linestyleid,0,0);
1153 //swf_ShapeSetAll(i->tag,i->shape,/*x*/UNDEFINED_COORD,/*y*/UNDEFINED_COORD,i->linestyleid,0,0);
1154 i->swflastx=i->swflasty=UNDEFINED_COORD;
1155 i->lastwasfill = -1;
1156 i->shapeisempty = 1;
1159 static void starttext(gfxdevice_t*dev)
1161 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1165 if(i->config_watermark) {
1166 insert_watermark(dev, 0);
1168 i->textid = getNewID(dev);
1169 i->swflastx=i->swflasty=0;
1173 /* TODO: move to ../lib/rfxswf */
1174 void changeRect(gfxdevice_t*dev, TAG*tag, int pos, SRECT*newrect)
1176 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1177 /* determine length of old rect */
1181 swf_GetRect(tag, &old);
1182 swf_ResetReadBits(tag);
1183 int pos_end = tag->pos;
1185 int len = tag->len - pos_end;
1186 U8*data = (U8*)malloc(len);
1187 memcpy(data, &tag->data[pos_end], len);
1190 swf_SetRect(tag, newrect);
1191 swf_SetBlock(tag, data, len);
1193 tag->pos = tag->readBit = 0;
1196 void cancelshape(gfxdevice_t*dev)
1198 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1199 /* delete old shape tag */
1201 i->tag = i->tag->prev;
1202 swf_DeleteTag(todel);
1203 if(i->shape) {swf_ShapeFree(i->shape);i->shape=0;}
1205 i->bboxrectpos = -1;
1207 // i->currentswfid--; // doesn't work, for some reason
1210 void fixAreas(gfxdevice_t*dev)
1212 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1213 if(!i->shapeisempty && i->fill &&
1214 (i->bboxrect.xmin == i->bboxrect.xmax ||
1215 i->bboxrect.ymin == i->bboxrect.ymax) &&
1216 i->config_minlinewidth >= 0.001
1218 msg("<debug> Shape has size 0: width=%.2f height=%.2f",
1219 (i->bboxrect.xmax-i->bboxrect.xmin)/20.0,
1220 (i->bboxrect.ymax-i->bboxrect.ymin)/20.0
1223 SRECT r = i->bboxrect;
1225 if(r.xmin == r.xmax && r.ymin == r.ymax) {
1226 /* this thing comes down to a single dot- nothing to fix here */
1232 RGBA save_col = i->strokergb;
1233 int save_width = i->linewidth;
1235 i->strokergb = i->fillrgb;
1236 i->linewidth = (int)(i->config_minlinewidth*20);
1237 if(i->linewidth==0) i->linewidth = 1;
1242 moveto(dev, i->tag, r.xmin/20.0,r.ymin/20.0);
1243 lineto(dev, i->tag, r.xmax/20.0,r.ymax/20.0);
1245 i->strokergb = save_col;
1246 i->linewidth = save_width;
1251 static void endshape_noput(gfxdevice_t*dev)
1253 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1256 //changeRect(dev, i->tag, i->bboxrectpos, &i->bboxrect);
1259 swf_ShapeFree(i->shape);
1267 static void endshape(gfxdevice_t*dev)
1269 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1275 if(i->shapeisempty ||
1277 (i->bboxrect.xmin == i->bboxrect.xmax &&
1278 i->bboxrect.ymin == i->bboxrect.ymax))
1280 // delete the shape again, we didn't do anything
1281 msg("<debug> cancelling shape: bbox is (%f,%f,%f,%f)",
1282 i->bboxrect.xmin /20.0,
1283 i->bboxrect.ymin /20.0,
1284 i->bboxrect.xmax /20.0,
1285 i->bboxrect.ymax /20.0
1291 swf_ShapeSetEnd(i->tag);
1293 SRECT r = swf_ClipRect(i->pagebbox, i->bboxrect);
1294 changeRect(dev, i->tag, i->bboxrectpos, &r);
1296 msg("<trace> Placing shape ID %d", i->shapeid);
1298 i->tag = swf_InsertTag(i->tag,ST_PLACEOBJECT2);
1299 MATRIX m = i->page_matrix;
1300 m.tx += i->shapeposx;
1301 m.ty += i->shapeposy;
1302 swf_ObjectPlace(i->tag,i->shapeid,getNewDepth(dev),&m,NULL,NULL);
1304 if(i->config_animate) {
1305 i->tag = swf_InsertTag(i->tag,ST_SHOWFRAME);
1308 swf_ShapeFree(i->shape);
1311 i->bboxrectpos = -1;
1318 void wipeSWF(SWF*swf)
1320 TAG*tag = swf->firstTag;
1322 TAG*next = tag->next;
1323 if(tag->id != ST_SETBACKGROUNDCOLOR &&
1324 tag->id != ST_END &&
1325 tag->id != ST_DOACTION &&
1326 tag->id != ST_SHOWFRAME) {
1333 void swfoutput_finalize(gfxdevice_t*dev)
1335 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1337 if(i->tag && i->tag->id == ST_END)
1338 return; //already done
1340 i->swf->fileVersion = i->config_flashversion;
1341 i->swf->frameRate = i->config_framerate*0x100;
1343 if(i->config_bboxvars) {
1344 TAG* tag = swf_InsertTag(i->swf->firstTag, ST_DOACTION);
1346 a = action_PushString(a, "xmin");
1347 a = action_PushFloat(a, i->swf->movieSize.xmin / 20.0);
1348 a = action_SetVariable(a);
1349 a = action_PushString(a, "ymin");
1350 a = action_PushFloat(a, i->swf->movieSize.ymin / 20.0);
1351 a = action_SetVariable(a);
1352 a = action_PushString(a, "xmax");
1353 a = action_PushFloat(a, i->swf->movieSize.xmax / 20.0);
1354 a = action_SetVariable(a);
1355 a = action_PushString(a, "ymax");
1356 a = action_PushFloat(a, i->swf->movieSize.ymax / 20.0);
1357 a = action_SetVariable(a);
1358 a = action_PushString(a, "width");
1359 a = action_PushFloat(a, (i->swf->movieSize.xmax - i->swf->movieSize.xmin) / 20.0);
1360 a = action_SetVariable(a);
1361 a = action_PushString(a, "height");
1362 a = action_PushFloat(a, (i->swf->movieSize.ymax - i->swf->movieSize.ymin) / 20.0);
1363 a = action_SetVariable(a);
1365 swf_ActionSet(tag, a);
1370 free(i->mark);i->mark = 0;
1374 fontlist_t *iterator = i->fontlist;
1376 TAG*mtag = i->swf->firstTag;
1377 if(iterator->swffont) {
1378 if(!i->config_storeallcharacters) {
1379 msg("<debug> Reducing font %s", iterator->swffont->name);
1380 swf_FontReduce(iterator->swffont);
1382 int used = iterator->swffont->use && iterator->swffont->use->used_glyphs;
1384 mtag = swf_InsertTag(mtag, ST_DEFINEFONT2);
1385 swf_FontSetDefine2(mtag, iterator->swffont);
1389 iterator = iterator->next;
1392 i->tag = swf_InsertTag(i->tag,ST_END);
1393 TAG* tag = i->tag->prev;
1395 /* remove the removeobject2 tags between the last ST_SHOWFRAME
1396 and the ST_END- they confuse the flash player */
1397 while(tag->id == ST_REMOVEOBJECT2) {
1398 TAG* prev = tag->prev;
1406 if(i->config_enablezlib || i->config_flashversion>=6) {
1407 i->swf->compressed = 1;
1410 /* Initialize AVM2 if it is a Flash9 file */
1411 if(i->config_flashversion>=9 && i->config_insertstoptag) {
1412 AVM2_InsertStops(i->swf);
1414 // if(i->config_reordertags)
1415 // swf_Optimize(i->swf);
1418 int swfresult_save(gfxresult_t*gfx, const char*filename)
1420 SWF*swf = (SWF*)gfx->internal;
1423 fi = open(filename, O_BINARY|O_CREAT|O_TRUNC|O_WRONLY, 0777);
1428 msg("<fatal> Could not create \"%s\". ", FIXNULL(filename));
1432 if FAILED(swf_WriteSWF(fi,swf))
1433 msg("<error> WriteSWF() failed.\n");
1439 void* swfresult_get(gfxresult_t*gfx, const char*name)
1441 SWF*swf = (SWF*)gfx->internal;
1442 if(!strcmp(name, "swf")) {
1443 return (void*)swf_CopySWF(swf);
1444 } else if(!strcmp(name, "xmin")) {
1445 return (void*)(swf->movieSize.xmin/20);
1446 } else if(!strcmp(name, "ymin")) {
1447 return (void*)(swf->movieSize.ymin/20);
1448 } else if(!strcmp(name, "xmax")) {
1449 return (void*)(swf->movieSize.xmax/20);
1450 } else if(!strcmp(name, "ymax")) {
1451 return (void*)(swf->movieSize.ymax/20);
1452 } else if(!strcmp(name, "width")) {
1453 return (void*)((swf->movieSize.xmax - swf->movieSize.xmin)/20);
1454 } else if(!strcmp(name, "height")) {
1455 return (void*)((swf->movieSize.ymax - swf->movieSize.ymin)/20);
1459 void swfresult_destroy(gfxresult_t*gfx)
1462 swf_FreeTags((SWF*)gfx->internal);
1463 free(gfx->internal);
1466 memset(gfx, 0, sizeof(gfxresult_t));
1470 static void swfoutput_destroy(gfxdevice_t* dev);
1472 gfxresult_t* swf_finish(gfxdevice_t* dev)
1474 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1477 if(i->config_linktarget) {
1478 free(i->config_linktarget);
1479 i->config_linktarget = 0;
1482 swfoutput_finalize(dev);
1483 SWF* swf = i->swf;i->swf = 0;
1484 swfoutput_destroy(dev);
1486 result = (gfxresult_t*)rfx_calloc(sizeof(gfxresult_t));
1487 result->internal = swf;
1488 result->save = swfresult_save;
1490 result->get = swfresult_get;
1491 result->destroy = swfresult_destroy;
1495 /* Perform cleaning up */
1496 static void swfoutput_destroy(gfxdevice_t* dev)
1498 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1500 /* not initialized yet- nothing to destroy */
1504 fontlist_t *tmp,*iterator = i->fontlist;
1506 if(iterator->swffont) {
1507 swf_FontFree(iterator->swffont);iterator->swffont=0;
1510 iterator = iterator->next;
1513 if(i->swf) {swf_FreeTags(i->swf);free(i->swf);i->swf = 0;}
1516 memset(dev, 0, sizeof(gfxdevice_t));
1519 static void swfoutput_setstrokecolor(gfxdevice_t* dev, U8 r, U8 g, U8 b, U8 a)
1521 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1522 if(i->strokergb.r == r &&
1523 i->strokergb.g == g &&
1524 i->strokergb.b == b &&
1525 i->strokergb.a == a) return;
1535 //#define ROUND_UP 19
1536 //#define ROUND_UP 10
1538 static void swfoutput_setlinewidth(gfxdevice_t*dev, double _linewidth)
1540 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1541 if(i->linewidth == (U16)(_linewidth*20+19.0/20.0))
1545 i->linewidth = (U16)(_linewidth*20+19.0/20.0);
1549 static void drawlink(gfxdevice_t*dev, ActionTAG*,ActionTAG*, gfxline_t*points, char mouseover, const char*url);
1550 static void swfoutput_namedlink(gfxdevice_t*dev, char*name, gfxline_t*points);
1551 static void swfoutput_linktopage(gfxdevice_t*dev, int page, gfxline_t*points);
1552 static void swfoutput_linktourl(gfxdevice_t*dev, const char*url, gfxline_t*points);
1554 /*void swfoutput_drawlink(gfxdevice_t*dev, char*url, gfxline_t*points)
1556 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1557 dev->drawlink(dev, points, url);
1560 void swf_drawlink(gfxdevice_t*dev, gfxline_t*points, const char*url)
1562 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1564 if(!strncmp("http://pdf2swf:", url, 15)) {
1565 char*tmp = strdup(url);
1566 int l = strlen(tmp);
1569 swfoutput_namedlink(dev, tmp+15, points);
1572 } else if(!strncmp("page", url, 4)) {
1575 if(url[t]<'0' || url[t]>'9')
1578 int page = atoi(&url[4]);
1579 if(page<0) page = 0;
1580 swfoutput_linktopage(dev, page, points);
1583 swfoutput_linktourl(dev, url, points);
1586 void swfoutput_linktourl(gfxdevice_t*dev, const char*url, gfxline_t*points)
1588 ActionTAG* actions = 0;
1589 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1595 if(i->config_externallinkfunction) {
1596 actions = action_PushString(actions, url); //parameter
1597 actions = action_PushInt(actions, 1); //number of parameters (1)
1598 actions = action_PushString(actions, i->config_externallinkfunction); //function name
1599 actions = action_CallFunction(actions);
1600 } else if(!i->config_linktarget) {
1601 if(!i->config_opennewwindow)
1602 actions = action_GetUrl(actions, url, "_parent");
1604 actions = action_GetUrl(actions, url, "_this");
1606 actions = action_GetUrl(actions, url, i->config_linktarget);
1608 actions = action_End(actions);
1610 drawlink(dev, actions, 0, points, 0, url);
1612 void swfoutput_linktopage(gfxdevice_t*dev, int page, gfxline_t*points)
1614 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1615 ActionTAG* actions = 0;
1622 if(!i->config_internallinkfunction) {
1623 actions = action_GotoFrame(actions, page-1);
1624 actions = action_End(actions);
1626 actions = action_PushInt(actions, page); //parameter
1627 actions = action_PushInt(actions, 1); //number of parameters (1)
1628 actions = action_PushString(actions, i->config_internallinkfunction); //function name
1629 actions = action_CallFunction(actions);
1630 actions = action_End(actions);
1634 sprintf(name, "page%d", page);
1636 drawlink(dev, actions, 0, points, 0, name);
1639 /* Named Links (a.k.a. Acrobatmenu) are used to implement various gadgets
1640 of the viewer objects, like subtitles, index elements etc.
1642 void swfoutput_namedlink(gfxdevice_t*dev, char*name, gfxline_t*points)
1644 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1645 ActionTAG *actions1,*actions2;
1646 char*tmp = strdup(name);
1654 if(!strncmp(tmp, "call:", 5))
1656 char*x = strchr(&tmp[5], ':');
1658 actions1 = action_PushInt(0, 0); //number of parameters (0)
1659 actions1 = action_PushString(actions1, &tmp[5]); //function name
1660 actions1 = action_CallFunction(actions1);
1661 actions1 = action_End(actions1);
1664 actions1 = action_PushString(0, x+1); //parameter
1665 actions1 = action_PushInt(actions1, 1); //number of parameters (1)
1666 actions1 = action_PushString(actions1, &tmp[5]); //function name
1667 actions1 = action_CallFunction(actions1);
1668 actions1 = action_End(actions1);
1670 actions2 = action_End(0);
1675 actions1 = action_PushString(0, "/:subtitle");
1676 actions1 = action_PushString(actions1, name);
1677 actions1 = action_SetVariable(actions1);
1678 actions1 = action_End(actions1);
1680 actions2 = action_PushString(0, "/:subtitle");
1681 actions2 = action_PushString(actions2, "");
1682 actions2 = action_SetVariable(actions2);
1683 actions2 = action_End(actions2);
1686 drawlink(dev, actions1, actions2, points, mouseover, name);
1688 swf_ActionFree(actions1);
1689 swf_ActionFree(actions2);
1693 static void drawgfxline(gfxdevice_t*dev, gfxline_t*line, int fill)
1695 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1696 gfxcoord_t lastx=0,lasty=0,px=0,py=0;
1698 int lines= 0, splines=0;
1705 /* check whether the next segment is zero */
1706 if(line->type == gfx_moveTo) {
1707 moveto(dev, i->tag, line->x, line->y);
1708 px = lastx = line->x;
1709 py = lasty = line->y;
1711 } if(line->type == gfx_lineTo) {
1712 lineto(dev, i->tag, line->x, line->y);
1717 } else if(line->type == gfx_splineTo) {
1719 s.x = line->sx;p.x = line->x;
1720 s.y = line->sy;p.y = line->y;
1721 splineto(dev, i->tag, s, p);
1729 msg("<trace> drawgfxline, %d lines, %d splines", lines, splines);
1733 static void drawlink(gfxdevice_t*dev, ActionTAG*actions1, ActionTAG*actions2, gfxline_t*points, char mouseover, const char*url)
1735 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1744 int buttonid = getNewID(dev);
1745 gfxbbox_t bbox = gfxline_getbbox(points);
1748 myshapeid = getNewID(dev);
1749 i->tag = swf_InsertTag(i->tag,ST_DEFINESHAPE3);
1750 swf_ShapeNew(&i->shape);
1751 rgb.r = rgb.b = rgb.a = rgb.g = 0;
1752 fsid = swf_ShapeAddSolidFillStyle(i->shape,&rgb);
1753 swf_SetU16(i->tag, myshapeid);
1754 r.xmin = (int)(bbox.xmin*20);
1755 r.ymin = (int)(bbox.ymin*20);
1756 r.xmax = (int)(bbox.xmax*20);
1757 r.ymax = (int)(bbox.ymax*20);
1758 r = swf_ClipRect(i->pagebbox, r);
1759 swf_SetRect(i->tag,&r);
1760 swf_SetShapeStyles(i->tag,i->shape);
1761 swf_ShapeCountBits(i->shape,NULL,NULL);
1762 swf_SetShapeBits(i->tag,i->shape);
1763 swf_ShapeSetAll(i->tag,i->shape,/*x*/0,/*y*/0,0,fsid,0);
1764 i->swflastx = i->swflasty = 0;
1765 drawgfxline(dev, points, 1);
1766 swf_ShapeSetEnd(i->tag);
1769 myshapeid2 = getNewID(dev);
1770 i->tag = swf_InsertTag(i->tag,ST_DEFINESHAPE3);
1771 swf_ShapeNew(&i->shape);
1773 rgb = i->config_linkcolor;
1775 fsid = swf_ShapeAddSolidFillStyle(i->shape,&rgb);
1776 swf_SetU16(i->tag, myshapeid2);
1777 r.xmin = (int)(bbox.xmin*20);
1778 r.ymin = (int)(bbox.ymin*20);
1779 r.xmax = (int)(bbox.xmax*20);
1780 r.ymax = (int)(bbox.ymax*20);
1781 r = swf_ClipRect(i->pagebbox, r);
1782 swf_SetRect(i->tag,&r);
1783 swf_SetShapeStyles(i->tag,i->shape);
1784 swf_ShapeCountBits(i->shape,NULL,NULL);
1785 swf_SetShapeBits(i->tag,i->shape);
1786 swf_ShapeSetAll(i->tag,i->shape,/*x*/0,/*y*/0,0,fsid,0);
1787 i->swflastx = i->swflasty = 0;
1788 drawgfxline(dev, points, 1);
1789 swf_ShapeSetEnd(i->tag);
1793 i->tag = swf_InsertTag(i->tag,ST_DEFINEBUTTON);
1794 swf_SetU16(i->tag,buttonid); //id
1795 swf_ButtonSetFlags(i->tag, 0); //menu=no
1796 swf_ButtonSetRecord(i->tag,0x01,myshapeid,i->depth,0,0);
1797 swf_ButtonSetRecord(i->tag,0x02,myshapeid2,i->depth,0,0);
1798 swf_ButtonSetRecord(i->tag,0x04,myshapeid2,i->depth,0,0);
1799 swf_ButtonSetRecord(i->tag,0x08,myshapeid,i->depth,0,0);
1800 swf_SetU8(i->tag,0);
1801 swf_ActionSet(i->tag,actions1);
1802 swf_SetU8(i->tag,0);
1806 i->tag = swf_InsertTag(i->tag,ST_DEFINEBUTTON2);
1807 swf_SetU16(i->tag,buttonid); //id
1808 swf_ButtonSetFlags(i->tag, 0); //menu=no
1809 swf_ButtonSetRecord(i->tag,0x01,myshapeid,i->depth,0,0);
1810 swf_ButtonSetRecord(i->tag,0x02,myshapeid2,i->depth,0,0);
1811 swf_ButtonSetRecord(i->tag,0x04,myshapeid2,i->depth,0,0);
1812 swf_ButtonSetRecord(i->tag,0x08,myshapeid,i->depth,0,0);
1813 swf_SetU8(i->tag,0); // end of button records
1814 swf_ButtonSetCondition(i->tag, BC_IDLE_OVERUP);
1815 swf_ActionSet(i->tag,actions1);
1817 swf_ButtonSetCondition(i->tag, BC_OVERUP_IDLE);
1818 swf_ActionSet(i->tag,actions2);
1819 swf_SetU8(i->tag,0);
1820 swf_ButtonPostProcess(i->tag, 2);
1822 swf_SetU8(i->tag,0);
1823 swf_ButtonPostProcess(i->tag, 1);
1826 const char* name = 0;
1827 if(i->config_linknameurl) {
1831 msg("<trace> Placing link ID %d", buttonid);
1832 i->tag = swf_InsertTag(i->tag,ST_PLACEOBJECT2);
1834 if(posx!=0 || posy!=0) {
1836 p.x = (int)(posx*20);
1837 p.y = (int)(posy*20);
1838 p = swf_TurnPoint(p, &i->page_matrix);
1843 swf_ObjectPlace(i->tag, buttonid, getNewDepth(dev),&m,0,name);
1845 swf_ObjectPlace(i->tag, buttonid, getNewDepth(dev),&i->page_matrix,0,name);
1852 for(t=0;t<picpos;t++)
1854 if(pic_xids[t] == xid &&
1855 pic_yids[t] == yid) {
1856 width = pic_width[t];
1857 height = pic_height[t];
1861 pic_ids[picpos] = swfoutput_drawimagelosslessN(&output, pic, pal, width, height, x1,y1,x2,y2,x3,y3,x4,y4, numpalette);
1862 pic_xids[picpos] = xid;
1863 pic_yids[picpos] = yid;
1864 pic_width[picpos] = width;
1865 pic_height[picpos] = height;
1868 pic[width*y+x] = buf[0];
1872 xid += pal[1].r*3 + pal[1].g*11 + pal[1].b*17;
1873 yid += pal[1].r*7 + pal[1].g*5 + pal[1].b*23;
1877 xid += x*r+x*b*3+x*g*7+x*a*11;
1878 yid += y*r*3+y*b*17+y*g*19+y*a*11;
1880 for(t=0;t<picpos;t++)
1882 if(pic_xids[t] == xid &&
1883 pic_yids[t] == yid) {
1892 int swf_setparameter(gfxdevice_t*dev, const char*name, const char*value)
1894 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1896 msg("<trace> swfdevice: %s=%s", name, value);
1897 if(!strcmp(name, "jpegsubpixels")) {
1898 i->config_jpegsubpixels = atof(value);
1899 } else if(!strcmp(name, "ppmsubpixels")) {
1900 i->config_ppmsubpixels = atof(value);
1901 } else if(!strcmp(name, "subpixels")) {
1902 i->config_ppmsubpixels = i->config_jpegsubpixels = atof(value);
1903 } else if(!strcmp(name, "drawonlyshapes")) {
1904 i->config_drawonlyshapes = atoi(value);
1905 } else if(!strcmp(name, "ignoredraworder")) {
1906 i->config_ignoredraworder = atoi(value);
1907 } else if(!strcmp(name, "mark")) {
1908 if(!value || !value[0]) {
1909 if(i->mark) free(i->mark);
1913 i->mark = strdup("...");
1914 for(t=0;t<3;t++) if(value[t]) i->mark[t] = value[t];
1916 } else if(!strcmp(name, "filloverlap")) {
1917 i->config_filloverlap = atoi(value);
1918 } else if(!strcmp(name, "linksopennewwindow")) {
1919 i->config_opennewwindow = atoi(value);
1920 } else if(!strcmp(name, "opennewwindow")) {
1921 i->config_opennewwindow = atoi(value);
1922 } else if(!strcmp(name, "storeallcharacters")) {
1923 i->config_storeallcharacters = atoi(value);
1924 } else if(!strcmp(name, "enablezlib")) {
1925 i->config_enablezlib = atoi(value);
1926 } else if(!strcmp(name, "bboxvars")) {
1927 i->config_bboxvars = atoi(value);
1928 } else if(!strcmp(name, "frameresets")) {
1929 i->config_frameresets = atoi(value);
1930 } else if(!strcmp(name, "showclipshapes")) {
1931 i->config_showclipshapes = atoi(value);
1932 } else if(!strcmp(name, "reordertags")) {
1933 i->config_reordertags = atoi(value);
1934 } else if(!strcmp(name, "internallinkfunction")) {
1935 i->config_internallinkfunction = strdup(value);
1936 } else if(!strcmp(name, "externallinkfunction")) {
1937 i->config_externallinkfunction = strdup(value);
1938 } else if(!strcmp(name, "linkfunction")) { //sets both internallinkfunction and externallinkfunction
1939 i->config_internallinkfunction = strdup(value);
1940 i->config_externallinkfunction = strdup(value);
1941 } else if(!strcmp(name, "disable_polygon_conversion")) {
1942 i->config_disable_polygon_conversion = atoi(value);
1943 } else if(!strcmp(name, "normalize_polygon_positions")) {
1944 i->config_normalize_polygon_positions = atoi(value);
1945 } else if(!strcmp(name, "wxwindowparams")) {
1946 i->config_watermark = atoi(value);
1947 } else if(!strcmp(name, "insertstop")) {
1948 i->config_insertstoptag = atoi(value);
1949 } else if(!strcmp(name, "protect")) {
1950 i->config_protect = atoi(value);
1951 if(i->config_protect && i->tag) {
1952 i->tag = swf_InsertTag(i->tag, ST_PROTECT);
1954 } else if(!strcmp(name, "flashversion")) {
1955 i->config_flashversion = atoi(value);
1957 i->swf->fileVersion = i->config_flashversion;
1959 } else if(!strcmp(name, "framerate")) {
1960 i->config_framerate = atof(value);
1962 i->swf->frameRate = i->config_framerate*0x100;
1964 } else if(!strcmp(name, "minlinewidth")) {
1965 i->config_minlinewidth = atof(value);
1966 } else if(!strcmp(name, "caplinewidth")) {
1967 i->config_caplinewidth = atof(value);
1968 } else if(!strcmp(name, "linktarget")) {
1969 i->config_linktarget = strdup(value);
1970 } else if(!strcmp(name, "dumpfonts")) {
1971 i->config_dumpfonts = atoi(value);
1972 } else if(!strcmp(name, "animate")) {
1973 i->config_animate = atoi(value);
1974 } else if(!strcmp(name, "simpleviewer")) {
1975 i->config_simpleviewer = atoi(value);
1976 } else if(!strcmp(name, "next_bitmap_is_jpeg")) {
1978 } else if(!strcmp(name, "jpegquality")) {
1979 int val = atoi(value);
1981 if(val>101) val=101;
1982 i->config_jpegquality = val;
1983 } else if(!strcmp(name, "splinequality")) {
1984 int v = atoi(value);
1985 v = 500-(v*5); // 100% = 0.25 pixel, 0% = 25 pixel
1987 i->config_splinemaxerror = v;
1988 } else if(!strcmp(name, "fontquality")) {
1989 int v = atoi(value);
1990 v = 500-(v*5); // 100% = 0.25 pixel, 0% = 25 pixel
1992 i->config_fontsplinemaxerror = v;
1993 } else if(!strcmp(name, "linkcolor")) {
1994 if(strlen(value)!=8) {
1995 fprintf(stderr, "Unknown format for option 'linkcolor'. (%s <-> RRGGBBAA)\n", value);
1998 # define NIBBLE(s) (((s)>='0' && (s)<='9')?((s)-'0'):((s)&0x0f)+9)
1999 i->config_linkcolor.r = NIBBLE(value[0])<<4 | NIBBLE(value[1]);
2000 i->config_linkcolor.g = NIBBLE(value[2])<<4 | NIBBLE(value[3]);
2001 i->config_linkcolor.b = NIBBLE(value[4])<<4 | NIBBLE(value[5]);
2002 i->config_linkcolor.a = NIBBLE(value[6])<<4 | NIBBLE(value[7]);
2003 } else if(!strcmp(name, "help")) {
2004 printf("\nSWF layer options:\n");
2005 printf("jpegsubpixels=<pixels> resolution adjustment for jpeg images (same as jpegdpi, but in pixels)\n");
2006 printf("ppmsubpixels=<pixels resolution adjustment for lossless images (same as ppmdpi, but in pixels)\n");
2007 printf("subpixels=<pixels> shortcut for setting both jpegsubpixels and ppmsubpixels\n");
2008 printf("drawonlyshapes convert everything to shapes (currently broken)\n");
2009 printf("ignoredraworder allow to perform a few optimizations for creating smaller SWFs\n");
2010 printf("linksopennewwindow make links open a new browser window\n");
2011 printf("linktarget target window name of new links\n");
2012 printf("linkcolor=<color) color of links (format: RRGGBBAA)\n");
2013 printf("linknameurl Link buttons will be named like the URL they refer to (handy for iterating through links with actionscript)\n");
2014 printf("storeallcharacters don't reduce the fonts to used characters in the output file\n");
2015 printf("enablezlib switch on zlib compression (also done if flashversion>=7)\n");
2016 printf("bboxvars store the bounding box of the SWF file in actionscript variables\n");
2017 printf("reordertags=0/1 (default: 1) perform some tag optimizations\n");
2018 printf("internallinkfunction=<name> when the user clicks a internal link (to a different page) in the converted file, this actionscript function is called\n");
2019 printf("externallinkfunction=<name> when the user clicks an external link (e.g. http://www.foo.bar/) on the converted file, this actionscript function is called\n");
2020 printf("disable_polygon_conversion never convert strokes to polygons (will remove capstyles and joint styles)\n");
2021 printf("caplinewidth=<width> the minimum thichness a line needs to have so that capstyles become visible (and are converted)\n");
2022 printf("insertstop put an ActionScript \"STOP\" tag in every frame\n");
2023 printf("protect add a \"protect\" tag to the file, to prevent loading in the Flash editor\n");
2024 printf("flashversion=<version> the SWF fileversion (6)\n");
2025 printf("framerate=<fps> SWF framerate\n");
2026 printf("minlinewidth=<width> convert horizontal/vertical boxes smaller than this width to lines (0.05) \n");
2027 printf("simpleviewer Add next/previous buttons to the SWF\n");
2028 printf("animate insert a showframe tag after each placeobject (animate draw order of PDF files)\n");
2029 printf("jpegquality=<quality> set compression quality of jpeg images\n");
2030 printf("splinequality=<value> Set the quality of spline convertion to value (0-100, default: 100).\n");
2037 // --------------------------------------------------------------------
2039 static CXFORM gfxcxform_to_cxform(gfxcxform_t* c)
2042 swf_GetCXForm(0, &cx, 1);
2045 if(c->rg!=0 || c->rb!=0 || c->ra!=0 ||
2046 c->gr!=0 || c->gb!=0 || c->ga!=0 ||
2047 c->br!=0 || c->bg!=0 || c->ba!=0 ||
2048 c->ar!=0 || c->ag!=0 || c->ab!=0)
2049 msg("<warning> CXForm not SWF-compatible");
2051 cx.a0 = (S16)(c->aa*256);
2052 cx.r0 = (S16)(c->rr*256);
2053 cx.g0 = (S16)(c->gg*256);
2054 cx.b0 = (S16)(c->bb*256);
2063 static int imageInCache(gfxdevice_t*dev, void*data, int width, int height)
2067 static void addImageToCache(gfxdevice_t*dev, void*data, int width, int height)
2071 static int add_image(swfoutput_internal*i, gfximage_t*img, int targetwidth, int targetheight, int* newwidth, int* newheight)
2073 gfxdevice_t*dev = i->dev;
2075 RGBA*mem = (RGBA*)img->data;
2077 int sizex = img->width;
2078 int sizey = img->height;
2079 int is_jpeg = i->jpeg;
2082 int newsizex=sizex, newsizey=sizey;
2085 if(is_jpeg && i->config_jpegsubpixels) {
2086 newsizex = (int)(targetwidth*i->config_jpegsubpixels + 0.5);
2087 newsizey = (int)(targetheight*i->config_jpegsubpixels + 0.5);
2088 } else if(!is_jpeg && i->config_ppmsubpixels) {
2089 newsizex = (int)(targetwidth*i->config_ppmsubpixels + 0.5);
2090 newsizey = (int)(targetheight*i->config_ppmsubpixels + 0.5);
2094 if(sizex<=0 || sizey<=0)
2101 /* TODO: cache images */
2103 if(newsizex<sizex || newsizey<sizey) {
2104 msg("<verbose> Scaling %dx%d image to %dx%d", sizex, sizey, newsizex, newsizey);
2105 newpic = swf_ImageScale(mem, sizex, sizey, newsizex, newsizey);
2106 *newwidth = sizex = newsizex;
2107 *newheight = sizey = newsizey;
2110 *newwidth = newsizex = sizex;
2111 *newheight = newsizey = sizey;
2114 int num_colors = swf_ImageGetNumberOfPaletteEntries(mem,sizex,sizey,0);
2115 int has_alpha = swf_ImageHasAlpha(mem,sizex,sizey);
2117 msg("<verbose> Drawing %dx%d %s%simage (id %d) at size %dx%d (%dx%d), %s%d colors",
2119 has_alpha?(has_alpha==2?"semi-transparent ":"transparent "):"",
2120 is_jpeg?"jpeg-":"", i->currentswfid+1,
2122 targetwidth, targetheight,
2123 /*newsizex, newsizey,*/
2124 num_colors>256?">":"", num_colors>256?256:num_colors);
2126 /*RGBA* pal = (RGBA*)rfx_alloc(sizeof(RGBA)*num_colors);
2127 swf_ImageGetNumberOfPaletteEntries(mem,sizex,sizey,pal);
2129 for(t=0;t<num_colors;t++) {
2130 printf("%02x%02x%02x%02x ",
2131 pal[t].r, pal[t].g, pal[t].b, pal[t].a);
2138 int cacheid = imageInCache(dev, mem, sizex, sizey);
2141 bitid = getNewID(dev);
2143 i->tag = swf_AddImage(i->tag, bitid, mem, sizex, sizey, i->config_jpegquality);
2144 addImageToCache(dev, mem, sizex, sizey);
2154 static SRECT gfxline_getSWFbbox(gfxline_t*line)
2156 gfxbbox_t bbox = gfxline_getbbox(line);
2158 r.xmin = (int)(bbox.xmin*20);
2159 r.ymin = (int)(bbox.ymin*20);
2160 r.xmax = (int)(bbox.xmax*20);
2161 r.ymax = (int)(bbox.ymax*20);
2165 int line_is_empty(gfxline_t*line)
2168 if(line->type != gfx_moveTo)
2175 static void swf_fillbitmap(gfxdevice_t*dev, gfxline_t*line, gfximage_t*img, gfxmatrix_t*matrix, gfxcxform_t*cxform)
2177 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
2179 if(line_is_empty(line))
2185 int targetx = (int)(sqrt(matrix->m00*matrix->m00 + matrix->m01*matrix->m01)*img->width);
2186 int targety = (int)(sqrt(matrix->m10*matrix->m10 + matrix->m11*matrix->m11)*img->height);
2188 int newwidth=0,newheight=0;
2189 int bitid = add_image(i, img, targetx, targety, &newwidth, &newheight);
2192 double fx = (double)img->width / (double)newwidth;
2193 double fy = (double)img->height / (double)newheight;
2196 m.sx = (int)(65536*20*matrix->m00*fx); m.r1 = (int)(65536*20*matrix->m10*fy);
2197 m.r0 = (int)(65536*20*matrix->m01*fx); m.sy = (int)(65536*20*matrix->m11*fy);
2198 m.tx = (int)(matrix->tx*20);
2199 m.ty = (int)(matrix->ty*20);
2202 int myshapeid = getNewID(dev);
2203 i->tag = swf_InsertTag(i->tag,ST_DEFINESHAPE);
2205 swf_ShapeNew(&shape);
2206 int fsid = swf_ShapeAddBitmapFillStyle(shape,&m,bitid,1);
2207 swf_SetU16(i->tag, myshapeid);
2208 SRECT r = gfxline_getSWFbbox(line);
2209 r = swf_ClipRect(i->pagebbox, r);
2210 swf_SetRect(i->tag,&r);
2211 swf_SetShapeStyles(i->tag,shape);
2212 swf_ShapeCountBits(shape,NULL,NULL);
2213 swf_SetShapeBits(i->tag,shape);
2214 swf_ShapeSetAll(i->tag,shape,UNDEFINED_COORD,UNDEFINED_COORD,0,fsid,0);
2215 i->swflastx = i->swflasty = UNDEFINED_COORD;
2216 drawgfxline(dev, line, 1);
2217 swf_ShapeSetEnd(i->tag);
2218 swf_ShapeFree(shape);
2220 msg("<trace> Placing image, shape ID %d, bitmap ID %d", myshapeid, bitid);
2221 i->tag = swf_InsertTag(i->tag,ST_PLACEOBJECT2);
2222 CXFORM cxform2 = gfxcxform_to_cxform(cxform);
2223 swf_ObjectPlace(i->tag,myshapeid,getNewDepth(dev),&i->page_matrix,&cxform2,NULL);
2226 static RGBA col_black = {255,0,0,0};
2228 static void drawoutline(gfxdevice_t*dev, gfxline_t*line)
2230 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
2232 int myshapeid = getNewID(dev);
2233 i->tag = swf_InsertTag(i->tag,ST_DEFINESHAPE3);
2236 swf_ShapeNew(&shape);
2237 int lsid = swf_ShapeAddLineStyle(shape,1,&col_black);
2239 swf_SetU16(i->tag,myshapeid);
2240 SRECT r = gfxline_getSWFbbox(line);
2241 r = swf_ClipRect(i->pagebbox, r);
2242 swf_SetRect(i->tag,&r);
2243 swf_SetShapeStyles(i->tag,shape);
2244 swf_ShapeCountBits(shape,NULL,NULL);
2245 swf_SetShapeBits(i->tag,shape);
2246 swf_ShapeSetAll(i->tag,shape,UNDEFINED_COORD,UNDEFINED_COORD,lsid,0,0);
2247 drawgfxline(dev, line, 1);
2248 swf_ShapeSetEnd(i->tag);
2249 swf_ShapeFree(shape);
2251 i->tag = swf_InsertTag(i->tag,ST_PLACEOBJECT2);
2252 swf_ObjectPlace(i->tag, myshapeid, getNewDepth(dev), 0,0,0);
2255 static void swf_startclip(gfxdevice_t*dev, gfxline_t*line)
2257 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
2262 if(i->clippos >= 127)
2264 msg("<warning> Too many clip levels.");
2268 if(i->config_showclipshapes)
2269 drawoutline(dev, line);
2271 int myshapeid = getNewID(dev);
2272 i->tag = swf_InsertTag(i->tag,ST_DEFINESHAPE3);
2274 memset(&col, 0, sizeof(RGBA));
2277 swf_ShapeNew(&shape);
2278 int fsid = swf_ShapeAddSolidFillStyle(shape,&col);
2280 RGBA markcol = {0,i->mark[0],i->mark[1],i->mark[2]};
2281 swf_ShapeAddSolidFillStyle(shape,&markcol);
2283 swf_SetU16(i->tag,myshapeid);
2284 SRECT r = gfxline_getSWFbbox(line);
2285 r = swf_ClipRect(i->pagebbox, r);
2286 swf_SetRect(i->tag,&r);
2287 swf_SetShapeStyles(i->tag,shape);
2288 swf_ShapeCountBits(shape,NULL,NULL);
2289 swf_SetShapeBits(i->tag,shape);
2290 swf_ShapeSetAll(i->tag,shape,UNDEFINED_COORD,UNDEFINED_COORD,0,fsid,0);
2291 i->swflastx = i->swflasty = UNDEFINED_COORD;
2292 i->shapeisempty = 1;
2293 drawgfxline(dev, line, 1);
2294 if(i->shapeisempty) {
2295 /* an empty clip shape is equivalent to a shape with no area */
2296 int x = line?line->x:0;
2297 int y = line?line->y:0;
2298 moveto(dev, i->tag, x,y);
2299 lineto(dev, i->tag, x,y);
2300 lineto(dev, i->tag, x,y);
2302 if(!i->shapeisempty && i->currentswfid==1 && r.xmin==0 && r.ymin==0 && r.xmax==(int)(i->max_x*20) && r.ymax==(int)(i->max_y*20)) {
2303 if(i->config_watermark) {
2304 gfxbbox_t r; r.xmin = r.ymin = 0;r.xmax = i->max_x;r.ymax = i->max_y;
2305 draw_watermark(dev, r, 1);
2308 swf_ShapeSetEnd(i->tag);
2309 swf_ShapeFree(shape);
2311 /* TODO: remember the bbox, and check all shapes against it */
2313 msg("<trace> Placing clip ID %d", myshapeid);
2314 i->tag = swf_InsertTag(i->tag,ST_PLACEOBJECT2);
2315 i->cliptags[i->clippos] = i->tag;
2316 i->clipshapes[i->clippos] = myshapeid;
2317 i->clipdepths[i->clippos] = getNewDepth(dev);
2321 static void swf_endclip(gfxdevice_t*dev)
2323 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
2330 msg("<error> Invalid end of clipping region");
2334 /*swf_ObjectPlaceClip(i->cliptags[i->clippos],i->clipshapes[i->clippos],i->clipdepths[i->clippos],&i->page_matrix,NULL,NULL,
2335 / * clip to depth: * / i->depth <= i->clipdepths[i->clippos]? i->depth : i->depth - 1);
2337 swf_ObjectPlaceClip(i->cliptags[i->clippos],i->clipshapes[i->clippos],i->clipdepths[i->clippos],&i->page_matrix,NULL,NULL,i->depth);
2339 static int gfxline_type(gfxline_t*line)
2345 int haszerosegments=0;
2348 if(line->type == gfx_moveTo) {
2351 } else if(line->type == gfx_lineTo) {
2355 } else if(line->type == gfx_splineTo) {
2357 if(tmpsplines>lines)
2365 if(lines==0 && splines==0) return 0;
2366 else if(lines==1 && splines==0) return 1;
2367 else if(lines==0 && splines==1) return 2;
2368 else if(splines==0) return 3;
2372 static int gfxline_has_dots(gfxline_t*line)
2380 if(line->type == gfx_moveTo) {
2381 /* test the length of the preceding line, and assume it is a dot if
2382 it's length is less than 1.0. But *only* if there's a noticable
2383 gap between the previous line and the next moveTo. (I've come
2384 across a PDF where thousands of "dots" were stringed together,
2386 int last_short_gap = short_gap;
2387 if((fabs(line->x - x) + fabs(line->y - y)) < 1.0) {
2392 if(isline && dist < 1 && !short_gap && !last_short_gap) {
2397 } else if(line->type == gfx_lineTo) {
2398 dist += fabs(line->x - x) + fabs(line->y - y);
2400 } else if(line->type == gfx_splineTo) {
2401 dist += fabs(line->sx - x) + fabs(line->sy - y) +
2402 fabs(line->x - line->sx) + fabs(line->y - line->sy);
2409 if(isline && dist < 1 && !short_gap) {
2415 static int gfxline_fix_short_edges(gfxline_t*line)
2419 if(line->type == gfx_lineTo) {
2420 if(fabs(line->x - x) + fabs(line->y - y) < 0.01) {
2423 } else if(line->type == gfx_splineTo) {
2424 if(fabs(line->sx - x) + fabs(line->sy - y) +
2425 fabs(line->x - line->sx) + fabs(line->y - line->sy) < 0.01) {
2436 static char is_inside_page(gfxdevice_t*dev, gfxcoord_t x, gfxcoord_t y)
2438 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
2439 if(x<i->min_x || x>i->max_x) return 0;
2440 if(y<i->min_y || y>i->max_y) return 0;
2444 gfxline_t* gfxline_move(gfxline_t*line, double x, double y)
2446 gfxline_t*l = line = gfxline_clone(line);
2458 //#define NORMALIZE_POLYGON_POSITIONS
2460 static void swf_stroke(gfxdevice_t*dev, gfxline_t*line, gfxcoord_t width, gfxcolor_t*color, gfx_capType cap_style, gfx_joinType joint_style, gfxcoord_t miterLimit)
2462 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
2463 if(line_is_empty(line))
2465 int type = gfxline_type(line);
2466 int has_dots = gfxline_has_dots(line);
2467 gfxbbox_t r = gfxline_getbbox(line);
2468 int is_outside_page = !is_inside_page(dev, r.xmin, r.ymin) || !is_inside_page(dev, r.xmax, r.ymax);
2470 /* TODO: * split line into segments, and perform this check for all segments */
2472 if(i->config_disable_polygon_conversion || type>=5 ||
2474 (width <= i->config_caplinewidth
2475 || (cap_style == gfx_capRound && joint_style == gfx_joinRound)
2476 || (cap_style == gfx_capRound && type<=2)))) {} else
2478 /* convert line to polygon */
2479 msg("<trace> draw as polygon, type=%d dots=%d", type, has_dots);
2481 gfxline_fix_short_edges(line);
2482 /* we need to convert the line into a polygon */
2483 gfxpoly_t* poly = gfxpoly_strokeToPoly(line, width, cap_style, joint_style, miterLimit);
2484 gfxline_t*gfxline = gfxpoly_to_gfxline(poly);
2485 dev->fill(dev, gfxline, color);
2486 gfxline_free(gfxline);
2491 msg("<trace> draw as stroke, type=%d dots=%d", type, has_dots);
2494 if(i->config_normalize_polygon_positions) {
2496 double startx = 0, starty = 0;
2497 if(line && line->type == gfx_moveTo) {
2501 line = gfxline_move(line, -startx, -starty);
2502 i->shapeposx = (int)(startx*20);
2503 i->shapeposy = (int)(starty*20);
2506 swfoutput_setstrokecolor(dev, color->r, color->g, color->b, color->a);
2507 swfoutput_setlinewidth(dev, width);
2510 drawgfxline(dev, line, 0);
2512 if(i->config_normalize_polygon_positions) {
2513 free(line); //account for _move
2518 static void swf_fill(gfxdevice_t*dev, gfxline_t*line, gfxcolor_t*color)
2520 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
2521 if(line_is_empty(line))
2525 gfxbbox_t r = gfxline_getbbox(line);
2526 int is_outside_page = !is_inside_page(dev, r.xmin, r.ymin) || !is_inside_page(dev, r.xmax, r.ymax);
2528 //if(i->chardatapos && i->chardata[i->chardatapos-1].color.a) {
2531 if(!i->config_ignoredraworder)
2534 if(i->config_normalize_polygon_positions) {
2536 double startx = 0, starty = 0;
2537 if(line && line->type == gfx_moveTo) {
2541 line = gfxline_move(line, -startx, -starty);
2542 i->shapeposx = (int)(startx*20);
2543 i->shapeposy = (int)(starty*20);
2546 swfoutput_setfillcolor(dev, color->r, color->g, color->b, color->a);
2549 drawgfxline(dev, line, 1);
2551 if(i->currentswfid==2 && r.xmin==0 && r.ymin==0 && r.xmax==i->max_x && r.ymax==i->max_y) {
2552 if(i->config_watermark) {
2553 draw_watermark(dev, r, 1);
2557 msg("<trace> end of swf_fill (shapeid=%d)", i->shapeid);
2559 if(i->config_normalize_polygon_positions) {
2560 free(line); //account for _move
2564 static GRADIENT* gfxgradient_to_GRADIENT(gfxgradient_t*gradient)
2567 gfxgradient_t*g = gradient;
2572 GRADIENT* swfgradient = malloc(sizeof(GRADIENT));
2573 swfgradient->num = num;
2574 swfgradient->rgba = malloc(sizeof(swfgradient->rgba[0])*num);
2575 swfgradient->ratios = malloc(sizeof(swfgradient->ratios[0])*num);
2580 swfgradient->ratios[num] = g->pos*255;
2581 swfgradient->rgba[num] = *(RGBA*)&g->color;
2588 static void swf_fillgradient(gfxdevice_t*dev, gfxline_t*line, gfxgradient_t*gradient, gfxgradienttype_t type, gfxmatrix_t*matrix)
2590 if(line_is_empty(line))
2592 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
2594 if(line_is_empty(line))
2597 GRADIENT* swfgradient = gfxgradient_to_GRADIENT(gradient);
2604 double f = type==gfxgradient_radial?4:4;
2606 m.sx = (int)(matrix->m00*20*f); m.r1 = (int)(matrix->m10*20*f);
2607 m.r0 = (int)(matrix->m01*20*f); m.sy = (int)(matrix->m11*20*f);
2608 m.tx = (int)(matrix->tx*20);
2609 m.ty = (int)(matrix->ty*20);
2612 int myshapeid = getNewID(dev);
2613 i->tag = swf_InsertTag(i->tag, ST_DEFINESHAPE2);
2615 swf_ShapeNew(&shape);
2616 int fsid = swf_ShapeAddGradientFillStyle(shape,&m,swfgradient,type==gfxgradient_radial);
2617 swf_SetU16(i->tag, myshapeid);
2618 SRECT r = gfxline_getSWFbbox(line);
2619 r = swf_ClipRect(i->pagebbox, r);
2620 swf_SetRect(i->tag,&r);
2621 swf_SetShapeStyles(i->tag,shape);
2622 swf_ShapeCountBits(shape,NULL,NULL);
2623 swf_SetShapeBits(i->tag,shape);
2624 swf_ShapeSetAll(i->tag,shape,UNDEFINED_COORD,UNDEFINED_COORD,0,fsid,0);
2625 i->swflastx = i->swflasty = UNDEFINED_COORD;
2626 drawgfxline(dev, line, 1);
2627 swf_ShapeSetEnd(i->tag);
2628 swf_ShapeFree(shape);
2630 int depth = getNewDepth(dev);
2631 msg("<trace> Placing gradient, shape ID %d, depth %d", myshapeid, depth);
2632 i->tag = swf_InsertTag(i->tag,ST_PLACEOBJECT2);
2633 swf_ObjectPlace(i->tag,myshapeid,depth,&i->page_matrix,NULL,NULL);
2635 swf_FreeGradient(swfgradient);free(swfgradient);
2638 static SWFFONT* gfxfont_to_swffont(gfxfont_t*font, const char* id)
2640 SWFFONT*swffont = (SWFFONT*)rfx_calloc(sizeof(SWFFONT));
2642 SRECT bounds = {0,0,0,0};
2644 swffont->version = 2;
2645 swffont->name = (U8*)strdup(id);
2646 swffont->layout = (SWFLAYOUT*)rfx_calloc(sizeof(SWFLAYOUT));
2647 swffont->layout->ascent = 0;
2648 swffont->layout->descent = 0;
2649 swffont->layout->leading = 0;
2650 swffont->layout->bounds = (SRECT*)rfx_calloc(sizeof(SRECT)*font->num_glyphs);
2651 swffont->encoding = FONT_ENCODING_UNICODE;
2652 swffont->numchars = font->num_glyphs;
2653 swffont->maxascii = font->max_unicode;
2654 swffont->ascii2glyph = (int*)rfx_calloc(sizeof(int)*swffont->maxascii);
2655 swffont->glyph2ascii = (U16*)rfx_calloc(sizeof(U16)*swffont->numchars);
2656 swffont->glyph = (SWFGLYPH*)rfx_calloc(sizeof(SWFGLYPH)*swffont->numchars);
2657 swffont->glyphnames = (char**)rfx_calloc(sizeof(char*)*swffont->numchars);
2658 for(t=0;t<font->max_unicode;t++) {
2659 swffont->ascii2glyph[t] = font->unicode2glyph[t];
2661 for(t=0;t<font->num_glyphs;t++) {
2665 swffont->glyph2ascii[t] = font->glyphs[t].unicode;
2666 if(swffont->glyph2ascii[t] == 0xffff || swffont->glyph2ascii[t] == 0x0000) {
2667 /* flash 8 flashtype requires unique unicode IDs for each character.
2668 We use the Unicode private user area to assign characters, hoping that
2669 the font doesn't contain more than 2048 glyphs */
2670 swffont->glyph2ascii[t] = 0xe000 + (t&0x1fff);
2673 if(font->glyphs[t].name) {
2674 swffont->glyphnames[t] = strdup(font->glyphs[t].name);
2676 swffont->glyphnames[t] = 0;
2678 advance = (int)(font->glyphs[t].advance);
2680 swf_Shape01DrawerInit(&draw, 0);
2681 line = font->glyphs[t].line;
2684 c.x = line->sx * GLYPH_SCALE; c.y = line->sy * GLYPH_SCALE;
2685 to.x = line->x * GLYPH_SCALE; to.y = line->y * GLYPH_SCALE;
2686 if(line->type == gfx_moveTo) {
2687 draw.moveTo(&draw, &to);
2688 } else if(line->type == gfx_lineTo) {
2689 draw.lineTo(&draw, &to);
2690 } else if(line->type == gfx_splineTo) {
2691 draw.splineTo(&draw, &c, &to);
2696 swffont->glyph[t].shape = swf_ShapeDrawerToShape(&draw);
2697 swffont->layout->bounds[t] = swf_ShapeDrawerGetBBox(&draw);
2699 int xmax = swffont->layout->bounds[t].xmax / 20;
2700 if(xmax>0 && xmax*2 < advance) {
2701 printf("fix bad advance value: bbox=%d, advance=%d (%f)\n", xmax, advance, font->glyphs[t].advance);
2705 if(advance<32768/20) {
2706 swffont->glyph[t].advance = advance*20;
2708 swffont->glyph[t].advance = 32767;
2711 draw.dealloc(&draw);
2713 swf_ExpandRect2(&bounds, &swffont->layout->bounds[t]);
2717 /* Flash player will use the advance value from the char, and the ascent/descent values
2718 from the layout for text selection.
2719 ascent will extend the char into negative y direction, from the baseline, while descent
2720 will extend in positive y direction, also from the baseline.
2721 The baseline is defined as the y-position zero
2724 swffont->layout->ascent = -bounds.ymin;
2725 if(swffont->layout->ascent < 0)
2726 swffont->layout->ascent = 0;
2727 swffont->layout->descent = bounds.ymax;
2728 if(swffont->layout->descent < 0)
2729 swffont->layout->descent = 0;
2730 swffont->layout->leading = bounds.ymax - bounds.ymin;
2735 static void swf_addfont(gfxdevice_t*dev, gfxfont_t*font)
2737 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
2739 if(i->swffont && i->swffont->name && !strcmp((char*)i->swffont->name,font->id))
2740 return; // the requested font is the current font
2742 fontlist_t*last=0,*l = i->fontlist;
2745 if(!strcmp((char*)l->swffont->name, font->id)) {
2746 return; // we already know this font
2750 l = (fontlist_t*)rfx_calloc(sizeof(fontlist_t));
2751 l->swffont = gfxfont_to_swffont(font, font->id);
2758 swf_FontSetID(l->swffont, getNewID(i->dev));
2760 if(getScreenLogLevel() >= LOGLEVEL_DEBUG) {
2762 // print font information
2763 msg("<debug> Font %s",font->id);
2764 msg("<debug> | ID: %d", l->swffont->id);
2765 msg("<debug> | Version: %d", l->swffont->version);
2766 msg("<debug> | Name: %s", l->swffont->name);
2767 msg("<debug> | Numchars: %d", l->swffont->numchars);
2768 msg("<debug> | Maxascii: %d", l->swffont->maxascii);
2769 msg("<debug> | Style: %d", l->swffont->style);
2770 msg("<debug> | Encoding: %d", l->swffont->encoding);
2771 for(iii=0; iii<l->swffont->numchars;iii++) {
2772 msg("<debug> | Glyph %d) name=%s, unicode=%d size=%d bbox=(%.2f,%.2f,%.2f,%.2f)\n", iii, l->swffont->glyphnames?l->swffont->glyphnames[iii]:"<nonames>", l->swffont->glyph2ascii[iii], l->swffont->glyph[iii].shape->bitlen,
2773 l->swffont->layout->bounds[iii].xmin/20.0,
2774 l->swffont->layout->bounds[iii].ymin/20.0,
2775 l->swffont->layout->bounds[iii].xmax/20.0,
2776 l->swffont->layout->bounds[iii].ymax/20.0
2779 for(t=0;t<l->swffont->maxascii;t++) {
2780 if(l->swffont->ascii2glyph[t] == iii)
2781 msg("<debug> | - maps to %d",t);
2787 static void swf_switchfont(gfxdevice_t*dev, const char*fontid)
2789 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
2791 if(i->swffont && i->swffont->name && !strcmp((char*)i->swffont->name,fontid))
2792 return; // the requested font is the current font
2794 fontlist_t*l = i->fontlist;
2796 if(!strcmp((char*)l->swffont->name, fontid)) {
2797 i->swffont = l->swffont;
2802 msg("<error> Unknown font id: %s", fontid);
2806 static void swf_drawchar(gfxdevice_t*dev, gfxfont_t*font, int glyph, gfxcolor_t*color, gfxmatrix_t*matrix)
2808 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
2810 msg("<error> swf_drawchar called (glyph %d) without font", glyph);
2813 if(!i->swffont || !i->swffont->name || strcmp((char*)i->swffont->name,font->id)) // not equal to current font
2815 /* TODO: remove the need for this (enhance getcharacterbbox so that it can cope
2816 with multiple fonts */
2818 swf_switchfont(dev, font->id); // set the current font
2821 msg("<warning> swf_drawchar: Font is NULL");
2824 if(glyph<0 || glyph>=i->swffont->numchars) {
2825 msg("<warning> No character %d in font %s (%d chars)", glyph, FIXNULL((char*)i->swffont->name), i->swffont->numchars);
2829 setfontscale(dev, matrix->m00, matrix->m01, matrix->m10, matrix->m11, matrix->tx, matrix->ty, 0);
2831 double det = i->fontmatrix.sx/65536.0 * i->fontmatrix.sy/65536.0 -
2832 i->fontmatrix.r0/65536.0 * i->fontmatrix.r1/65536.0;
2833 if(fabs(det) < 0.0005) {
2834 /* x direction equals y direction- the text is invisible */
2835 msg("<verbose> Not drawing invisible character character %d (det=%f, m=[%f %f;%f %f]\n", glyph,
2837 i->fontmatrix.sx/65536.0, i->fontmatrix.r1/65536.0,
2838 i->fontmatrix.r0/65536.0, i->fontmatrix.sy/65536.0);
2842 /*if(i->swffont->glyph[glyph].shape->bitlen <= 16) {
2843 msg("<warning> Glyph %d in current charset (%s, %d characters) is empty",
2844 glyph, FIXNULL((char*)i->swffont->name), i->swffont->numchars);
2848 /* calculate character position with respect to the current font matrix */
2849 double s = 20 * GLYPH_SCALE / det;
2850 double px = matrix->tx - i->fontmatrix.tx/20.0;
2851 double py = matrix->ty - i->fontmatrix.ty/20.0;
2852 int x = (SCOORD)(( px * i->fontmatrix.sy/65536.0 - py * i->fontmatrix.r1/65536.0)*s);
2853 int y = (SCOORD)((- px * i->fontmatrix.r0/65536.0 + py * i->fontmatrix.sx/65536.0)*s);
2854 if(x>32767 || x<-32768 || y>32767 || y<-32768) {
2855 msg("<verbose> Moving character origin to %f %f\n", matrix->tx, matrix->ty);
2857 setfontscale(dev, matrix->m00, matrix->m01, matrix->m10, matrix->m11, matrix->tx, matrix->ty, 1);
2858 /* since we just moved the char origin to the current char's position,
2859 it now has the relative position (0,0) */
2868 msg("<trace> Drawing char %d in font %d at %d,%d in color %02x%02x%02x%02x",
2869 glyph, i->swffont->id, x, y, color->r, color->g, color->b, color->a);
2871 putcharacter(dev, i->swffont->id, glyph, x, y, i->current_font_size, *(RGBA*)color);
2872 swf_FontUseGlyph(i->swffont, glyph);