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;
1110 i->swf->firstTag = swf_InsertTag(NULL,ST_SETBACKGROUNDCOLOR);
1111 i->tag = i->swf->firstTag;
1113 rgb.a = rgb.r = rgb.g = rgb.b = 0xff;
1115 swf_SetRGB(i->tag,&rgb);
1117 i->startdepth = i->depth = 0;
1118 i->startids = i->currentswfid = 0;
1121 static void startshape(gfxdevice_t*dev)
1123 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1128 //if(i->chardatapos && i->chardata[i->chardatapos-1].color.a)
1131 i->tag = swf_InsertTag(i->tag,ST_DEFINESHAPE3);
1133 swf_ShapeNew(&i->shape);
1134 i->linestyleid = swf_ShapeAddLineStyle(i->shape,i->linewidth,&i->strokergb);
1135 i->fillstyleid = swf_ShapeAddSolidFillStyle(i->shape,&i->fillrgb);
1137 RGBA markcol = {0,i->mark[0],i->mark[1],i->mark[2]};
1138 swf_ShapeAddSolidFillStyle(i->shape,&markcol);
1141 i->shapeid = getNewID(dev);
1143 msg("<debug> Using shape id %d", i->shapeid);
1145 swf_SetU16(i->tag,i->shapeid); // ID
1147 i->bboxrectpos = i->tag->len;
1149 swf_SetRect(i->tag,&i->pagebbox);
1151 memset(&i->bboxrect, 0, sizeof(i->bboxrect));
1153 swf_SetShapeStyles(i->tag,i->shape);
1154 swf_ShapeCountBits(i->shape,NULL,NULL);
1155 swf_SetShapeBits(i->tag,i->shape);
1157 /* TODO: do we really need this? */
1158 //swf_ShapeSetAll(i->tag,i->shape,/*x*/0,/*y*/0,i->linestyleid,0,0);
1159 //swf_ShapeSetAll(i->tag,i->shape,/*x*/UNDEFINED_COORD,/*y*/UNDEFINED_COORD,i->linestyleid,0,0);
1160 i->swflastx=i->swflasty=UNDEFINED_COORD;
1161 i->lastwasfill = -1;
1162 i->shapeisempty = 1;
1165 static void starttext(gfxdevice_t*dev)
1167 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1171 if(i->config_watermark) {
1172 insert_watermark(dev, 0);
1174 i->textid = getNewID(dev);
1175 i->swflastx=i->swflasty=0;
1179 /* TODO: move to ../lib/rfxswf */
1180 void changeRect(gfxdevice_t*dev, TAG*tag, int pos, SRECT*newrect)
1182 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1183 /* determine length of old rect */
1187 swf_GetRect(tag, &old);
1188 swf_ResetReadBits(tag);
1189 int pos_end = tag->pos;
1191 int len = tag->len - pos_end;
1192 U8*data = (U8*)malloc(len);
1193 memcpy(data, &tag->data[pos_end], len);
1196 swf_SetRect(tag, newrect);
1197 swf_SetBlock(tag, data, len);
1199 tag->pos = tag->readBit = 0;
1202 void cancelshape(gfxdevice_t*dev)
1204 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1205 /* delete old shape tag */
1207 i->tag = i->tag->prev;
1208 swf_DeleteTag(0, todel);
1209 if(i->shape) {swf_ShapeFree(i->shape);i->shape=0;}
1211 i->bboxrectpos = -1;
1213 // i->currentswfid--; // doesn't work, for some reason
1216 void fixAreas(gfxdevice_t*dev)
1218 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1219 if(!i->shapeisempty && i->fill &&
1220 (i->bboxrect.xmin == i->bboxrect.xmax ||
1221 i->bboxrect.ymin == i->bboxrect.ymax) &&
1222 i->config_minlinewidth >= 0.001
1224 msg("<debug> Shape has size 0: width=%.2f height=%.2f",
1225 (i->bboxrect.xmax-i->bboxrect.xmin)/20.0,
1226 (i->bboxrect.ymax-i->bboxrect.ymin)/20.0
1229 SRECT r = i->bboxrect;
1231 if(r.xmin == r.xmax && r.ymin == r.ymax) {
1232 /* this thing comes down to a single dot- nothing to fix here */
1238 RGBA save_col = i->strokergb;
1239 int save_width = i->linewidth;
1241 i->strokergb = i->fillrgb;
1242 i->linewidth = (int)(i->config_minlinewidth*20);
1243 if(i->linewidth==0) i->linewidth = 1;
1248 moveto(dev, i->tag, r.xmin/20.0,r.ymin/20.0);
1249 lineto(dev, i->tag, r.xmax/20.0,r.ymax/20.0);
1251 i->strokergb = save_col;
1252 i->linewidth = save_width;
1257 static void endshape_noput(gfxdevice_t*dev)
1259 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1262 //changeRect(dev, i->tag, i->bboxrectpos, &i->bboxrect);
1265 swf_ShapeFree(i->shape);
1273 static void endshape(gfxdevice_t*dev)
1275 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1281 if(i->shapeisempty ||
1283 (i->bboxrect.xmin == i->bboxrect.xmax &&
1284 i->bboxrect.ymin == i->bboxrect.ymax))
1286 // delete the shape again, we didn't do anything
1287 msg("<debug> cancelling shape: bbox is (%f,%f,%f,%f)",
1288 i->bboxrect.xmin /20.0,
1289 i->bboxrect.ymin /20.0,
1290 i->bboxrect.xmax /20.0,
1291 i->bboxrect.ymax /20.0
1297 swf_ShapeSetEnd(i->tag);
1299 SRECT r = swf_ClipRect(i->pagebbox, i->bboxrect);
1300 changeRect(dev, i->tag, i->bboxrectpos, &r);
1302 msg("<trace> Placing shape ID %d", i->shapeid);
1304 i->tag = swf_InsertTag(i->tag,ST_PLACEOBJECT2);
1305 MATRIX m = i->page_matrix;
1306 m.tx += i->shapeposx;
1307 m.ty += i->shapeposy;
1308 swf_ObjectPlace(i->tag,i->shapeid,getNewDepth(dev),&m,NULL,NULL);
1310 if(i->config_animate) {
1311 i->tag = swf_InsertTag(i->tag,ST_SHOWFRAME);
1314 swf_ShapeFree(i->shape);
1317 i->bboxrectpos = -1;
1324 void wipeSWF(SWF*swf)
1326 TAG*tag = swf->firstTag;
1328 TAG*next = tag->next;
1329 if(tag->id != ST_SETBACKGROUNDCOLOR &&
1330 tag->id != ST_END &&
1331 tag->id != ST_DOACTION &&
1332 tag->id != ST_SHOWFRAME) {
1333 swf_DeleteTag(swf, tag);
1339 void swfoutput_finalize(gfxdevice_t*dev)
1341 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1343 if(i->tag && i->tag->id == ST_END)
1344 return; //already done
1346 i->swf->fileVersion = i->config_flashversion;
1347 i->swf->frameRate = i->config_framerate*0x100;
1349 if(i->config_bboxvars) {
1350 TAG* tag = swf_InsertTag(i->swf->firstTag, ST_DOACTION);
1352 a = action_PushString(a, "xmin");
1353 a = action_PushFloat(a, i->swf->movieSize.xmin / 20.0);
1354 a = action_SetVariable(a);
1355 a = action_PushString(a, "ymin");
1356 a = action_PushFloat(a, i->swf->movieSize.ymin / 20.0);
1357 a = action_SetVariable(a);
1358 a = action_PushString(a, "xmax");
1359 a = action_PushFloat(a, i->swf->movieSize.xmax / 20.0);
1360 a = action_SetVariable(a);
1361 a = action_PushString(a, "ymax");
1362 a = action_PushFloat(a, i->swf->movieSize.ymax / 20.0);
1363 a = action_SetVariable(a);
1364 a = action_PushString(a, "width");
1365 a = action_PushFloat(a, (i->swf->movieSize.xmax - i->swf->movieSize.xmin) / 20.0);
1366 a = action_SetVariable(a);
1367 a = action_PushString(a, "height");
1368 a = action_PushFloat(a, (i->swf->movieSize.ymax - i->swf->movieSize.ymin) / 20.0);
1369 a = action_SetVariable(a);
1371 swf_ActionSet(tag, a);
1376 free(i->mark);i->mark = 0;
1380 fontlist_t *iterator = i->fontlist;
1382 TAG*mtag = i->swf->firstTag;
1383 if(iterator->swffont) {
1384 if(!i->config_storeallcharacters) {
1385 msg("<debug> Reducing font %s", iterator->swffont->name);
1386 swf_FontReduce(iterator->swffont);
1388 int used = iterator->swffont->use && iterator->swffont->use->used_glyphs;
1390 mtag = swf_InsertTag(mtag, ST_DEFINEFONT2);
1391 swf_FontSetDefine2(mtag, iterator->swffont);
1395 iterator = iterator->next;
1398 i->tag = swf_InsertTag(i->tag,ST_END);
1399 TAG* tag = i->tag->prev;
1401 /* remove the removeobject2 tags between the last ST_SHOWFRAME
1402 and the ST_END- they confuse the flash player */
1403 while(tag->id == ST_REMOVEOBJECT2) {
1404 TAG* prev = tag->prev;
1405 swf_DeleteTag(i->swf, tag);
1412 if(i->config_enablezlib || i->config_flashversion>=6) {
1413 i->swf->compressed = 1;
1416 /* Add AVM2 actionscript */
1417 if(i->config_flashversion>=9 &&
1418 (i->config_insertstoptag || i->hasbuttons)) {
1419 swf_AddButtonLinks(i->swf, i->config_insertstoptag,
1420 i->config_internallinkfunction||i->config_externallinkfunction);
1422 // if(i->config_reordertags)
1423 // swf_Optimize(i->swf);
1426 int swfresult_save(gfxresult_t*gfx, const char*filename)
1428 SWF*swf = (SWF*)gfx->internal;
1431 fi = open(filename, O_BINARY|O_CREAT|O_TRUNC|O_WRONLY, 0777);
1436 msg("<fatal> Could not create \"%s\". ", FIXNULL(filename));
1440 if FAILED(swf_WriteSWF(fi,swf))
1441 msg("<error> WriteSWF() failed.\n");
1447 void* swfresult_get(gfxresult_t*gfx, const char*name)
1449 SWF*swf = (SWF*)gfx->internal;
1450 if(!strcmp(name, "swf")) {
1451 return (void*)swf_CopySWF(swf);
1452 } else if(!strcmp(name, "xmin")) {
1453 return (void*)(swf->movieSize.xmin/20);
1454 } else if(!strcmp(name, "ymin")) {
1455 return (void*)(swf->movieSize.ymin/20);
1456 } else if(!strcmp(name, "xmax")) {
1457 return (void*)(swf->movieSize.xmax/20);
1458 } else if(!strcmp(name, "ymax")) {
1459 return (void*)(swf->movieSize.ymax/20);
1460 } else if(!strcmp(name, "width")) {
1461 return (void*)((swf->movieSize.xmax - swf->movieSize.xmin)/20);
1462 } else if(!strcmp(name, "height")) {
1463 return (void*)((swf->movieSize.ymax - swf->movieSize.ymin)/20);
1467 void swfresult_destroy(gfxresult_t*gfx)
1470 swf_FreeTags((SWF*)gfx->internal);
1471 free(gfx->internal);
1474 memset(gfx, 0, sizeof(gfxresult_t));
1478 static void swfoutput_destroy(gfxdevice_t* dev);
1480 gfxresult_t* swf_finish(gfxdevice_t* dev)
1482 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1485 if(i->config_linktarget) {
1486 free(i->config_linktarget);
1487 i->config_linktarget = 0;
1490 swfoutput_finalize(dev);
1491 SWF* swf = i->swf;i->swf = 0;
1492 swfoutput_destroy(dev);
1494 result = (gfxresult_t*)rfx_calloc(sizeof(gfxresult_t));
1495 result->internal = swf;
1496 result->save = swfresult_save;
1498 result->get = swfresult_get;
1499 result->destroy = swfresult_destroy;
1503 /* Perform cleaning up */
1504 static void swfoutput_destroy(gfxdevice_t* dev)
1506 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1508 /* not initialized yet- nothing to destroy */
1512 fontlist_t *tmp,*iterator = i->fontlist;
1514 if(iterator->swffont) {
1515 swf_FontFree(iterator->swffont);iterator->swffont=0;
1518 iterator = iterator->next;
1521 if(i->swf) {swf_FreeTags(i->swf);free(i->swf);i->swf = 0;}
1524 memset(dev, 0, sizeof(gfxdevice_t));
1527 static void swfoutput_setstrokecolor(gfxdevice_t* dev, U8 r, U8 g, U8 b, U8 a)
1529 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1530 if(i->strokergb.r == r &&
1531 i->strokergb.g == g &&
1532 i->strokergb.b == b &&
1533 i->strokergb.a == a) return;
1543 //#define ROUND_UP 19
1544 //#define ROUND_UP 10
1546 static void swfoutput_setlinewidth(gfxdevice_t*dev, double _linewidth)
1548 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1549 if(i->linewidth == (U16)(_linewidth*20+19.0/20.0))
1553 i->linewidth = (U16)(_linewidth*20+19.0/20.0);
1557 static void drawlink(gfxdevice_t*dev, ActionTAG*,ActionTAG*, gfxline_t*points, char mouseover, const char*url);
1558 static void swfoutput_namedlink(gfxdevice_t*dev, char*name, gfxline_t*points);
1559 static void swfoutput_linktopage(gfxdevice_t*dev, int page, gfxline_t*points);
1560 static void swfoutput_linktourl(gfxdevice_t*dev, const char*url, gfxline_t*points);
1562 /*void swfoutput_drawlink(gfxdevice_t*dev, char*url, gfxline_t*points)
1564 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1565 dev->drawlink(dev, points, url);
1568 void swf_drawlink(gfxdevice_t*dev, gfxline_t*points, const char*url)
1570 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1572 if(i->config_disablelinks)
1575 if(!strncmp("http://pdf2swf:", url, 15)) {
1576 char*tmp = strdup(url);
1577 int l = strlen(tmp);
1580 swfoutput_namedlink(dev, tmp+15, points);
1583 } else if(!strncmp("page", url, 4)) {
1586 if(url[t]<'0' || url[t]>'9')
1589 int page = atoi(&url[4]);
1590 if(page<0) page = 0;
1591 swfoutput_linktopage(dev, page, points);
1594 swfoutput_linktourl(dev, url, points);
1597 void swfoutput_linktourl(gfxdevice_t*dev, const char*url, gfxline_t*points)
1599 ActionTAG* actions = 0;
1600 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1606 /* TODO: escape special characters in url */
1608 if(i->config_externallinkfunction && i->config_flashversion<=8) {
1609 actions = action_PushString(actions, url); //parameter
1610 actions = action_PushInt(actions, 1); //number of parameters (1)
1611 actions = action_PushString(actions, i->config_externallinkfunction); //function name
1612 actions = action_CallFunction(actions);
1613 } else if(!i->config_linktarget) {
1614 if(!i->config_opennewwindow)
1615 actions = action_GetUrl(actions, url, "_parent");
1617 actions = action_GetUrl(actions, url, "_this");
1619 actions = action_GetUrl(actions, url, i->config_linktarget);
1621 actions = action_End(actions);
1623 drawlink(dev, actions, 0, points, 0, url);
1625 void swfoutput_linktopage(gfxdevice_t*dev, int page, gfxline_t*points)
1627 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1628 ActionTAG* actions = 0;
1635 if(!i->config_internallinkfunction || i->config_flashversion>=9) {
1636 actions = action_GotoFrame(actions, page-1);
1637 actions = action_End(actions);
1639 actions = action_PushInt(actions, page); //parameter
1640 actions = action_PushInt(actions, 1); //number of parameters (1)
1641 actions = action_PushString(actions, i->config_internallinkfunction); //function name
1642 actions = action_CallFunction(actions);
1643 actions = action_End(actions);
1647 sprintf(name, "page%d", page);
1649 drawlink(dev, actions, 0, points, 0, name);
1652 /* Named Links (a.k.a. Acrobatmenu) are used to implement various gadgets
1653 of the viewer objects, like subtitles, index elements etc.
1655 void swfoutput_namedlink(gfxdevice_t*dev, char*name, gfxline_t*points)
1657 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1658 ActionTAG *actions1,*actions2;
1659 char*tmp = strdup(name);
1667 if(!strncmp(tmp, "call:", 5))
1669 char*x = strchr(&tmp[5], ':');
1671 actions1 = action_PushInt(0, 0); //number of parameters (0)
1672 actions1 = action_PushString(actions1, &tmp[5]); //function name
1673 actions1 = action_CallFunction(actions1);
1674 actions1 = action_End(actions1);
1677 actions1 = action_PushString(0, x+1); //parameter
1678 actions1 = action_PushInt(actions1, 1); //number of parameters (1)
1679 actions1 = action_PushString(actions1, &tmp[5]); //function name
1680 actions1 = action_CallFunction(actions1);
1681 actions1 = action_End(actions1);
1683 actions2 = action_End(0);
1688 actions1 = action_PushString(0, "/:subtitle");
1689 actions1 = action_PushString(actions1, name);
1690 actions1 = action_SetVariable(actions1);
1691 actions1 = action_End(actions1);
1693 actions2 = action_PushString(0, "/:subtitle");
1694 actions2 = action_PushString(actions2, "");
1695 actions2 = action_SetVariable(actions2);
1696 actions2 = action_End(actions2);
1699 drawlink(dev, actions1, actions2, points, mouseover, name);
1701 swf_ActionFree(actions1);
1702 swf_ActionFree(actions2);
1706 static void drawgfxline(gfxdevice_t*dev, gfxline_t*line, int fill)
1708 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1709 gfxcoord_t lastx=0,lasty=0,px=0,py=0;
1711 int lines= 0, splines=0;
1718 /* check whether the next segment is zero */
1719 if(line->type == gfx_moveTo) {
1720 moveto(dev, i->tag, line->x, line->y);
1721 px = lastx = line->x;
1722 py = lasty = line->y;
1724 } if(line->type == gfx_lineTo) {
1725 lineto(dev, i->tag, line->x, line->y);
1730 } else if(line->type == gfx_splineTo) {
1732 s.x = line->sx;p.x = line->x;
1733 s.y = line->sy;p.y = line->y;
1734 splineto(dev, i->tag, s, p);
1742 msg("<trace> drawgfxline, %d lines, %d splines", lines, splines);
1746 static void drawlink(gfxdevice_t*dev, ActionTAG*actions1, ActionTAG*actions2, gfxline_t*points, char mouseover, const char*url)
1748 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1757 int buttonid = getNewID(dev);
1758 gfxbbox_t bbox = gfxline_getbbox(points);
1763 myshapeid = getNewID(dev);
1764 i->tag = swf_InsertTag(i->tag,ST_DEFINESHAPE3);
1765 swf_ShapeNew(&i->shape);
1766 rgb.r = rgb.b = rgb.a = rgb.g = 0;
1767 fsid = swf_ShapeAddSolidFillStyle(i->shape,&rgb);
1768 swf_SetU16(i->tag, myshapeid);
1769 r.xmin = (int)(bbox.xmin*20);
1770 r.ymin = (int)(bbox.ymin*20);
1771 r.xmax = (int)(bbox.xmax*20);
1772 r.ymax = (int)(bbox.ymax*20);
1773 r = swf_ClipRect(i->pagebbox, r);
1774 swf_SetRect(i->tag,&r);
1775 swf_SetShapeStyles(i->tag,i->shape);
1776 swf_ShapeCountBits(i->shape,NULL,NULL);
1777 swf_SetShapeBits(i->tag,i->shape);
1778 swf_ShapeSetAll(i->tag,i->shape,/*x*/0,/*y*/0,0,fsid,0);
1779 i->swflastx = i->swflasty = 0;
1780 drawgfxline(dev, points, 1);
1781 swf_ShapeSetEnd(i->tag);
1784 myshapeid2 = getNewID(dev);
1785 i->tag = swf_InsertTag(i->tag,ST_DEFINESHAPE3);
1786 swf_ShapeNew(&i->shape);
1788 rgb = i->config_linkcolor;
1790 fsid = swf_ShapeAddSolidFillStyle(i->shape,&rgb);
1791 swf_SetU16(i->tag, myshapeid2);
1792 r.xmin = (int)(bbox.xmin*20);
1793 r.ymin = (int)(bbox.ymin*20);
1794 r.xmax = (int)(bbox.xmax*20);
1795 r.ymax = (int)(bbox.ymax*20);
1796 r = swf_ClipRect(i->pagebbox, r);
1797 swf_SetRect(i->tag,&r);
1798 swf_SetShapeStyles(i->tag,i->shape);
1799 swf_ShapeCountBits(i->shape,NULL,NULL);
1800 swf_SetShapeBits(i->tag,i->shape);
1801 swf_ShapeSetAll(i->tag,i->shape,/*x*/0,/*y*/0,0,fsid,0);
1802 i->swflastx = i->swflasty = 0;
1803 drawgfxline(dev, points, 1);
1804 swf_ShapeSetEnd(i->tag);
1808 i->tag = swf_InsertTag(i->tag,ST_DEFINEBUTTON);
1809 swf_SetU16(i->tag,buttonid); //id
1810 swf_ButtonSetFlags(i->tag, 0); //menu=no
1811 swf_ButtonSetRecord(i->tag,0x01,myshapeid,i->depth,0,0);
1812 swf_ButtonSetRecord(i->tag,0x02,myshapeid2,i->depth,0,0);
1813 swf_ButtonSetRecord(i->tag,0x04,myshapeid2,i->depth,0,0);
1814 swf_ButtonSetRecord(i->tag,0x08,myshapeid,i->depth,0,0);
1815 swf_SetU8(i->tag,0);
1816 swf_ActionSet(i->tag,actions1);
1817 swf_SetU8(i->tag,0);
1821 i->tag = swf_InsertTag(i->tag,ST_DEFINEBUTTON2);
1822 swf_SetU16(i->tag,buttonid); //id
1823 swf_ButtonSetFlags(i->tag, 0); //menu=no
1824 swf_ButtonSetRecord(i->tag,0x01,myshapeid,i->depth,0,0);
1825 swf_ButtonSetRecord(i->tag,0x02,myshapeid2,i->depth,0,0);
1826 swf_ButtonSetRecord(i->tag,0x04,myshapeid2,i->depth,0,0);
1827 swf_ButtonSetRecord(i->tag,0x08,myshapeid,i->depth,0,0);
1828 swf_SetU8(i->tag,0); // end of button records
1829 swf_ButtonSetCondition(i->tag, BC_IDLE_OVERUP);
1830 swf_ActionSet(i->tag,actions1);
1832 swf_ButtonSetCondition(i->tag, BC_OVERUP_IDLE);
1833 swf_ActionSet(i->tag,actions2);
1834 swf_SetU8(i->tag,0);
1835 swf_ButtonPostProcess(i->tag, 2);
1837 swf_SetU8(i->tag,0);
1838 swf_ButtonPostProcess(i->tag, 1);
1842 const char* name = 0;
1843 if(i->config_linknameurl) {
1847 sprintf(buf, "button%d", buttonid);
1850 msg("<trace> Placing link ID %d", buttonid);
1851 i->tag = swf_InsertTag(i->tag,ST_PLACEOBJECT2);
1853 if(posx!=0 || posy!=0) {
1855 p.x = (int)(posx*20);
1856 p.y = (int)(posy*20);
1857 p = swf_TurnPoint(p, &i->page_matrix);
1862 swf_ObjectPlace(i->tag, buttonid, getNewDepth(dev),&m,0,name);
1864 swf_ObjectPlace(i->tag, buttonid, getNewDepth(dev),&i->page_matrix,0,name);
1871 for(t=0;t<picpos;t++)
1873 if(pic_xids[t] == xid &&
1874 pic_yids[t] == yid) {
1875 width = pic_width[t];
1876 height = pic_height[t];
1880 pic_ids[picpos] = swfoutput_drawimagelosslessN(&output, pic, pal, width, height, x1,y1,x2,y2,x3,y3,x4,y4, numpalette);
1881 pic_xids[picpos] = xid;
1882 pic_yids[picpos] = yid;
1883 pic_width[picpos] = width;
1884 pic_height[picpos] = height;
1887 pic[width*y+x] = buf[0];
1891 xid += pal[1].r*3 + pal[1].g*11 + pal[1].b*17;
1892 yid += pal[1].r*7 + pal[1].g*5 + pal[1].b*23;
1896 xid += x*r+x*b*3+x*g*7+x*a*11;
1897 yid += y*r*3+y*b*17+y*g*19+y*a*11;
1899 for(t=0;t<picpos;t++)
1901 if(pic_xids[t] == xid &&
1902 pic_yids[t] == yid) {
1911 int swf_setparameter(gfxdevice_t*dev, const char*name, const char*value)
1913 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1915 msg("<trace> swfdevice: %s=%s", name, value);
1916 if(!strcmp(name, "jpegsubpixels")) {
1917 i->config_jpegsubpixels = atof(value);
1918 } else if(!strcmp(name, "ppmsubpixels")) {
1919 i->config_ppmsubpixels = atof(value);
1920 } else if(!strcmp(name, "subpixels")) {
1921 i->config_ppmsubpixels = i->config_jpegsubpixels = atof(value);
1922 } else if(!strcmp(name, "drawonlyshapes")) {
1923 i->config_drawonlyshapes = atoi(value);
1924 } else if(!strcmp(name, "ignoredraworder")) {
1925 i->config_ignoredraworder = atoi(value);
1926 } else if(!strcmp(name, "mark")) {
1927 if(!value || !value[0]) {
1928 if(i->mark) free(i->mark);
1932 i->mark = strdup("...");
1933 for(t=0;t<3;t++) if(value[t]) i->mark[t] = value[t];
1935 } else if(!strcmp(name, "filloverlap")) {
1936 i->config_filloverlap = atoi(value);
1937 } else if(!strcmp(name, "linksopennewwindow")) {
1938 i->config_opennewwindow = atoi(value);
1939 } else if(!strcmp(name, "opennewwindow")) {
1940 i->config_opennewwindow = atoi(value);
1941 } else if(!strcmp(name, "storeallcharacters")) {
1942 i->config_storeallcharacters = atoi(value);
1943 } else if(!strcmp(name, "enablezlib")) {
1944 i->config_enablezlib = atoi(value);
1945 } else if(!strcmp(name, "bboxvars")) {
1946 i->config_bboxvars = atoi(value);
1947 } else if(!strcmp(name, "dots")) {
1948 i->config_dots = atoi(value);
1949 } else if(!strcmp(name, "frameresets")) {
1950 i->config_frameresets = atoi(value);
1951 } else if(!strcmp(name, "showclipshapes")) {
1952 i->config_showclipshapes = atoi(value);
1953 } else if(!strcmp(name, "reordertags")) {
1954 i->config_reordertags = atoi(value);
1955 } else if(!strcmp(name, "internallinkfunction")) {
1956 i->config_internallinkfunction = strdup(value);
1957 } else if(!strcmp(name, "externallinkfunction")) {
1958 i->config_externallinkfunction = strdup(value);
1959 } else if(!strcmp(name, "linkfunction")) { //sets both internallinkfunction and externallinkfunction
1960 i->config_internallinkfunction = strdup(value);
1961 i->config_externallinkfunction = strdup(value);
1962 } else if(!strcmp(name, "disable_polygon_conversion")) {
1963 i->config_disable_polygon_conversion = atoi(value);
1964 } else if(!strcmp(name, "normalize_polygon_positions")) {
1965 i->config_normalize_polygon_positions = atoi(value);
1966 } else if(!strcmp(name, "wxwindowparams")) {
1967 i->config_watermark = atoi(value);
1968 } else if(!strcmp(name, "insertstop")) {
1969 i->config_insertstoptag = atoi(value);
1970 } else if(!strcmp(name, "protect")) {
1971 i->config_protect = atoi(value);
1972 if(i->config_protect && i->tag) {
1973 i->tag = swf_InsertTag(i->tag, ST_PROTECT);
1975 } else if(!strcmp(name, "flashversion")) {
1976 i->config_flashversion = atoi(value);
1978 i->swf->fileVersion = i->config_flashversion;
1980 } else if(!strcmp(name, "framerate")) {
1981 i->config_framerate = atof(value);
1983 i->swf->frameRate = i->config_framerate*0x100;
1985 } else if(!strcmp(name, "minlinewidth")) {
1986 i->config_minlinewidth = atof(value);
1987 } else if(!strcmp(name, "caplinewidth")) {
1988 i->config_caplinewidth = atof(value);
1989 } else if(!strcmp(name, "linktarget")) {
1990 i->config_linktarget = strdup(value);
1991 } else if(!strcmp(name, "dumpfonts")) {
1992 i->config_dumpfonts = atoi(value);
1993 } else if(!strcmp(name, "animate")) {
1994 i->config_animate = atoi(value);
1995 } else if(!strcmp(name, "disablelinks")) {
1996 i->config_disablelinks = atoi(value);
1997 } else if(!strcmp(name, "simpleviewer")) {
1998 i->config_simpleviewer = atoi(value);
1999 } else if(!strcmp(name, "next_bitmap_is_jpeg")) {
2001 } else if(!strcmp(name, "jpegquality")) {
2002 int val = atoi(value);
2004 if(val>101) val=101;
2005 i->config_jpegquality = val;
2006 } else if(!strcmp(name, "splinequality")) {
2007 int v = atoi(value);
2008 v = 500-(v*5); // 100% = 0.25 pixel, 0% = 25 pixel
2010 i->config_splinemaxerror = v;
2011 } else if(!strcmp(name, "fontquality")) {
2012 int v = atoi(value);
2013 v = 500-(v*5); // 100% = 0.25 pixel, 0% = 25 pixel
2015 i->config_fontsplinemaxerror = v;
2016 } else if(!strcmp(name, "linkcolor")) {
2017 if(strlen(value)!=8) {
2018 fprintf(stderr, "Unknown format for option 'linkcolor'. (%s <-> RRGGBBAA)\n", value);
2021 # define NIBBLE(s) (((s)>='0' && (s)<='9')?((s)-'0'):((s)&0x0f)+9)
2022 i->config_linkcolor.r = NIBBLE(value[0])<<4 | NIBBLE(value[1]);
2023 i->config_linkcolor.g = NIBBLE(value[2])<<4 | NIBBLE(value[3]);
2024 i->config_linkcolor.b = NIBBLE(value[4])<<4 | NIBBLE(value[5]);
2025 i->config_linkcolor.a = NIBBLE(value[6])<<4 | NIBBLE(value[7]);
2026 } else if(!strcmp(name, "help")) {
2027 printf("\nSWF layer options:\n");
2028 printf("jpegsubpixels=<pixels> resolution adjustment for jpeg images (same as jpegdpi, but in pixels)\n");
2029 printf("ppmsubpixels=<pixels resolution adjustment for lossless images (same as ppmdpi, but in pixels)\n");
2030 printf("subpixels=<pixels> shortcut for setting both jpegsubpixels and ppmsubpixels\n");
2031 printf("drawonlyshapes convert everything to shapes (currently broken)\n");
2032 printf("ignoredraworder allow to perform a few optimizations for creating smaller SWFs\n");
2033 printf("linksopennewwindow make links open a new browser window\n");
2034 printf("linktarget target window name of new links\n");
2035 printf("linkcolor=<color) color of links (format: RRGGBBAA)\n");
2036 printf("linknameurl Link buttons will be named like the URL they refer to (handy for iterating through links with actionscript)\n");
2037 printf("storeallcharacters don't reduce the fonts to used characters in the output file\n");
2038 printf("enablezlib switch on zlib compression (also done if flashversion>=7)\n");
2039 printf("bboxvars store the bounding box of the SWF file in actionscript variables\n");
2040 printf("dots Take care to handle dots correctly\n");
2041 printf("reordertags=0/1 (default: 1) perform some tag optimizations\n");
2042 printf("internallinkfunction=<name> when the user clicks a internal link (to a different page) in the converted file, this actionscript function is called\n");
2043 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");
2044 printf("disable_polygon_conversion never convert strokes to polygons (will remove capstyles and joint styles)\n");
2045 printf("caplinewidth=<width> the minimum thichness a line needs to have so that capstyles become visible (and are converted)\n");
2046 printf("insertstop put an ActionScript \"STOP\" tag in every frame\n");
2047 printf("protect add a \"protect\" tag to the file, to prevent loading in the Flash editor\n");
2048 printf("flashversion=<version> the SWF fileversion (6)\n");
2049 printf("framerate=<fps> SWF framerate\n");
2050 printf("minlinewidth=<width> convert horizontal/vertical boxes smaller than this width to lines (0.05) \n");
2051 printf("simpleviewer Add next/previous buttons to the SWF\n");
2052 printf("animate insert a showframe tag after each placeobject (animate draw order of PDF files)\n");
2053 printf("jpegquality=<quality> set compression quality of jpeg images\n");
2054 printf("splinequality=<value> Set the quality of spline convertion to value (0-100, default: 100).\n");
2055 printf("disablelinks Disable links.\n");
2062 // --------------------------------------------------------------------
2064 static CXFORM gfxcxform_to_cxform(gfxcxform_t* c)
2067 swf_GetCXForm(0, &cx, 1);
2070 if(c->rg!=0 || c->rb!=0 || c->ra!=0 ||
2071 c->gr!=0 || c->gb!=0 || c->ga!=0 ||
2072 c->br!=0 || c->bg!=0 || c->ba!=0 ||
2073 c->ar!=0 || c->ag!=0 || c->ab!=0)
2074 msg("<warning> CXForm not SWF-compatible");
2076 cx.a0 = (S16)(c->aa*256);
2077 cx.r0 = (S16)(c->rr*256);
2078 cx.g0 = (S16)(c->gg*256);
2079 cx.b0 = (S16)(c->bb*256);
2088 static int imageInCache(gfxdevice_t*dev, void*data, int width, int height)
2092 static void addImageToCache(gfxdevice_t*dev, void*data, int width, int height)
2096 static int add_image(swfoutput_internal*i, gfximage_t*img, int targetwidth, int targetheight, int* newwidth, int* newheight)
2098 gfxdevice_t*dev = i->dev;
2100 RGBA*mem = (RGBA*)img->data;
2102 int sizex = img->width;
2103 int sizey = img->height;
2104 int is_jpeg = i->jpeg;
2107 int newsizex=sizex, newsizey=sizey;
2110 if(is_jpeg && i->config_jpegsubpixels) {
2111 newsizex = (int)(targetwidth*i->config_jpegsubpixels + 0.5);
2112 newsizey = (int)(targetheight*i->config_jpegsubpixels + 0.5);
2113 } else if(!is_jpeg && i->config_ppmsubpixels) {
2114 newsizex = (int)(targetwidth*i->config_ppmsubpixels + 0.5);
2115 newsizey = (int)(targetheight*i->config_ppmsubpixels + 0.5);
2119 if(sizex<=0 || sizey<=0)
2126 /* TODO: cache images */
2128 if(newsizex<sizex || newsizey<sizey) {
2129 msg("<verbose> Scaling %dx%d image to %dx%d", sizex, sizey, newsizex, newsizey);
2130 newpic = swf_ImageScale(mem, sizex, sizey, newsizex, newsizey);
2131 *newwidth = sizex = newsizex;
2132 *newheight = sizey = newsizey;
2135 *newwidth = newsizex = sizex;
2136 *newheight = newsizey = sizey;
2139 int num_colors = swf_ImageGetNumberOfPaletteEntries(mem,sizex,sizey,0);
2140 int has_alpha = swf_ImageHasAlpha(mem,sizex,sizey);
2142 msg("<verbose> Drawing %dx%d %s%simage (id %d) at size %dx%d (%dx%d), %s%d colors",
2144 has_alpha?(has_alpha==2?"semi-transparent ":"transparent "):"",
2145 is_jpeg?"jpeg-":"", i->currentswfid+1,
2147 targetwidth, targetheight,
2148 /*newsizex, newsizey,*/
2149 num_colors>256?">":"", num_colors>256?256:num_colors);
2151 /*RGBA* pal = (RGBA*)rfx_alloc(sizeof(RGBA)*num_colors);
2152 swf_ImageGetNumberOfPaletteEntries(mem,sizex,sizey,pal);
2154 for(t=0;t<num_colors;t++) {
2155 printf("%02x%02x%02x%02x ",
2156 pal[t].r, pal[t].g, pal[t].b, pal[t].a);
2163 int cacheid = imageInCache(dev, mem, sizex, sizey);
2166 bitid = getNewID(dev);
2168 i->tag = swf_AddImage(i->tag, bitid, mem, sizex, sizey, i->config_jpegquality);
2169 addImageToCache(dev, mem, sizex, sizey);
2179 static SRECT gfxline_getSWFbbox(gfxline_t*line)
2181 gfxbbox_t bbox = gfxline_getbbox(line);
2183 r.xmin = (int)(bbox.xmin*20);
2184 r.ymin = (int)(bbox.ymin*20);
2185 r.xmax = (int)(bbox.xmax*20);
2186 r.ymax = (int)(bbox.ymax*20);
2190 int line_is_empty(gfxline_t*line)
2193 if(line->type != gfx_moveTo)
2200 static void swf_fillbitmap(gfxdevice_t*dev, gfxline_t*line, gfximage_t*img, gfxmatrix_t*matrix, gfxcxform_t*cxform)
2202 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
2204 if(line_is_empty(line))
2210 int targetx = (int)(sqrt(matrix->m00*matrix->m00 + matrix->m01*matrix->m01)*img->width);
2211 int targety = (int)(sqrt(matrix->m10*matrix->m10 + matrix->m11*matrix->m11)*img->height);
2213 int newwidth=0,newheight=0;
2214 int bitid = add_image(i, img, targetx, targety, &newwidth, &newheight);
2217 double fx = (double)img->width / (double)newwidth;
2218 double fy = (double)img->height / (double)newheight;
2221 m.sx = (int)(65536*20*matrix->m00*fx); m.r1 = (int)(65536*20*matrix->m10*fy);
2222 m.r0 = (int)(65536*20*matrix->m01*fx); m.sy = (int)(65536*20*matrix->m11*fy);
2223 m.tx = (int)(matrix->tx*20);
2224 m.ty = (int)(matrix->ty*20);
2227 int myshapeid = getNewID(dev);
2228 i->tag = swf_InsertTag(i->tag,ST_DEFINESHAPE);
2230 swf_ShapeNew(&shape);
2231 int fsid = swf_ShapeAddBitmapFillStyle(shape,&m,bitid,1);
2232 swf_SetU16(i->tag, myshapeid);
2233 SRECT r = gfxline_getSWFbbox(line);
2234 r = swf_ClipRect(i->pagebbox, r);
2235 swf_SetRect(i->tag,&r);
2236 swf_SetShapeStyles(i->tag,shape);
2237 swf_ShapeCountBits(shape,NULL,NULL);
2238 swf_SetShapeBits(i->tag,shape);
2239 swf_ShapeSetAll(i->tag,shape,UNDEFINED_COORD,UNDEFINED_COORD,0,fsid,0);
2240 i->swflastx = i->swflasty = UNDEFINED_COORD;
2241 drawgfxline(dev, line, 1);
2242 swf_ShapeSetEnd(i->tag);
2243 swf_ShapeFree(shape);
2245 msg("<trace> Placing image, shape ID %d, bitmap ID %d", myshapeid, bitid);
2246 i->tag = swf_InsertTag(i->tag,ST_PLACEOBJECT2);
2247 CXFORM cxform2 = gfxcxform_to_cxform(cxform);
2248 swf_ObjectPlace(i->tag,myshapeid,getNewDepth(dev),&i->page_matrix,&cxform2,NULL);
2251 static RGBA col_black = {255,0,0,0};
2253 static void drawoutline(gfxdevice_t*dev, gfxline_t*line)
2255 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
2257 int myshapeid = getNewID(dev);
2258 i->tag = swf_InsertTag(i->tag,ST_DEFINESHAPE3);
2261 swf_ShapeNew(&shape);
2262 int lsid = swf_ShapeAddLineStyle(shape,1,&col_black);
2264 swf_SetU16(i->tag,myshapeid);
2265 SRECT r = gfxline_getSWFbbox(line);
2266 r = swf_ClipRect(i->pagebbox, r);
2267 swf_SetRect(i->tag,&r);
2268 swf_SetShapeStyles(i->tag,shape);
2269 swf_ShapeCountBits(shape,NULL,NULL);
2270 swf_SetShapeBits(i->tag,shape);
2271 swf_ShapeSetAll(i->tag,shape,UNDEFINED_COORD,UNDEFINED_COORD,lsid,0,0);
2272 drawgfxline(dev, line, 1);
2273 swf_ShapeSetEnd(i->tag);
2274 swf_ShapeFree(shape);
2276 i->tag = swf_InsertTag(i->tag,ST_PLACEOBJECT2);
2277 swf_ObjectPlace(i->tag, myshapeid, getNewDepth(dev), 0,0,0);
2280 static void swf_startclip(gfxdevice_t*dev, gfxline_t*line)
2282 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
2287 if(i->clippos >= 127)
2289 msg("<warning> Too many clip levels.");
2293 if(i->config_showclipshapes)
2294 drawoutline(dev, line);
2296 int myshapeid = getNewID(dev);
2297 i->tag = swf_InsertTag(i->tag,ST_DEFINESHAPE3);
2299 memset(&col, 0, sizeof(RGBA));
2302 swf_ShapeNew(&shape);
2303 int fsid = swf_ShapeAddSolidFillStyle(shape,&col);
2305 RGBA markcol = {0,i->mark[0],i->mark[1],i->mark[2]};
2306 swf_ShapeAddSolidFillStyle(shape,&markcol);
2308 swf_SetU16(i->tag,myshapeid);
2309 SRECT r = gfxline_getSWFbbox(line);
2310 r = swf_ClipRect(i->pagebbox, r);
2311 swf_SetRect(i->tag,&r);
2312 swf_SetShapeStyles(i->tag,shape);
2313 swf_ShapeCountBits(shape,NULL,NULL);
2314 swf_SetShapeBits(i->tag,shape);
2315 swf_ShapeSetAll(i->tag,shape,UNDEFINED_COORD,UNDEFINED_COORD,0,fsid,0);
2316 i->swflastx = i->swflasty = UNDEFINED_COORD;
2317 i->shapeisempty = 1;
2318 drawgfxline(dev, line, 1);
2319 if(i->shapeisempty) {
2320 /* an empty clip shape is equivalent to a shape with no area */
2321 int x = line?line->x:0;
2322 int y = line?line->y:0;
2323 moveto(dev, i->tag, x,y);
2324 lineto(dev, i->tag, x,y);
2325 lineto(dev, i->tag, x,y);
2327 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)) {
2328 if(i->config_watermark) {
2329 gfxbbox_t r; r.xmin = r.ymin = 0;r.xmax = i->max_x;r.ymax = i->max_y;
2330 draw_watermark(dev, r, 1);
2333 swf_ShapeSetEnd(i->tag);
2334 swf_ShapeFree(shape);
2336 /* TODO: remember the bbox, and check all shapes against it */
2338 msg("<trace> Placing clip ID %d", myshapeid);
2339 i->tag = swf_InsertTag(i->tag,ST_PLACEOBJECT2);
2340 i->cliptags[i->clippos] = i->tag;
2341 i->clipshapes[i->clippos] = myshapeid;
2342 i->clipdepths[i->clippos] = getNewDepth(dev);
2346 static void swf_endclip(gfxdevice_t*dev)
2348 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
2355 msg("<error> Invalid end of clipping region");
2359 /*swf_ObjectPlaceClip(i->cliptags[i->clippos],i->clipshapes[i->clippos],i->clipdepths[i->clippos],&i->page_matrix,NULL,NULL,
2360 / * clip to depth: * / i->depth <= i->clipdepths[i->clippos]? i->depth : i->depth - 1);
2362 swf_ObjectPlaceClip(i->cliptags[i->clippos],i->clipshapes[i->clippos],i->clipdepths[i->clippos],&i->page_matrix,NULL,NULL,i->depth);
2364 static int gfxline_type(gfxline_t*line)
2370 int haszerosegments=0;
2373 if(line->type == gfx_moveTo) {
2376 } else if(line->type == gfx_lineTo) {
2380 } else if(line->type == gfx_splineTo) {
2382 if(tmpsplines>lines)
2390 if(lines==0 && splines==0) return 0;
2391 else if(lines==1 && splines==0) return 1;
2392 else if(lines==0 && splines==1) return 2;
2393 else if(splines==0) return 3;
2397 static int gfxline_has_dots(gfxline_t*line)
2405 if(line->type == gfx_moveTo) {
2406 /* test the length of the preceding line, and assume it is a dot if
2407 it's length is less than 1.0. But *only* if there's a noticable
2408 gap between the previous line and the next moveTo. (I've come
2409 across a PDF where thousands of "dots" were stringed together,
2411 int last_short_gap = short_gap;
2412 if((fabs(line->x - x) + fabs(line->y - y)) < 1.0) {
2417 if(isline && dist < 1 && !short_gap && !last_short_gap) {
2422 } else if(line->type == gfx_lineTo) {
2423 dist += fabs(line->x - x) + fabs(line->y - y);
2425 } else if(line->type == gfx_splineTo) {
2426 dist += fabs(line->sx - x) + fabs(line->sy - y) +
2427 fabs(line->x - line->sx) + fabs(line->y - line->sy);
2434 if(isline && dist < 1 && !short_gap) {
2440 static int gfxline_fix_short_edges(gfxline_t*line)
2444 if(line->type == gfx_lineTo) {
2445 if(fabs(line->x - x) + fabs(line->y - y) < 0.01) {
2448 } else if(line->type == gfx_splineTo) {
2449 if(fabs(line->sx - x) + fabs(line->sy - y) +
2450 fabs(line->x - line->sx) + fabs(line->y - line->sy) < 0.01) {
2461 static char is_inside_page(gfxdevice_t*dev, gfxcoord_t x, gfxcoord_t y)
2463 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
2464 if(x<i->min_x || x>i->max_x) return 0;
2465 if(y<i->min_y || y>i->max_y) return 0;
2469 gfxline_t* gfxline_move(gfxline_t*line, double x, double y)
2471 gfxline_t*l = line = gfxline_clone(line);
2483 //#define NORMALIZE_POLYGON_POSITIONS
2485 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)
2487 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
2488 if(line_is_empty(line))
2490 int type = gfxline_type(line);
2491 int has_dots = gfxline_has_dots(line);
2492 gfxbbox_t r = gfxline_getbbox(line);
2493 int is_outside_page = !is_inside_page(dev, r.xmin, r.ymin) || !is_inside_page(dev, r.xmax, r.ymax);
2495 /* TODO: * split line into segments, and perform this check for all segments */
2497 if(i->config_disable_polygon_conversion || /*type>=5 ||*/
2499 (width <= i->config_caplinewidth
2500 || (cap_style == gfx_capRound && joint_style == gfx_joinRound)
2501 || (cap_style == gfx_capRound && type<=2))))
2505 /* convert line to polygon */
2506 msg("<trace> draw as polygon, type=%d dots=%d", type, has_dots);
2508 gfxline_fix_short_edges(line);
2509 /* we need to convert the line into a polygon */
2510 gfxpoly_t* poly = gfxpoly_strokeToPoly(line, width, cap_style, joint_style, miterLimit);
2511 gfxline_t*gfxline = gfxpoly_to_gfxline(poly);
2512 dev->fill(dev, gfxline, color);
2513 gfxline_free(gfxline);
2518 msg("<trace> draw as stroke, type=%d dots=%d", type, has_dots);
2521 if(i->config_normalize_polygon_positions) {
2523 double startx = 0, starty = 0;
2524 if(line && line->type == gfx_moveTo) {
2528 line = gfxline_move(line, -startx, -starty);
2529 i->shapeposx = (int)(startx*20);
2530 i->shapeposy = (int)(starty*20);
2533 swfoutput_setstrokecolor(dev, color->r, color->g, color->b, color->a);
2534 swfoutput_setlinewidth(dev, width);
2537 drawgfxline(dev, line, 0);
2539 if(i->config_normalize_polygon_positions) {
2540 free(line); //account for _move
2545 static void swf_fill(gfxdevice_t*dev, gfxline_t*line, gfxcolor_t*color)
2547 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
2548 if(line_is_empty(line))
2552 gfxbbox_t r = gfxline_getbbox(line);
2553 int is_outside_page = !is_inside_page(dev, r.xmin, r.ymin) || !is_inside_page(dev, r.xmax, r.ymax);
2555 //if(i->chardatapos && i->chardata[i->chardatapos-1].color.a) {
2558 if(!i->config_ignoredraworder)
2561 if(i->config_normalize_polygon_positions) {
2563 double startx = 0, starty = 0;
2564 if(line && line->type == gfx_moveTo) {
2568 line = gfxline_move(line, -startx, -starty);
2569 i->shapeposx = (int)(startx*20);
2570 i->shapeposy = (int)(starty*20);
2573 swfoutput_setfillcolor(dev, color->r, color->g, color->b, color->a);
2576 drawgfxline(dev, line, 1);
2578 if(i->currentswfid==2 && r.xmin==0 && r.ymin==0 && r.xmax==i->max_x && r.ymax==i->max_y) {
2579 if(i->config_watermark) {
2580 draw_watermark(dev, r, 1);
2584 msg("<trace> end of swf_fill (shapeid=%d)", i->shapeid);
2586 if(i->config_normalize_polygon_positions) {
2587 free(line); //account for _move
2591 static GRADIENT* gfxgradient_to_GRADIENT(gfxgradient_t*gradient)
2594 gfxgradient_t*g = gradient;
2599 GRADIENT* swfgradient = malloc(sizeof(GRADIENT));
2600 swfgradient->num = num;
2601 swfgradient->rgba = malloc(sizeof(swfgradient->rgba[0])*num);
2602 swfgradient->ratios = malloc(sizeof(swfgradient->ratios[0])*num);
2607 swfgradient->ratios[num] = g->pos*255;
2608 swfgradient->rgba[num] = *(RGBA*)&g->color;
2615 static void swf_fillgradient(gfxdevice_t*dev, gfxline_t*line, gfxgradient_t*gradient, gfxgradienttype_t type, gfxmatrix_t*matrix)
2617 if(line_is_empty(line))
2619 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
2621 if(line_is_empty(line))
2624 GRADIENT* swfgradient = gfxgradient_to_GRADIENT(gradient);
2631 double f = type==gfxgradient_radial?4:4;
2633 m.sx = (int)(matrix->m00*20*f); m.r1 = (int)(matrix->m10*20*f);
2634 m.r0 = (int)(matrix->m01*20*f); m.sy = (int)(matrix->m11*20*f);
2635 m.tx = (int)(matrix->tx*20);
2636 m.ty = (int)(matrix->ty*20);
2639 int myshapeid = getNewID(dev);
2640 i->tag = swf_InsertTag(i->tag, ST_DEFINESHAPE2);
2642 swf_ShapeNew(&shape);
2643 int fsid = swf_ShapeAddGradientFillStyle(shape,&m,swfgradient,type==gfxgradient_radial);
2644 swf_SetU16(i->tag, myshapeid);
2645 SRECT r = gfxline_getSWFbbox(line);
2646 r = swf_ClipRect(i->pagebbox, r);
2647 swf_SetRect(i->tag,&r);
2648 swf_SetShapeStyles(i->tag,shape);
2649 swf_ShapeCountBits(shape,NULL,NULL);
2650 swf_SetShapeBits(i->tag,shape);
2651 swf_ShapeSetAll(i->tag,shape,UNDEFINED_COORD,UNDEFINED_COORD,0,fsid,0);
2652 i->swflastx = i->swflasty = UNDEFINED_COORD;
2653 drawgfxline(dev, line, 1);
2654 swf_ShapeSetEnd(i->tag);
2655 swf_ShapeFree(shape);
2657 int depth = getNewDepth(dev);
2658 msg("<trace> Placing gradient, shape ID %d, depth %d", myshapeid, depth);
2659 i->tag = swf_InsertTag(i->tag,ST_PLACEOBJECT2);
2660 swf_ObjectPlace(i->tag,myshapeid,depth,&i->page_matrix,NULL,NULL);
2662 swf_FreeGradient(swfgradient);free(swfgradient);
2665 static SWFFONT* gfxfont_to_swffont(gfxfont_t*font, const char* id)
2667 SWFFONT*swffont = (SWFFONT*)rfx_calloc(sizeof(SWFFONT));
2669 SRECT bounds = {0,0,0,0};
2671 swffont->version = 2;
2672 swffont->name = (U8*)strdup(id);
2673 swffont->layout = (SWFLAYOUT*)rfx_calloc(sizeof(SWFLAYOUT));
2674 swffont->layout->ascent = 0;
2675 swffont->layout->descent = 0;
2676 swffont->layout->leading = 0;
2677 swffont->layout->bounds = (SRECT*)rfx_calloc(sizeof(SRECT)*font->num_glyphs);
2678 swffont->encoding = FONT_ENCODING_UNICODE;
2679 swffont->numchars = font->num_glyphs;
2680 swffont->maxascii = font->max_unicode;
2681 swffont->ascii2glyph = (int*)rfx_calloc(sizeof(int)*swffont->maxascii);
2682 swffont->glyph2ascii = (U16*)rfx_calloc(sizeof(U16)*swffont->numchars);
2683 swffont->glyph = (SWFGLYPH*)rfx_calloc(sizeof(SWFGLYPH)*swffont->numchars);
2684 swffont->glyphnames = (char**)rfx_calloc(sizeof(char*)*swffont->numchars);
2685 for(t=0;t<font->max_unicode;t++) {
2686 swffont->ascii2glyph[t] = font->unicode2glyph[t];
2688 SRECT max = {0,0,0,0};
2689 for(t=0;t<font->num_glyphs;t++) {
2693 swffont->glyph2ascii[t] = font->glyphs[t].unicode;
2694 if(swffont->glyph2ascii[t] == 0xffff || swffont->glyph2ascii[t] == 0x0000) {
2695 /* flash 8 flashtype requires unique unicode IDs for each character.
2696 We use the Unicode private user area to assign characters, hoping that
2697 the font doesn't contain more than 2048 glyphs */
2698 swffont->glyph2ascii[t] = 0xe000 + (t&0x1fff);
2701 if(font->glyphs[t].name) {
2702 swffont->glyphnames[t] = strdup(font->glyphs[t].name);
2704 swffont->glyphnames[t] = 0;
2706 advance = font->glyphs[t].advance;
2708 swf_Shape01DrawerInit(&draw, 0);
2709 line = font->glyphs[t].line;
2712 c.x = line->sx * GLYPH_SCALE; c.y = line->sy * GLYPH_SCALE;
2713 to.x = line->x * GLYPH_SCALE; to.y = line->y * GLYPH_SCALE;
2714 if(line->type == gfx_moveTo) {
2715 draw.moveTo(&draw, &to);
2716 } else if(line->type == gfx_lineTo) {
2717 draw.lineTo(&draw, &to);
2718 } else if(line->type == gfx_splineTo) {
2719 draw.splineTo(&draw, &c, &to);
2724 swffont->glyph[t].shape = swf_ShapeDrawerToShape(&draw);
2726 SRECT bbox = swf_ShapeDrawerGetBBox(&draw);
2727 swf_ExpandRect2(&max, &bbox);
2729 swffont->layout->bounds[t] = bbox;
2731 if(advance<32768.0/20) {
2732 swffont->glyph[t].advance = (int)(advance*20);
2734 //msg("<warning> Advance value overflow in glyph %d", t);
2735 swffont->glyph[t].advance = 32767;
2738 draw.dealloc(&draw);
2740 swf_ExpandRect2(&bounds, &swffont->layout->bounds[t]);
2742 for(t=0;t<font->num_glyphs;t++) {
2743 SRECT bbox = swffont->layout->bounds[t];
2745 /* if the glyph doesn't have a bounding box, use the
2746 combined bounding box (necessary e.g. for space characters) */
2747 if(!(bbox.xmin|bbox.ymin|bbox.xmax|bbox.ymax)) {
2748 swffont->layout->bounds[t] = bbox = max;
2751 /* check that the advance value is reasonable, by comparing it
2752 with the bounding box */
2753 if(bbox.xmax>0 && (bbox.xmax*10 < swffont->glyph[t].advance || !swffont->glyph[t].advance)) {
2754 if(swffont->glyph[t].advance)
2755 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);
2756 swffont->glyph[t].advance = bbox.xmax;
2758 //swffont->glyph[t].advance = bbox.xmax - bbox.xmin;
2762 /* Flash player will use the advance value from the char, and the ascent/descent values
2763 from the layout for text selection.
2764 ascent will extend the char into negative y direction, from the baseline, while descent
2765 will extend in positive y direction, also from the baseline.
2766 The baseline is defined as the y-position zero
2769 swffont->layout->ascent = -bounds.ymin;
2770 if(swffont->layout->ascent < 0)
2771 swffont->layout->ascent = 0;
2772 swffont->layout->descent = bounds.ymax;
2773 if(swffont->layout->descent < 0)
2774 swffont->layout->descent = 0;
2775 swffont->layout->leading = bounds.ymax - bounds.ymin;
2777 /* if the font has proper ascent/descent values (>0) and those define
2778 greater line spacing that what we estimated from the bounding boxes,
2779 use the font's parameters */
2780 if(font->ascent*20 > swffont->layout->ascent)
2781 swffont->layout->ascent = font->ascent*20;
2782 if(font->descent*20 > swffont->layout->descent)
2783 swffont->layout->descent = font->descent*20;
2788 static void swf_addfont(gfxdevice_t*dev, gfxfont_t*font)
2790 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
2792 if(i->swffont && i->swffont->name && !strcmp((char*)i->swffont->name,font->id))
2793 return; // the requested font is the current font
2795 fontlist_t*last=0,*l = i->fontlist;
2798 if(!strcmp((char*)l->swffont->name, font->id)) {
2799 return; // we already know this font
2803 l = (fontlist_t*)rfx_calloc(sizeof(fontlist_t));
2804 l->swffont = gfxfont_to_swffont(font, font->id);
2811 swf_FontSetID(l->swffont, getNewID(i->dev));
2813 if(getScreenLogLevel() >= LOGLEVEL_DEBUG) {
2815 // print font information
2816 msg("<debug> Font %s",font->id);
2817 msg("<debug> | ID: %d", l->swffont->id);
2818 msg("<debug> | Version: %d", l->swffont->version);
2819 msg("<debug> | Name: %s", l->swffont->name);
2820 msg("<debug> | Numchars: %d", l->swffont->numchars);
2821 msg("<debug> | Maxascii: %d", l->swffont->maxascii);
2822 msg("<debug> | Style: %d", l->swffont->style);
2823 msg("<debug> | Encoding: %d", l->swffont->encoding);
2824 for(iii=0; iii<l->swffont->numchars;iii++) {
2825 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,
2826 l->swffont->layout->bounds[iii].xmin/20.0,
2827 l->swffont->layout->bounds[iii].ymin/20.0,
2828 l->swffont->layout->bounds[iii].xmax/20.0,
2829 l->swffont->layout->bounds[iii].ymax/20.0
2832 for(t=0;t<l->swffont->maxascii;t++) {
2833 if(l->swffont->ascii2glyph[t] == iii)
2834 msg("<debug> | - maps to %d",t);
2840 static void swf_switchfont(gfxdevice_t*dev, const char*fontid)
2842 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
2844 if(i->swffont && i->swffont->name && !strcmp((char*)i->swffont->name,fontid))
2845 return; // the requested font is the current font
2847 fontlist_t*l = i->fontlist;
2849 if(!strcmp((char*)l->swffont->name, fontid)) {
2850 i->swffont = l->swffont;
2855 msg("<error> Unknown font id: %s", fontid);
2859 static void swf_drawchar(gfxdevice_t*dev, gfxfont_t*font, int glyph, gfxcolor_t*color, gfxmatrix_t*matrix)
2861 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
2863 msg("<error> swf_drawchar called (glyph %d) without font", glyph);
2867 if(i->config_drawonlyshapes) {
2868 gfxglyph_t*g = &font->glyphs[glyph];
2869 gfxline_t*line2 = gfxline_clone(g->line);
2870 gfxline_transform(line2, matrix);
2871 dev->fill(dev, line2, color);
2872 gfxline_free(line2);
2876 if(!i->swffont || !i->swffont->name || strcmp((char*)i->swffont->name,font->id)) // not equal to current font
2878 /* TODO: remove the need for this (enhance getcharacterbbox so that it can cope
2879 with multiple fonts */
2881 swf_switchfont(dev, font->id); // set the current font
2884 msg("<warning> swf_drawchar: Font is NULL");
2887 if(glyph<0 || glyph>=i->swffont->numchars) {
2888 msg("<warning> No character %d in font %s (%d chars)", glyph, FIXNULL((char*)i->swffont->name), i->swffont->numchars);
2892 setfontscale(dev, matrix->m00, matrix->m01, matrix->m10, matrix->m11, matrix->tx, matrix->ty, 0);
2894 double det = i->fontmatrix.sx/65536.0 * i->fontmatrix.sy/65536.0 -
2895 i->fontmatrix.r0/65536.0 * i->fontmatrix.r1/65536.0;
2896 if(fabs(det) < 0.0005) {
2897 /* x direction equals y direction- the text is invisible */
2898 msg("<verbose> Not drawing invisible character character %d (det=%f, m=[%f %f;%f %f]\n", glyph,
2900 i->fontmatrix.sx/65536.0, i->fontmatrix.r1/65536.0,
2901 i->fontmatrix.r0/65536.0, i->fontmatrix.sy/65536.0);
2905 /*if(i->swffont->glyph[glyph].shape->bitlen <= 16) {
2906 msg("<warning> Glyph %d in current charset (%s, %d characters) is empty",
2907 glyph, FIXNULL((char*)i->swffont->name), i->swffont->numchars);
2911 /* calculate character position with respect to the current font matrix */
2912 double s = 20 * GLYPH_SCALE / det;
2913 double px = matrix->tx - i->fontmatrix.tx/20.0;
2914 double py = matrix->ty - i->fontmatrix.ty/20.0;
2915 int x = (SCOORD)(( px * i->fontmatrix.sy/65536.0 - py * i->fontmatrix.r1/65536.0)*s);
2916 int y = (SCOORD)((- px * i->fontmatrix.r0/65536.0 + py * i->fontmatrix.sx/65536.0)*s);
2917 if(x>32767 || x<-32768 || y>32767 || y<-32768) {
2918 msg("<verbose> Moving character origin to %f %f\n", matrix->tx, matrix->ty);
2920 setfontscale(dev, matrix->m00, matrix->m01, matrix->m10, matrix->m11, matrix->tx, matrix->ty, 1);
2921 /* since we just moved the char origin to the current char's position,
2922 it now has the relative position (0,0) */
2931 msg("<trace> Drawing char %d in font %d at %d,%d in color %02x%02x%02x%02x",
2932 glyph, i->swffont->id, x, y, color->r, color->g, color->b, color->a);
2934 putcharacter(dev, i->swffont->id, glyph, x, y, i->current_font_size, *(RGBA*)color);
2935 swf_FontUseGlyph(i->swffont, glyph);