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 #include "../art/libart.h"
45 #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;
79 int config_simpleviewer;
80 int config_opennewwindow;
81 int config_ignoredraworder;
82 int config_drawonlyshapes;
83 int config_linknameurl;
84 int config_jpegquality;
85 int config_storeallcharacters;
86 int config_enablezlib;
87 int config_insertstoptag;
89 int config_flashversion;
90 int config_reordertags;
91 int config_showclipshapes;
92 int config_splinemaxerror;
93 int config_fontsplinemaxerror;
94 int config_filloverlap;
97 int config_disable_polygon_conversion;
98 int config_normalize_polygon_positions;
99 RGBA config_linkcolor;
100 float config_minlinewidth;
101 double config_caplinewidth;
102 char* config_linktarget;
103 char*config_internallinkfunction;
104 char*config_externallinkfunction;
106 double config_framerate;
110 fontlist_t* fontlist;
147 int pic_height[1024];
153 char fillstylechanged;
155 int jpeg; //next image type
162 chardata_t chardata[CHARDATAMAX];
169 int current_font_size;
171 double lastfontm11,lastfontm12,lastfontm21,lastfontm22;
182 } swfoutput_internal;
184 static void swf_fillbitmap(gfxdevice_t*driver, gfxline_t*line, gfximage_t*img, gfxmatrix_t*move, gfxcxform_t*cxform);
185 static int swf_setparameter(gfxdevice_t*driver, const char*key, const char*value);
186 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);
187 static void swf_startclip(gfxdevice_t*dev, gfxline_t*line);
188 static void swf_endclip(gfxdevice_t*dev);
189 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);
190 static void swf_fill(gfxdevice_t*dev, gfxline_t*line, gfxcolor_t*color);
191 static void swf_fillbitmap(gfxdevice_t*dev, gfxline_t*line, gfximage_t*img, gfxmatrix_t*matrix, gfxcxform_t*cxform);
192 static void swf_fillgradient(gfxdevice_t*dev, gfxline_t*line, gfxgradient_t*gradient, gfxgradienttype_t type, gfxmatrix_t*matrix);
193 static void swf_drawchar(gfxdevice_t*dev, gfxfont_t*font, int glyph, gfxcolor_t*color, gfxmatrix_t*matrix);
194 static void swf_addfont(gfxdevice_t*dev, gfxfont_t*font);
195 static void swf_drawlink(gfxdevice_t*dev, gfxline_t*line, const char*action);
196 static void swf_startframe(gfxdevice_t*dev, int width, int height);
197 static void swf_endframe(gfxdevice_t*dev);
198 static gfxresult_t* swf_finish(gfxdevice_t*driver);
200 static swfoutput_internal* init_internal_struct()
202 swfoutput_internal*i = (swfoutput_internal*)malloc(sizeof(swfoutput_internal));
203 memset(i, 0, sizeof(swfoutput_internal));
227 i->fillstylechanged = 0;
234 i->config_dumpfonts=0;
235 i->config_ppmsubpixels=0;
236 i->config_jpegsubpixels=0;
237 i->config_opennewwindow=1;
238 i->config_ignoredraworder=0;
239 i->config_drawonlyshapes=0;
240 i->config_jpegquality=85;
241 i->config_storeallcharacters=0;
242 i->config_enablezlib=0;
243 i->config_insertstoptag=0;
244 i->config_flashversion=6;
245 i->config_framerate=0.25;
246 i->config_splinemaxerror=1;
247 i->config_fontsplinemaxerror=1;
248 i->config_filloverlap=0;
250 i->config_bboxvars=0;
251 i->config_showclipshapes=0;
252 i->config_minlinewidth=0.05;
253 i->config_caplinewidth=1;
254 i->config_linktarget=0;
255 i->config_internallinkfunction=0;
256 i->config_externallinkfunction=0;
257 i->config_reordertags=1;
258 i->config_linknameurl=1;
260 i->config_linkcolor.r = i->config_linkcolor.g = i->config_linkcolor.b = 255;
261 i->config_linkcolor.a = 0x40;
266 static int id_error = 0;
268 static U16 getNewID(gfxdevice_t* dev)
270 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
271 if(i->currentswfid == 65535) {
273 msg("<error> ID Table overflow");
274 msg("<error> This file is too complex to render- SWF only supports 65536 shapes at once");
280 return ++i->currentswfid;
282 static U16 getNewDepth(gfxdevice_t* dev)
284 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
285 if(i->depth == 65520) {
287 msg("<error> Depth Table overflow");
288 msg("<error> This file is too complex to render- SWF only supports 65536 shapes at once");
297 static void startshape(gfxdevice_t* dev);
298 static void starttext(gfxdevice_t* dev);
299 static void endshape(gfxdevice_t* dev);
300 static void endtext(gfxdevice_t* dev);
302 typedef struct _plotxy
307 // write a move-to command into the swf
308 static int movetoxy(gfxdevice_t*dev, TAG*tag, plotxy_t p0)
310 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
311 int rx = (int)(p0.x*20);
312 int ry = (int)(p0.y*20);
313 if(rx!=i->swflastx || ry!=i->swflasty || i->fillstylechanged) {
314 swf_ShapeSetMove (tag, i->shape, rx,ry);
315 i->fillstylechanged = 0;
322 static int moveto(gfxdevice_t*dev, TAG*tag, double x, double y)
324 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
328 return movetoxy(dev, tag, p);
330 static void addPointToBBox(gfxdevice_t*dev, int px, int py)
332 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
338 swf_ExpandRect(&i->bboxrect, p);
340 swf_ExpandRect3(&i->bboxrect, p, i->linewidth*3/2);
344 /*static void plot(gfxdevice_t*dev, int x, int y, TAG*tag)
346 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
347 int width = i->linewidth/4;
351 //swf_ShapeSetLine(tag, i->shape,-width,-width);
352 //swf_ShapeSetLine(tag, i->shape,width*2,0);
353 //swf_ShapeSetLine(tag, i->shape,0,width*2);
354 //swf_ShapeSetLine(tag, i->shape,-width*2,0);
355 //swf_ShapeSetLine(tag, i->shape,0,-width*2);
356 //swf_ShapeSetLine(tag, i->shape,width,width);
359 swf_ShapeSetLine(tag, i->shape,-width,0);
360 swf_ShapeSetLine(tag, i->shape,width,-width);
361 swf_ShapeSetLine(tag, i->shape,width,width);
362 swf_ShapeSetLine(tag, i->shape,-width,width);
363 swf_ShapeSetLine(tag, i->shape,-width,-width);
364 swf_ShapeSetLine(tag, i->shape,width,0);
366 addPointToBBox(dev, x-width ,y-width);
367 addPointToBBox(dev, x+width ,y+width);
370 // write a line-to command into the swf
371 static void linetoxy(gfxdevice_t*dev, TAG*tag, plotxy_t p0)
373 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
374 int px = (int)(p0.x*20);
375 int py = (int)(p0.y*20);
376 int rx = (px-i->swflastx);
377 int ry = (py-i->swflasty);
379 swf_ShapeSetLine (tag, i->shape, rx,ry);
380 addPointToBBox(dev, i->swflastx,i->swflasty);
381 addPointToBBox(dev, px,py);
382 }/* else if(!i->fill) {
383 // treat lines of length 0 as plots, making them
384 // at least 1 twip wide so Flash will display them
385 plot(dev, i->swflastx, i->swflasty, tag);
392 static void lineto(gfxdevice_t*dev, TAG*tag, double x, double y)
397 linetoxy(dev,tag, p);
400 // write a spline-to command into the swf
401 static void splineto(gfxdevice_t*dev, TAG*tag, plotxy_t control,plotxy_t end)
403 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
404 int lastlastx = i->swflastx;
405 int lastlasty = i->swflasty;
407 int cx = ((int)(control.x*20)-i->swflastx);
408 int cy = ((int)(control.y*20)-i->swflasty);
411 int ex = ((int)(end.x*20)-i->swflastx);
412 int ey = ((int)(end.y*20)-i->swflasty);
416 if((cx || cy) && (ex || ey)) {
417 swf_ShapeSetCurve(tag, i->shape, cx,cy,ex,ey);
418 addPointToBBox(dev, lastlastx ,lastlasty );
419 addPointToBBox(dev, lastlastx+cx,lastlasty+cy);
420 addPointToBBox(dev, lastlastx+cx+ex,lastlasty+cy+ey);
421 } else if(cx || cy || ex || ey) {
422 swf_ShapeSetLine(tag, i->shape, cx+ex,cy+ey);
423 addPointToBBox(dev, lastlastx ,lastlasty );
424 addPointToBBox(dev, lastlastx+cx,lastlasty+cy);
425 addPointToBBox(dev, lastlastx+cx+ex,lastlasty+cy+ey);
431 /* write a line, given two points and the transformation
433 /*static void line(gfxdevice_t*dev, TAG*tag, plotxy_t p0, plotxy_t p1)
435 moveto(dev, tag, p0);
436 lineto(dev, tag, p1);
439 void resetdrawer(gfxdevice_t*dev)
441 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
446 static void stopFill(gfxdevice_t*dev)
448 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
449 if(i->lastwasfill!=0)
451 swf_ShapeSetStyle(i->tag,i->shape,i->linestyleid,0x8000,0);
452 i->fillstylechanged = 1;
456 static void startFill(gfxdevice_t*dev)
458 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
459 if(i->lastwasfill!=1)
461 swf_ShapeSetStyle(i->tag,i->shape,0x8000,i->fillstyleid,0);
462 i->fillstylechanged = 1;
467 static inline int colorcompare(gfxdevice_t*dev, RGBA*a,RGBA*b)
479 static SRECT getcharacterbbox(gfxdevice_t*dev, SWFFONT*font, MATRIX* m)
481 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
484 memset(&r, 0, sizeof(r));
487 if(debug) printf("\n");
488 for(t=0;t<i->chardatapos;t++)
490 if(i->chardata[t].fontid != font->id) {
491 msg("<error> Internal error: fontid %d != fontid %d", i->chardata[t].fontid, font->id);
494 SRECT b = font->layout->bounds[i->chardata[t].charid];
495 b.xmin *= i->chardata[t].size;
496 b.ymin *= i->chardata[t].size;
497 b.xmax *= i->chardata[t].size;
498 b.ymax *= i->chardata[t].size;
500 /* divide by 1024, rounding xmax/ymax up */
501 b.xmax += 1023; b.ymax += 1023; b.xmin /= 1024; b.ymin /= 1024; b.xmax /= 1024; b.ymax /= 1024;
503 b.xmin += i->chardata[t].x;
504 b.ymin += i->chardata[t].y;
505 b.xmax += i->chardata[t].x;
506 b.ymax += i->chardata[t].y;
508 /* until we solve the INTERNAL_SCALING problem (see below)
509 make sure the bounding box is big enough */
515 b = swf_TurnRect(b, m);
517 if(debug) printf("(%f,%f,%f,%f) -> (%f,%f,%f,%f) [font %d/%d, char %d]\n",
518 font->layout->bounds[i->chardata[t].charid].xmin/20.0,
519 font->layout->bounds[i->chardata[t].charid].ymin/20.0,
520 font->layout->bounds[i->chardata[t].charid].xmax/20.0,
521 font->layout->bounds[i->chardata[t].charid].ymax/20.0,
526 i->chardata[t].fontid,
528 i->chardata[t].charid
530 swf_ExpandRect2(&r, &b);
532 if(debug) printf("-----> (%f,%f,%f,%f)\n",
540 static void putcharacters(gfxdevice_t*dev, TAG*tag)
542 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
546 color.r = i->chardata[0].color.r^255;
555 int charadvance[128];
558 int glyphbits=1; //TODO: can this be zero?
561 if(tag->id != ST_DEFINETEXT &&
562 tag->id != ST_DEFINETEXT2) {
563 msg("<error> internal error: putcharacters needs an text tag, not %d\n",tag->id);
566 if(!i->chardatapos) {
567 msg("<warning> putcharacters called with zero characters");
570 for(pass = 0; pass < 2; pass++)
580 advancebits++; // add sign bit
581 swf_SetU8(tag, glyphbits);
582 swf_SetU8(tag, advancebits);
585 for(t=0;t<=i->chardatapos;t++)
587 if(lastfontid != i->chardata[t].fontid ||
588 lastx!=i->chardata[t].x ||
589 lasty!=i->chardata[t].y ||
590 !colorcompare(dev,&color, &i->chardata[t].color) ||
592 lastsize != i->chardata[t].size ||
595 if(charstorepos && pass==0)
598 for(s=0;s<charstorepos;s++)
600 while(charids[s]>=(1<<glyphbits))
602 while(charadvance[s]>=(1<<advancebits))
606 if(charstorepos && pass==1)
608 tag->writeBit = 0; // Q&D
609 swf_SetBits(tag, 0, 1); // GLYPH Record
610 swf_SetBits(tag, charstorepos, 7); // number of glyphs
612 for(s=0;s<charstorepos;s++)
614 swf_SetBits(tag, charids[s], glyphbits);
615 swf_SetBits(tag, charadvance[s], advancebits);
620 if(pass == 1 && t<i->chardatapos)
626 if(lastx != i->chardata[t].x ||
627 lasty != i->chardata[t].y)
629 newx = i->chardata[t].x;
630 newy = i->chardata[t].y;
636 if(!colorcompare(dev,&color, &i->chardata[t].color))
638 color = i->chardata[t].color;
641 font.id = i->chardata[t].fontid;
642 if(lastfontid != i->chardata[t].fontid || lastsize != i->chardata[t].size)
645 tag->writeBit = 0; // Q&D
646 swf_TextSetInfoRecord(tag, newfont, i->chardata[t].size, newcolor, newx,newy);
649 lastfontid = i->chardata[t].fontid;
650 lastx = i->chardata[t].x;
651 lasty = i->chardata[t].y;
652 lastsize = i->chardata[t].size;
655 if(t==i->chardatapos)
659 int nextt = t==i->chardatapos-1?t:t+1;
660 int rel = i->chardata[nextt].x-i->chardata[t].x;
661 if(rel>=0 && (rel<(1<<(advancebits-1)) || pass==0)) {
663 lastx=i->chardata[nextt].x;
667 lastx=i->chardata[t].x;
669 charids[charstorepos] = i->chardata[t].charid;
670 charadvance[charstorepos] = advance;
677 static void putcharacter(gfxdevice_t*dev, int fontid, int charid, int x,int y, int size, RGBA color)
679 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
680 if(i->chardatapos == CHARDATAMAX)
682 msg("<warning> Character buffer too small. SWF will be slightly bigger");
686 i->chardata[i->chardatapos].fontid = fontid;
687 i->chardata[i->chardatapos].charid = charid;
688 i->chardata[i->chardatapos].x = x;
689 i->chardata[i->chardatapos].y = y;
690 i->chardata[i->chardatapos].color = color;
691 i->chardata[i->chardatapos].size = size;
695 /* Notice: we can only put chars in the range -1639,1638 (-32768/20,32768/20).
696 So if we set this value to high, the char coordinates will overflow.
697 If we set it to low, however, the char positions will be inaccurate */
698 #define GLYPH_SCALE 1
700 static void endtext(gfxdevice_t*dev)
702 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
706 i->tag = swf_InsertTag(i->tag,ST_DEFINETEXT2);
707 swf_SetU16(i->tag, i->textid);
710 r = getcharacterbbox(dev, i->swffont, &i->fontmatrix);
711 r = swf_ClipRect(i->pagebbox, r);
712 swf_SetRect(i->tag,&r);
714 swf_SetMatrix(i->tag,&i->fontmatrix);
716 msg("<trace> Placing text (%d characters) as ID %d", i->chardatapos, i->textid);
718 putcharacters(dev, i->tag);
721 if(i->swf->fileVersion >= 8) {
722 i->tag = swf_InsertTag(i->tag, ST_CSMTEXTSETTINGS);
723 swf_SetU16(i->tag, i->textid);
725 //swf_SetU8(i->tag, /*subpixel grid*/(2<<3)|/*flashtype*/0x40);
726 swf_SetU8(i->tag, /*grid*/(1<<3)|/*flashtype*/0x40);
727 //swf_SetU8(i->tag, /*no grid*/(0<<3)|/*flashtype*/0x40);
729 swf_SetU32(i->tag, 0);//thickness
730 swf_SetU32(i->tag, 0);//sharpness
731 swf_SetU8(i->tag, 0);//reserved
733 i->tag = swf_InsertTag(i->tag,ST_PLACEOBJECT2);
735 swf_ObjectPlace(i->tag,i->textid,getNewDepth(dev),&i->page_matrix,NULL,NULL);
739 /* set's the matrix which is to be applied to characters drawn by swfoutput_drawchar() */
740 static void setfontscale(gfxdevice_t*dev,double m11,double m12, double m21,double m22,double x, double y, char force)
746 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
747 if(i->lastfontm11 == m11 &&
748 i->lastfontm12 == m12 &&
749 i->lastfontm21 == m21 &&
750 i->lastfontm22 == m22 && !force)
755 i->lastfontm11 = m11;
756 i->lastfontm12 = m12;
757 i->lastfontm21 = m21;
758 i->lastfontm22 = m22;
760 double xsize = sqrt(m11*m11 + m12*m12);
761 double ysize = sqrt(m21*m21 + m22*m22);
762 i->current_font_size = (xsize>ysize?xsize:ysize)*1;
763 if(i->current_font_size < 1)
764 i->current_font_size = 1;
765 double ifs = 1.0 / (i->current_font_size*GLYPH_SCALE);
768 m.sx = (S32)((m11*ifs)*65536); m.r1 = (S32)((m21*ifs)*65536);
769 m.r0 = (S32)((m12*ifs)*65536); m.sy = (S32)((m22*ifs)*65536);
770 /* this is the position of the first char to set a new fontmatrix-
771 we hope that it's close enough to all other characters using the
772 font, so we use its position as origin for the matrix */
778 static int watermark2_width=47;
779 static int watermark2_height=11;
780 static int watermark2[47] = {95,1989,71,0,2015,337,1678,0,2015,5,1921,320,1938,25,2006,1024,
781 1042,21,13,960,1039,976,8,2000,1359,1088,31,1989,321,1728,0,1152,
782 1344,832,0,1984,0,896,1088,1088,896,0,1984,128,256,512,1984};
784 static void draw_watermark(gfxdevice_t*dev, gfxbbox_t r, char drawall)
786 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
787 double wx = r.xmax / 5.0;
788 double tx = r.xmax*4.0 / 5.0;
789 double ty = r.ymax-wx*watermark2_height/watermark2_width;
790 double sx = (r.xmax - tx) / watermark2_width;
791 double sy = (r.ymax - ty) / watermark2_height;
794 if(ty > 0 && px > 1.0 && py > 1.0) {
796 for(y=0;y<watermark2_height;y++)
797 for(x=0;x<watermark2_width;x++) {
798 if(((watermark2[x]>>y)&1)) {
799 if(!drawall && rand()%5)
801 unsigned int b = rand();
802 moveto(dev, i->tag, x*sx+tx+((b>>1)&1)/20.0, y*sy+ty+((b>>3)&1)/20.0);
803 lineto(dev, i->tag, x*sx+px+tx+((b>>2)&1)/20.0, y*sy+ty+((b>>3)&1)/20.0);
804 lineto(dev, i->tag, x*sx+px+tx+((b>>2)&1)/20.0, y*sy+py+ty+((b>>4)&1)/20.0);
805 lineto(dev, i->tag, x*sx+tx+((b>>1)&1)/20.0, y*sy+py+ty+((b>>4)&1)/20.0);
806 lineto(dev, i->tag, x*sx+tx+((b>>1)&1)/20.0, y*sy+ty+((b>>3)&1)/20.0);
812 static void swfoutput_setfillcolor(gfxdevice_t* dev, U8 r, U8 g, U8 b, U8 a)
814 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
815 if(i->fillrgb.r == r &&
818 i->fillrgb.a == a) return;
827 static void insert_watermark(gfxdevice_t*dev, char drawall)
829 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
830 if(!drawall && i->watermarks>20)
836 swfoutput_setfillcolor(dev, 0,0,255,192);
838 swfoutput_setfillcolor(dev, rand(),rand(),rand(),(rand()&127)+128);
843 gfxbbox_t r; r.xmin = r.ymin = 0;
846 draw_watermark(dev, r, drawall);
852 static void endpage(gfxdevice_t*dev)
854 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
865 if(i->config_watermark) {
866 insert_watermark(dev, 1);
872 static void addViewer(gfxdevice_t* dev)
874 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
877 RGBA button_colors[3]= {{0xbf,0x00,0x00,0x80},{0xbf,0x20,0x20,0xc0}, {0xbf,0xc0,0xc0,0xff}};
879 int button_sizex = 20;
880 int button_sizey = 20;
882 RGBA black = {255,0,0,0};
884 i->tag = swf_InsertTag(i->tag,ST_DEFINESHAPE3);
886 int ls1 = swf_ShapeAddLineStyle(s,40,&black);
887 int fs1 = swf_ShapeAddSolidFillStyle(s,&button_colors[t/2]);
888 int shapeid = ids[t] = getNewID(dev);
889 swf_SetU16(i->tag,shapeid);
891 r.xmin = -20*button_sizex;
892 r.xmax = 20*button_sizex;
894 r.ymax = 40*button_sizey;
895 swf_SetRect(i->tag,&r); // set shape bounds
896 swf_SetShapeHeader(i->tag,s); // write all styles to tag
897 swf_ShapeSetAll(i->tag,s,0*button_sizex,0,ls1,fs1,0);
898 swf_ShapeSetLine(i->tag,s,(1-(t&1)*2)*20*button_sizex,20*button_sizey);
899 swf_ShapeSetLine(i->tag,s,-(1-(t&1)*2)*20*button_sizex,20*button_sizey);
900 swf_ShapeSetLine(i->tag,s,0,-40*button_sizey);
901 swf_ShapeSetEnd(i->tag); // finish drawing
902 swf_ShapeFree(s); // clean shape structure (which isn't needed anymore after writing the tag)
904 ActionTAG*a1=0,*a2=0,*a3=0;
905 a1 = action_NextFrame(a1);
906 a1 = action_Stop(a1);
909 a2 = action_PreviousFrame(a2);
910 a2 = action_Stop(a2);
913 a3 = action_Stop(a3);
916 i->tag = swf_InsertTag(i->tag, ST_DOACTION);
917 swf_ActionSet(i->tag,a3);
919 i->tag = swf_InsertTag(i->tag,ST_DEFINEBUTTON);
920 int buttonid1 = getNewID(dev);
921 swf_SetU16(i->tag, buttonid1);
922 swf_ButtonSetRecord(i->tag,BS_UP|BS_HIT,ids[0],1,NULL,NULL);
923 swf_ButtonSetRecord(i->tag,BS_OVER,ids[2],1,NULL,NULL);
924 swf_ButtonSetRecord(i->tag,BS_DOWN,ids[4],1,NULL,NULL);
925 swf_SetU8(i->tag,0); // end of button records
926 swf_ActionSet(i->tag,a1);
928 i->tag = swf_InsertTag(i->tag,ST_DEFINEBUTTON);
929 int buttonid2 = getNewID(dev);
930 swf_SetU16(i->tag, buttonid2);
931 swf_ButtonSetRecord(i->tag,BS_UP|BS_HIT,ids[1],1,NULL,NULL);
932 swf_ButtonSetRecord(i->tag,BS_OVER,ids[3],1,NULL,NULL);
933 swf_ButtonSetRecord(i->tag,BS_DOWN,ids[5],1,NULL,NULL);
934 swf_SetU8(i->tag,0); // end of button records
935 swf_ActionSet(i->tag,a2);
937 i->tag = swf_InsertTag(i->tag,ST_PLACEOBJECT2);
939 swf_GetMatrix(0, &m);
940 m.tx = button_sizex*20+200;
941 swf_ObjectPlace(i->tag, buttonid2, 65534,&m,0,0);
942 i->tag = swf_InsertTag(i->tag,ST_PLACEOBJECT2);
943 m.tx = button_sizex*20+200+200;
944 swf_ObjectPlace(i->tag, buttonid1, 65535,&m,0,0);
948 void swf_startframe(gfxdevice_t*dev, int width, int height)
950 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
952 if(i->config_protect) {
953 i->tag = swf_InsertTag(i->tag, ST_PROTECT);
954 i->config_protect = 0;
956 if(i->config_simpleviewer) {
961 if(!i->firstpage && !i->pagefinished)
964 msg("<verbose> Starting new SWF page of size %dx%d", width, height);
966 swf_GetMatrix(0, &i->page_matrix);
967 i->page_matrix.tx = 0;
968 i->page_matrix.ty = 0;
975 /* create a bbox structure with the page size. This is used
976 for clipping shape and text bounding boxes. As we don't want to
977 generate bounding boxes which extend beyond the movie size (in
978 order to not confuse Flash), we clip everything against i->pagebbox */
979 i->pagebbox.xmin = 0;
980 i->pagebbox.ymin = 0;
981 i->pagebbox.xmax = width*20;
982 i->pagebbox.ymax = height*20;
984 /* increase SWF's bounding box */
985 swf_ExpandRect2(&i->swf->movieSize, &i->pagebbox);
987 i->lastframeno = i->frameno;
992 void swf_endframe(gfxdevice_t*dev)
994 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
999 if( (i->swf->fileVersion <= 8) && (i->config_insertstoptag) ) {
1001 atag = action_Stop(atag);
1002 atag = action_End(atag);
1003 i->tag = swf_InsertTag(i->tag,ST_DOACTION);
1004 swf_ActionSet(i->tag,atag);
1006 i->tag = swf_InsertTag(i->tag,ST_SHOWFRAME);
1009 for(i->depth;i->depth>i->startdepth;i->depth--) {
1010 i->tag = swf_InsertTag(i->tag,ST_REMOVEOBJECT2);
1011 swf_SetU16(i->tag,i->depth);
1013 i->depth = i->startdepth;
1016 static void setBackground(gfxdevice_t*dev, int x1, int y1, int x2, int y2)
1018 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1020 rgb.a = rgb.r = rgb.g = rgb.b = 0xff;
1024 int shapeid = getNewID(dev);
1029 i->tag = swf_InsertTag(i->tag, ST_DEFINESHAPE);
1031 fs1 = swf_ShapeAddSolidFillStyle(s, &rgb);
1032 swf_SetU16(i->tag,shapeid);
1033 swf_SetRect(i->tag,&r);
1034 swf_SetShapeHeader(i->tag,s);
1035 swf_ShapeSetAll(i->tag,s,x1,y1,ls1,fs1,0);
1036 swf_ShapeSetLine(i->tag,s,(x2-x1),0);
1037 swf_ShapeSetLine(i->tag,s,0,(y2-y1));
1038 swf_ShapeSetLine(i->tag,s,(x1-x2),0);
1039 swf_ShapeSetLine(i->tag,s,0,(y1-y2));
1040 swf_ShapeSetEnd(i->tag);
1042 i->tag = swf_InsertTag(i->tag, ST_PLACEOBJECT2);
1043 swf_ObjectPlace(i->tag,shapeid,getNewDepth(dev),0,0,0);
1044 i->tag = swf_InsertTag(i->tag, ST_PLACEOBJECT2);
1045 swf_ObjectPlaceClip(i->tag,shapeid,getNewDepth(dev),0,0,0,65535);
1048 /* initialize the swf writer */
1049 void gfxdevice_swf_init(gfxdevice_t* dev)
1051 memset(dev, 0, sizeof(gfxdevice_t));
1055 dev->internal = init_internal_struct(); // set config to default values
1057 dev->startpage = swf_startframe;
1058 dev->endpage = swf_endframe;
1059 dev->finish = swf_finish;
1060 dev->fillbitmap = swf_fillbitmap;
1061 dev->setparameter = swf_setparameter;
1062 dev->stroke = swf_stroke;
1063 dev->startclip = swf_startclip;
1064 dev->endclip = swf_endclip;
1065 dev->fill = swf_fill;
1066 dev->fillbitmap = swf_fillbitmap;
1067 dev->fillgradient = swf_fillgradient;
1068 dev->addfont = swf_addfont;
1069 dev->drawchar = swf_drawchar;
1070 dev->drawlink = swf_drawlink;
1072 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1075 msg("<verbose> initializing swf output\n", i->max_x,i->max_y);
1079 i->swf = (SWF*)rfx_calloc(sizeof(SWF));
1080 i->swf->fileVersion = 0;
1081 i->swf->frameRate = 0x80;
1082 i->swf->movieSize.xmin = 0;
1083 i->swf->movieSize.ymin = 0;
1084 i->swf->movieSize.xmax = 0;
1085 i->swf->movieSize.ymax = 0;
1087 i->swf->firstTag = swf_InsertTag(NULL,ST_SETBACKGROUNDCOLOR);
1088 i->tag = i->swf->firstTag;
1090 rgb.a = rgb.r = rgb.g = rgb.b = 0xff;
1092 swf_SetRGB(i->tag,&rgb);
1094 i->startdepth = i->depth = 0;
1097 static void startshape(gfxdevice_t*dev)
1099 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1104 //if(i->chardatapos && i->chardata[i->chardatapos-1].color.a)
1107 i->tag = swf_InsertTag(i->tag,ST_DEFINESHAPE3);
1109 swf_ShapeNew(&i->shape);
1110 i->linestyleid = swf_ShapeAddLineStyle(i->shape,i->linewidth,&i->strokergb);
1111 i->fillstyleid = swf_ShapeAddSolidFillStyle(i->shape,&i->fillrgb);
1113 RGBA markcol = {0,i->mark[0],i->mark[1],i->mark[2]};
1114 swf_ShapeAddSolidFillStyle(i->shape,&markcol);
1117 i->shapeid = getNewID(dev);
1119 msg("<debug> Using shape id %d", i->shapeid);
1121 swf_SetU16(i->tag,i->shapeid); // ID
1123 i->bboxrectpos = i->tag->len;
1125 swf_SetRect(i->tag,&i->pagebbox);
1127 memset(&i->bboxrect, 0, sizeof(i->bboxrect));
1129 swf_SetShapeStyles(i->tag,i->shape);
1130 swf_ShapeCountBits(i->shape,NULL,NULL);
1131 swf_SetShapeBits(i->tag,i->shape);
1133 /* TODO: do we really need this? */
1134 //swf_ShapeSetAll(i->tag,i->shape,/*x*/0,/*y*/0,i->linestyleid,0,0);
1135 //swf_ShapeSetAll(i->tag,i->shape,/*x*/UNDEFINED_COORD,/*y*/UNDEFINED_COORD,i->linestyleid,0,0);
1136 i->swflastx=i->swflasty=UNDEFINED_COORD;
1137 i->lastwasfill = -1;
1138 i->shapeisempty = 1;
1141 static void starttext(gfxdevice_t*dev)
1143 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1147 if(i->config_watermark) {
1148 insert_watermark(dev, 0);
1150 i->textid = getNewID(dev);
1151 i->swflastx=i->swflasty=0;
1155 /* TODO: move to ../lib/rfxswf */
1156 void changeRect(gfxdevice_t*dev, TAG*tag, int pos, SRECT*newrect)
1158 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1159 /* determine length of old rect */
1163 swf_GetRect(tag, &old);
1164 swf_ResetReadBits(tag);
1165 int pos_end = tag->pos;
1167 int len = tag->len - pos_end;
1168 U8*data = (U8*)malloc(len);
1169 memcpy(data, &tag->data[pos_end], len);
1172 swf_SetRect(tag, newrect);
1173 swf_SetBlock(tag, data, len);
1175 tag->pos = tag->readBit = 0;
1178 void cancelshape(gfxdevice_t*dev)
1180 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1181 /* delete old shape tag */
1183 i->tag = i->tag->prev;
1184 swf_DeleteTag(todel);
1185 if(i->shape) {swf_ShapeFree(i->shape);i->shape=0;}
1187 i->bboxrectpos = -1;
1189 // i->currentswfid--; // doesn't work, for some reason
1192 void fixAreas(gfxdevice_t*dev)
1194 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1195 if(!i->shapeisempty && i->fill &&
1196 (i->bboxrect.xmin == i->bboxrect.xmax ||
1197 i->bboxrect.ymin == i->bboxrect.ymax) &&
1198 i->config_minlinewidth >= 0.001
1200 msg("<debug> Shape has size 0: width=%.2f height=%.2f",
1201 (i->bboxrect.xmax-i->bboxrect.xmin)/20.0,
1202 (i->bboxrect.ymax-i->bboxrect.ymin)/20.0
1205 SRECT r = i->bboxrect;
1207 if(r.xmin == r.xmax && r.ymin == r.ymax) {
1208 /* this thing comes down to a single dot- nothing to fix here */
1214 RGBA save_col = i->strokergb;
1215 int save_width = i->linewidth;
1217 i->strokergb = i->fillrgb;
1218 i->linewidth = (int)(i->config_minlinewidth*20);
1219 if(i->linewidth==0) i->linewidth = 1;
1224 moveto(dev, i->tag, r.xmin/20.0,r.ymin/20.0);
1225 lineto(dev, i->tag, r.xmax/20.0,r.ymax/20.0);
1227 i->strokergb = save_col;
1228 i->linewidth = save_width;
1233 static void endshape_noput(gfxdevice_t*dev)
1235 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1238 //changeRect(dev, i->tag, i->bboxrectpos, &i->bboxrect);
1241 swf_ShapeFree(i->shape);
1249 static void endshape(gfxdevice_t*dev)
1251 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1257 if(i->shapeisempty ||
1259 (i->bboxrect.xmin == i->bboxrect.xmax &&
1260 i->bboxrect.ymin == i->bboxrect.ymax))
1262 // delete the shape again, we didn't do anything
1263 msg("<debug> cancelling shape: bbox is (%f,%f,%f,%f)",
1264 i->bboxrect.xmin /20.0,
1265 i->bboxrect.ymin /20.0,
1266 i->bboxrect.xmax /20.0,
1267 i->bboxrect.ymax /20.0
1273 swf_ShapeSetEnd(i->tag);
1275 SRECT r = swf_ClipRect(i->pagebbox, i->bboxrect);
1276 changeRect(dev, i->tag, i->bboxrectpos, &r);
1278 msg("<trace> Placing shape ID %d", i->shapeid);
1280 i->tag = swf_InsertTag(i->tag,ST_PLACEOBJECT2);
1281 MATRIX m = i->page_matrix;
1282 m.tx += i->shapeposx;
1283 m.ty += i->shapeposy;
1284 swf_ObjectPlace(i->tag,i->shapeid,getNewDepth(dev),&m,NULL,NULL);
1286 if(i->config_animate) {
1287 i->tag = swf_InsertTag(i->tag,ST_SHOWFRAME);
1290 swf_ShapeFree(i->shape);
1293 i->bboxrectpos = -1;
1300 void wipeSWF(SWF*swf)
1302 TAG*tag = swf->firstTag;
1304 TAG*next = tag->next;
1305 if(tag->id != ST_SETBACKGROUNDCOLOR &&
1306 tag->id != ST_END &&
1307 tag->id != ST_DOACTION &&
1308 tag->id != ST_SHOWFRAME) {
1315 void swfoutput_finalize(gfxdevice_t*dev)
1317 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1319 if(i->tag && i->tag->id == ST_END)
1320 return; //already done
1322 i->swf->fileVersion = i->config_flashversion;
1323 i->swf->frameRate = i->config_framerate*0x100;
1325 if(i->config_bboxvars) {
1326 TAG* tag = swf_InsertTag(i->swf->firstTag, ST_DOACTION);
1328 a = action_PushString(a, "xmin");
1329 a = action_PushFloat(a, i->swf->movieSize.xmin / 20.0);
1330 a = action_SetVariable(a);
1331 a = action_PushString(a, "ymin");
1332 a = action_PushFloat(a, i->swf->movieSize.ymin / 20.0);
1333 a = action_SetVariable(a);
1334 a = action_PushString(a, "xmax");
1335 a = action_PushFloat(a, i->swf->movieSize.xmax / 20.0);
1336 a = action_SetVariable(a);
1337 a = action_PushString(a, "ymax");
1338 a = action_PushFloat(a, i->swf->movieSize.ymax / 20.0);
1339 a = action_SetVariable(a);
1340 a = action_PushString(a, "width");
1341 a = action_PushFloat(a, (i->swf->movieSize.xmax - i->swf->movieSize.xmin) / 20.0);
1342 a = action_SetVariable(a);
1343 a = action_PushString(a, "height");
1344 a = action_PushFloat(a, (i->swf->movieSize.ymax - i->swf->movieSize.ymin) / 20.0);
1345 a = action_SetVariable(a);
1347 swf_ActionSet(tag, a);
1352 free(i->mark);i->mark = 0;
1356 fontlist_t *iterator = i->fontlist;
1358 TAG*mtag = i->swf->firstTag;
1359 if(iterator->swffont) {
1360 if(!i->config_storeallcharacters) {
1361 msg("<debug> Reducing font %s", iterator->swffont->name);
1362 swf_FontReduce(iterator->swffont);
1364 int used = iterator->swffont->use && iterator->swffont->use->used_glyphs;
1366 mtag = swf_InsertTag(mtag, ST_DEFINEFONT2);
1367 swf_FontSetDefine2(mtag, iterator->swffont);
1371 iterator = iterator->next;
1374 i->tag = swf_InsertTag(i->tag,ST_END);
1375 TAG* tag = i->tag->prev;
1377 /* remove the removeobject2 tags between the last ST_SHOWFRAME
1378 and the ST_END- they confuse the flash player */
1379 while(tag->id == ST_REMOVEOBJECT2) {
1380 TAG* prev = tag->prev;
1388 if(i->config_enablezlib || i->config_flashversion>=6) {
1389 i->swf->compressed = 1;
1392 /* Initialize AVM2 if it is a Flash9 file */
1393 if(i->config_flashversion>=9 && i->config_insertstoptag) {
1394 AVM2_InsertStops(i->swf);
1396 // if(i->config_reordertags)
1397 // swf_Optimize(i->swf);
1400 int swfresult_save(gfxresult_t*gfx, const char*filename)
1402 SWF*swf = (SWF*)gfx->internal;
1405 fi = open(filename, O_BINARY|O_CREAT|O_TRUNC|O_WRONLY, 0777);
1410 msg("<fatal> Could not create \"%s\". ", FIXNULL(filename));
1414 if FAILED(swf_WriteSWF(fi,swf))
1415 msg("<error> WriteSWF() failed.\n");
1421 void* swfresult_get(gfxresult_t*gfx, const char*name)
1423 SWF*swf = (SWF*)gfx->internal;
1424 if(!strcmp(name, "swf")) {
1425 return (void*)swf_CopySWF(swf);
1426 } else if(!strcmp(name, "xmin")) {
1427 return (void*)(swf->movieSize.xmin/20);
1428 } else if(!strcmp(name, "ymin")) {
1429 return (void*)(swf->movieSize.ymin/20);
1430 } else if(!strcmp(name, "xmax")) {
1431 return (void*)(swf->movieSize.xmax/20);
1432 } else if(!strcmp(name, "ymax")) {
1433 return (void*)(swf->movieSize.ymax/20);
1434 } else if(!strcmp(name, "width")) {
1435 return (void*)((swf->movieSize.xmax - swf->movieSize.xmin)/20);
1436 } else if(!strcmp(name, "height")) {
1437 return (void*)((swf->movieSize.ymax - swf->movieSize.ymin)/20);
1441 void swfresult_destroy(gfxresult_t*gfx)
1444 swf_FreeTags((SWF*)gfx->internal);
1445 free(gfx->internal);
1448 memset(gfx, 0, sizeof(gfxresult_t));
1452 static void swfoutput_destroy(gfxdevice_t* dev);
1454 gfxresult_t* swf_finish(gfxdevice_t* dev)
1456 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1459 if(i->config_linktarget) {
1460 free(i->config_linktarget);
1461 i->config_linktarget = 0;
1464 swfoutput_finalize(dev);
1465 SWF* swf = i->swf;i->swf = 0;
1466 swfoutput_destroy(dev);
1468 result = (gfxresult_t*)rfx_calloc(sizeof(gfxresult_t));
1469 result->internal = swf;
1470 result->save = swfresult_save;
1472 result->get = swfresult_get;
1473 result->destroy = swfresult_destroy;
1477 /* Perform cleaning up */
1478 static void swfoutput_destroy(gfxdevice_t* dev)
1480 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1482 /* not initialized yet- nothing to destroy */
1486 fontlist_t *tmp,*iterator = i->fontlist;
1488 if(iterator->swffont) {
1489 swf_FontFree(iterator->swffont);iterator->swffont=0;
1492 iterator = iterator->next;
1495 if(i->swf) {swf_FreeTags(i->swf);free(i->swf);i->swf = 0;}
1498 memset(dev, 0, sizeof(gfxdevice_t));
1501 static void swfoutput_setstrokecolor(gfxdevice_t* dev, U8 r, U8 g, U8 b, U8 a)
1503 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1504 if(i->strokergb.r == r &&
1505 i->strokergb.g == g &&
1506 i->strokergb.b == b &&
1507 i->strokergb.a == a) return;
1517 //#define ROUND_UP 19
1518 //#define ROUND_UP 10
1520 static void swfoutput_setlinewidth(gfxdevice_t*dev, double _linewidth)
1522 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1523 if(i->linewidth == (U16)(_linewidth*20+19.0/20.0))
1527 i->linewidth = (U16)(_linewidth*20+19.0/20.0);
1531 static void drawlink(gfxdevice_t*dev, ActionTAG*,ActionTAG*, gfxline_t*points, char mouseover, const char*url);
1532 static void swfoutput_namedlink(gfxdevice_t*dev, char*name, gfxline_t*points);
1533 static void swfoutput_linktopage(gfxdevice_t*dev, int page, gfxline_t*points);
1534 static void swfoutput_linktourl(gfxdevice_t*dev, const char*url, gfxline_t*points);
1536 /*void swfoutput_drawlink(gfxdevice_t*dev, char*url, gfxline_t*points)
1538 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1539 dev->drawlink(dev, points, url);
1542 void swf_drawlink(gfxdevice_t*dev, gfxline_t*points, const char*url)
1544 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1546 if(!strncmp("http://pdf2swf:", url, 15)) {
1547 char*tmp = strdup(url);
1548 int l = strlen(tmp);
1551 swfoutput_namedlink(dev, tmp+15, points);
1554 } else if(!strncmp("page", url, 4)) {
1557 if(url[t]<'0' || url[t]>'9')
1560 int page = atoi(&url[4]);
1561 if(page<0) page = 0;
1562 swfoutput_linktopage(dev, page, points);
1565 swfoutput_linktourl(dev, url, points);
1568 void swfoutput_linktourl(gfxdevice_t*dev, const char*url, gfxline_t*points)
1570 ActionTAG* actions = 0;
1571 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1577 if(i->config_externallinkfunction) {
1578 actions = action_PushString(actions, url); //parameter
1579 actions = action_PushInt(actions, 1); //number of parameters (1)
1580 actions = action_PushString(actions, i->config_externallinkfunction); //function name
1581 actions = action_CallFunction(actions);
1582 } else if(!i->config_linktarget) {
1583 if(!i->config_opennewwindow)
1584 actions = action_GetUrl(actions, url, "_parent");
1586 actions = action_GetUrl(actions, url, "_this");
1588 actions = action_GetUrl(actions, url, i->config_linktarget);
1590 actions = action_End(actions);
1592 drawlink(dev, actions, 0, points, 0, url);
1594 void swfoutput_linktopage(gfxdevice_t*dev, int page, gfxline_t*points)
1596 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1597 ActionTAG* actions = 0;
1604 if(!i->config_internallinkfunction) {
1605 actions = action_GotoFrame(actions, page-1);
1606 actions = action_End(actions);
1608 actions = action_PushInt(actions, page); //parameter
1609 actions = action_PushInt(actions, 1); //number of parameters (1)
1610 actions = action_PushString(actions, i->config_internallinkfunction); //function name
1611 actions = action_CallFunction(actions);
1612 actions = action_End(actions);
1616 sprintf(name, "page%d", page);
1618 drawlink(dev, actions, 0, points, 0, name);
1621 /* Named Links (a.k.a. Acrobatmenu) are used to implement various gadgets
1622 of the viewer objects, like subtitles, index elements etc.
1624 void swfoutput_namedlink(gfxdevice_t*dev, char*name, gfxline_t*points)
1626 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1627 ActionTAG *actions1,*actions2;
1628 char*tmp = strdup(name);
1636 if(!strncmp(tmp, "call:", 5))
1638 char*x = strchr(&tmp[5], ':');
1640 actions1 = action_PushInt(0, 0); //number of parameters (0)
1641 actions1 = action_PushString(actions1, &tmp[5]); //function name
1642 actions1 = action_CallFunction(actions1);
1643 actions1 = action_End(actions1);
1646 actions1 = action_PushString(0, x+1); //parameter
1647 actions1 = action_PushInt(actions1, 1); //number of parameters (1)
1648 actions1 = action_PushString(actions1, &tmp[5]); //function name
1649 actions1 = action_CallFunction(actions1);
1650 actions1 = action_End(actions1);
1652 actions2 = action_End(0);
1657 actions1 = action_PushString(0, "/:subtitle");
1658 actions1 = action_PushString(actions1, name);
1659 actions1 = action_SetVariable(actions1);
1660 actions1 = action_End(actions1);
1662 actions2 = action_PushString(0, "/:subtitle");
1663 actions2 = action_PushString(actions2, "");
1664 actions2 = action_SetVariable(actions2);
1665 actions2 = action_End(actions2);
1668 drawlink(dev, actions1, actions2, points, mouseover, name);
1670 swf_ActionFree(actions1);
1671 swf_ActionFree(actions2);
1675 static void drawgfxline(gfxdevice_t*dev, gfxline_t*line, int fill)
1677 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1678 gfxcoord_t lastx=0,lasty=0,px=0,py=0;
1680 int lines= 0, splines=0;
1687 /* check whether the next segment is zero */
1688 if(line->type == gfx_moveTo) {
1689 moveto(dev, i->tag, line->x, line->y);
1690 px = lastx = line->x;
1691 py = lasty = line->y;
1693 } if(line->type == gfx_lineTo) {
1694 lineto(dev, i->tag, line->x, line->y);
1699 } else if(line->type == gfx_splineTo) {
1701 s.x = line->sx;p.x = line->x;
1702 s.y = line->sy;p.y = line->y;
1703 splineto(dev, i->tag, s, p);
1711 msg("<trace> drawgfxline, %d lines, %d splines", lines, splines);
1715 static void drawlink(gfxdevice_t*dev, ActionTAG*actions1, ActionTAG*actions2, gfxline_t*points, char mouseover, const char*url)
1717 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1726 int buttonid = getNewID(dev);
1727 gfxbbox_t bbox = gfxline_getbbox(points);
1730 myshapeid = getNewID(dev);
1731 i->tag = swf_InsertTag(i->tag,ST_DEFINESHAPE3);
1732 swf_ShapeNew(&i->shape);
1733 rgb.r = rgb.b = rgb.a = rgb.g = 0;
1734 fsid = swf_ShapeAddSolidFillStyle(i->shape,&rgb);
1735 swf_SetU16(i->tag, myshapeid);
1736 r.xmin = (int)(bbox.xmin*20);
1737 r.ymin = (int)(bbox.ymin*20);
1738 r.xmax = (int)(bbox.xmax*20);
1739 r.ymax = (int)(bbox.ymax*20);
1740 r = swf_ClipRect(i->pagebbox, r);
1741 swf_SetRect(i->tag,&r);
1742 swf_SetShapeStyles(i->tag,i->shape);
1743 swf_ShapeCountBits(i->shape,NULL,NULL);
1744 swf_SetShapeBits(i->tag,i->shape);
1745 swf_ShapeSetAll(i->tag,i->shape,/*x*/0,/*y*/0,0,fsid,0);
1746 i->swflastx = i->swflasty = 0;
1747 drawgfxline(dev, points, 1);
1748 swf_ShapeSetEnd(i->tag);
1751 myshapeid2 = getNewID(dev);
1752 i->tag = swf_InsertTag(i->tag,ST_DEFINESHAPE3);
1753 swf_ShapeNew(&i->shape);
1755 rgb = i->config_linkcolor;
1757 fsid = swf_ShapeAddSolidFillStyle(i->shape,&rgb);
1758 swf_SetU16(i->tag, myshapeid2);
1759 r.xmin = (int)(bbox.xmin*20);
1760 r.ymin = (int)(bbox.ymin*20);
1761 r.xmax = (int)(bbox.xmax*20);
1762 r.ymax = (int)(bbox.ymax*20);
1763 r = swf_ClipRect(i->pagebbox, r);
1764 swf_SetRect(i->tag,&r);
1765 swf_SetShapeStyles(i->tag,i->shape);
1766 swf_ShapeCountBits(i->shape,NULL,NULL);
1767 swf_SetShapeBits(i->tag,i->shape);
1768 swf_ShapeSetAll(i->tag,i->shape,/*x*/0,/*y*/0,0,fsid,0);
1769 i->swflastx = i->swflasty = 0;
1770 drawgfxline(dev, points, 1);
1771 swf_ShapeSetEnd(i->tag);
1775 i->tag = swf_InsertTag(i->tag,ST_DEFINEBUTTON);
1776 swf_SetU16(i->tag,buttonid); //id
1777 swf_ButtonSetFlags(i->tag, 0); //menu=no
1778 swf_ButtonSetRecord(i->tag,0x01,myshapeid,i->depth,0,0);
1779 swf_ButtonSetRecord(i->tag,0x02,myshapeid2,i->depth,0,0);
1780 swf_ButtonSetRecord(i->tag,0x04,myshapeid2,i->depth,0,0);
1781 swf_ButtonSetRecord(i->tag,0x08,myshapeid,i->depth,0,0);
1782 swf_SetU8(i->tag,0);
1783 swf_ActionSet(i->tag,actions1);
1784 swf_SetU8(i->tag,0);
1788 i->tag = swf_InsertTag(i->tag,ST_DEFINEBUTTON2);
1789 swf_SetU16(i->tag,buttonid); //id
1790 swf_ButtonSetFlags(i->tag, 0); //menu=no
1791 swf_ButtonSetRecord(i->tag,0x01,myshapeid,i->depth,0,0);
1792 swf_ButtonSetRecord(i->tag,0x02,myshapeid2,i->depth,0,0);
1793 swf_ButtonSetRecord(i->tag,0x04,myshapeid2,i->depth,0,0);
1794 swf_ButtonSetRecord(i->tag,0x08,myshapeid,i->depth,0,0);
1795 swf_SetU8(i->tag,0); // end of button records
1796 swf_ButtonSetCondition(i->tag, BC_IDLE_OVERUP);
1797 swf_ActionSet(i->tag,actions1);
1799 swf_ButtonSetCondition(i->tag, BC_OVERUP_IDLE);
1800 swf_ActionSet(i->tag,actions2);
1801 swf_SetU8(i->tag,0);
1802 swf_ButtonPostProcess(i->tag, 2);
1804 swf_SetU8(i->tag,0);
1805 swf_ButtonPostProcess(i->tag, 1);
1808 const char* name = 0;
1809 if(i->config_linknameurl) {
1813 msg("<trace> Placing link ID %d", buttonid);
1814 i->tag = swf_InsertTag(i->tag,ST_PLACEOBJECT2);
1816 if(posx!=0 || posy!=0) {
1818 p.x = (int)(posx*20);
1819 p.y = (int)(posy*20);
1820 p = swf_TurnPoint(p, &i->page_matrix);
1825 swf_ObjectPlace(i->tag, buttonid, getNewDepth(dev),&m,0,name);
1827 swf_ObjectPlace(i->tag, buttonid, getNewDepth(dev),&i->page_matrix,0,name);
1834 for(t=0;t<picpos;t++)
1836 if(pic_xids[t] == xid &&
1837 pic_yids[t] == yid) {
1838 width = pic_width[t];
1839 height = pic_height[t];
1843 pic_ids[picpos] = swfoutput_drawimagelosslessN(&output, pic, pal, width, height, x1,y1,x2,y2,x3,y3,x4,y4, numpalette);
1844 pic_xids[picpos] = xid;
1845 pic_yids[picpos] = yid;
1846 pic_width[picpos] = width;
1847 pic_height[picpos] = height;
1850 pic[width*y+x] = buf[0];
1854 xid += pal[1].r*3 + pal[1].g*11 + pal[1].b*17;
1855 yid += pal[1].r*7 + pal[1].g*5 + pal[1].b*23;
1859 xid += x*r+x*b*3+x*g*7+x*a*11;
1860 yid += y*r*3+y*b*17+y*g*19+y*a*11;
1862 for(t=0;t<picpos;t++)
1864 if(pic_xids[t] == xid &&
1865 pic_yids[t] == yid) {
1874 int swf_setparameter(gfxdevice_t*dev, const char*name, const char*value)
1876 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1878 msg("<trace> swfdevice: %s=%s", name, value);
1879 if(!strcmp(name, "jpegsubpixels")) {
1880 i->config_jpegsubpixels = atof(value);
1881 } else if(!strcmp(name, "ppmsubpixels")) {
1882 i->config_ppmsubpixels = atof(value);
1883 } else if(!strcmp(name, "subpixels")) {
1884 i->config_ppmsubpixels = i->config_jpegsubpixels = atof(value);
1885 } else if(!strcmp(name, "drawonlyshapes")) {
1886 i->config_drawonlyshapes = atoi(value);
1887 } else if(!strcmp(name, "ignoredraworder")) {
1888 i->config_ignoredraworder = atoi(value);
1889 } else if(!strcmp(name, "mark")) {
1890 if(!value || !value[0]) {
1891 if(i->mark) free(i->mark);
1895 i->mark = strdup("...");
1896 for(t=0;t<3;t++) if(value[t]) i->mark[t] = value[t];
1898 } else if(!strcmp(name, "filloverlap")) {
1899 i->config_filloverlap = atoi(value);
1900 } else if(!strcmp(name, "linksopennewwindow")) {
1901 i->config_opennewwindow = atoi(value);
1902 } else if(!strcmp(name, "opennewwindow")) {
1903 i->config_opennewwindow = atoi(value);
1904 } else if(!strcmp(name, "storeallcharacters")) {
1905 i->config_storeallcharacters = atoi(value);
1906 } else if(!strcmp(name, "enablezlib")) {
1907 i->config_enablezlib = atoi(value);
1908 } else if(!strcmp(name, "bboxvars")) {
1909 i->config_bboxvars = atoi(value);
1910 } else if(!strcmp(name, "showclipshapes")) {
1911 i->config_showclipshapes = atoi(value);
1912 } else if(!strcmp(name, "reordertags")) {
1913 i->config_reordertags = atoi(value);
1914 } else if(!strcmp(name, "internallinkfunction")) {
1915 i->config_internallinkfunction = strdup(value);
1916 } else if(!strcmp(name, "externallinkfunction")) {
1917 i->config_externallinkfunction = strdup(value);
1918 } else if(!strcmp(name, "linkfunction")) { //sets both internallinkfunction and externallinkfunction
1919 i->config_internallinkfunction = strdup(value);
1920 i->config_externallinkfunction = strdup(value);
1921 } else if(!strcmp(name, "disable_polygon_conversion")) {
1922 i->config_disable_polygon_conversion = atoi(value);
1923 } else if(!strcmp(name, "normalize_polygon_positions")) {
1924 i->config_normalize_polygon_positions = atoi(value);
1925 } else if(!strcmp(name, "wxwindowparams")) {
1926 i->config_watermark = atoi(value);
1927 } else if(!strcmp(name, "insertstop")) {
1928 i->config_insertstoptag = atoi(value);
1929 } else if(!strcmp(name, "protect")) {
1930 i->config_protect = atoi(value);
1931 if(i->config_protect && i->tag) {
1932 i->tag = swf_InsertTag(i->tag, ST_PROTECT);
1934 } else if(!strcmp(name, "flashversion")) {
1935 i->config_flashversion = atoi(value);
1937 i->swf->fileVersion = i->config_flashversion;
1939 } else if(!strcmp(name, "framerate")) {
1940 i->config_framerate = atoi(value);
1942 i->swf->frameRate = i->config_framerate*0x100;
1944 } else if(!strcmp(name, "minlinewidth")) {
1945 i->config_minlinewidth = atof(value);
1946 } else if(!strcmp(name, "caplinewidth")) {
1947 i->config_caplinewidth = atof(value);
1948 } else if(!strcmp(name, "linktarget")) {
1949 i->config_linktarget = strdup(value);
1950 } else if(!strcmp(name, "dumpfonts")) {
1951 i->config_dumpfonts = atoi(value);
1952 } else if(!strcmp(name, "animate")) {
1953 i->config_animate = atoi(value);
1954 } else if(!strcmp(name, "simpleviewer")) {
1955 i->config_simpleviewer = atoi(value);
1956 } else if(!strcmp(name, "next_bitmap_is_jpeg")) {
1958 } else if(!strcmp(name, "jpegquality")) {
1959 int val = atoi(value);
1961 if(val>101) val=101;
1962 i->config_jpegquality = val;
1963 } else if(!strcmp(name, "splinequality")) {
1964 int v = atoi(value);
1965 v = 500-(v*5); // 100% = 0.25 pixel, 0% = 25 pixel
1967 i->config_splinemaxerror = v;
1968 } else if(!strcmp(name, "fontquality")) {
1969 int v = atoi(value);
1970 v = 500-(v*5); // 100% = 0.25 pixel, 0% = 25 pixel
1972 i->config_fontsplinemaxerror = v;
1973 } else if(!strcmp(name, "linkcolor")) {
1974 if(strlen(value)!=8) {
1975 fprintf(stderr, "Unknown format for option 'linkcolor'. (%s <-> RRGGBBAA)\n", value);
1978 # define NIBBLE(s) (((s)>='0' && (s)<='9')?((s)-'0'):((s)&0x0f)+9)
1979 i->config_linkcolor.r = NIBBLE(value[0])<<4 | NIBBLE(value[1]);
1980 i->config_linkcolor.g = NIBBLE(value[2])<<4 | NIBBLE(value[3]);
1981 i->config_linkcolor.b = NIBBLE(value[4])<<4 | NIBBLE(value[5]);
1982 i->config_linkcolor.a = NIBBLE(value[6])<<4 | NIBBLE(value[7]);
1983 } else if(!strcmp(name, "help")) {
1984 printf("\nSWF layer options:\n");
1985 printf("jpegsubpixels=<pixels> resolution adjustment for jpeg images (same as jpegdpi, but in pixels)\n");
1986 printf("ppmsubpixels=<pixels resolution adjustment for lossless images (same as ppmdpi, but in pixels)\n");
1987 printf("subpixels=<pixels> shortcut for setting both jpegsubpixels and ppmsubpixels\n");
1988 printf("drawonlyshapes convert everything to shapes (currently broken)\n");
1989 printf("ignoredraworder allow to perform a few optimizations for creating smaller SWFs\n");
1990 printf("linksopennewwindow make links open a new browser window\n");
1991 printf("linktarget target window name of new links\n");
1992 printf("linkcolor=<color) color of links (format: RRGGBBAA)\n");
1993 printf("linknameurl Link buttons will be named like the URL they refer to (handy for iterating through links with actionscript)\n");
1994 printf("storeallcharacters don't reduce the fonts to used characters in the output file\n");
1995 printf("enablezlib switch on zlib compression (also done if flashversion>=7)\n");
1996 printf("bboxvars store the bounding box of the SWF file in actionscript variables\n");
1997 printf("reordertags=0/1 (default: 1) perform some tag optimizations\n");
1998 printf("internallinkfunction=<name> when the user clicks a internal link (to a different page) in the converted file, this actionscript function is called\n");
1999 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");
2000 printf("disable_polygon_conversion never convert strokes to polygons (will remove capstyles and joint styles)\n");
2001 printf("caplinewidth=<width> the minimum thichness a line needs to have so that capstyles become visible (and are converted)\n");
2002 printf("insertstop put an ActionScript \"STOP\" tag in every frame\n");
2003 printf("protect add a \"protect\" tag to the file, to prevent loading in the Flash editor\n");
2004 printf("flashversion=<version> the SWF fileversion (6)\n");
2005 printf("minlinewidth=<width> convert horizontal/vertical boxes smaller than this width to lines (0.05) \n");
2006 printf("simpleviewer Add next/previous buttons to the SWF\n");
2007 printf("animate insert a showframe tag after each placeobject (animate draw order of PDF files)\n");
2008 printf("jpegquality=<quality> set compression quality of jpeg images\n");
2015 // --------------------------------------------------------------------
2017 static CXFORM gfxcxform_to_cxform(gfxcxform_t* c)
2020 swf_GetCXForm(0, &cx, 1);
2023 if(c->rg!=0 || c->rb!=0 || c->ra!=0 ||
2024 c->gr!=0 || c->gb!=0 || c->ga!=0 ||
2025 c->br!=0 || c->bg!=0 || c->ba!=0 ||
2026 c->ar!=0 || c->ag!=0 || c->ab!=0)
2027 msg("<warning> CXForm not SWF-compatible");
2029 cx.a0 = (S16)(c->aa*256);
2030 cx.r0 = (S16)(c->rr*256);
2031 cx.g0 = (S16)(c->gg*256);
2032 cx.b0 = (S16)(c->bb*256);
2041 static int imageInCache(gfxdevice_t*dev, void*data, int width, int height)
2045 static void addImageToCache(gfxdevice_t*dev, void*data, int width, int height)
2049 static int add_image(swfoutput_internal*i, gfximage_t*img, int targetwidth, int targetheight, int* newwidth, int* newheight)
2051 gfxdevice_t*dev = i->dev;
2053 RGBA*mem = (RGBA*)img->data;
2055 int sizex = img->width;
2056 int sizey = img->height;
2057 int is_jpeg = i->jpeg;
2060 int newsizex=sizex, newsizey=sizey;
2063 if(is_jpeg && i->config_jpegsubpixels) {
2064 newsizex = (int)(targetwidth*i->config_jpegsubpixels + 0.5);
2065 newsizey = (int)(targetheight*i->config_jpegsubpixels + 0.5);
2066 } else if(!is_jpeg && i->config_ppmsubpixels) {
2067 newsizex = (int)(targetwidth*i->config_ppmsubpixels + 0.5);
2068 newsizey = (int)(targetheight*i->config_ppmsubpixels + 0.5);
2072 if(sizex<=0 || sizey<=0)
2079 /* TODO: cache images */
2081 if(newsizex<sizex || newsizey<sizey) {
2082 msg("<verbose> Scaling %dx%d image to %dx%d", sizex, sizey, newsizex, newsizey);
2083 newpic = swf_ImageScale(mem, sizex, sizey, newsizex, newsizey);
2084 *newwidth = sizex = newsizex;
2085 *newheight = sizey = newsizey;
2088 *newwidth = newsizex = sizex;
2089 *newheight = newsizey = sizey;
2092 int num_colors = swf_ImageGetNumberOfPaletteEntries(mem,sizex,sizey,0);
2093 int has_alpha = swf_ImageHasAlpha(mem,sizex,sizey);
2095 msg("<verbose> Drawing %dx%d %s%simage (id %d) at size %dx%d (%dx%d), %s%d colors",
2097 has_alpha?(has_alpha==2?"semi-transparent ":"transparent "):"",
2098 is_jpeg?"jpeg-":"", i->currentswfid+1,
2100 targetwidth, targetheight,
2101 /*newsizex, newsizey,*/
2102 num_colors>256?">":"", num_colors>256?256:num_colors);
2104 /*RGBA* pal = (RGBA*)rfx_alloc(sizeof(RGBA)*num_colors);
2105 swf_ImageGetNumberOfPaletteEntries(mem,sizex,sizey,pal);
2107 for(t=0;t<num_colors;t++) {
2108 printf("%02x%02x%02x%02x ",
2109 pal[t].r, pal[t].g, pal[t].b, pal[t].a);
2116 int cacheid = imageInCache(dev, mem, sizex, sizey);
2119 bitid = getNewID(dev);
2120 i->tag = swf_AddImage(i->tag, bitid, mem, sizex, sizey, i->config_jpegquality);
2121 addImageToCache(dev, mem, sizex, sizey);
2131 static SRECT gfxline_getSWFbbox(gfxline_t*line)
2133 gfxbbox_t bbox = gfxline_getbbox(line);
2135 r.xmin = (int)(bbox.xmin*20);
2136 r.ymin = (int)(bbox.ymin*20);
2137 r.xmax = (int)(bbox.xmax*20);
2138 r.ymax = (int)(bbox.ymax*20);
2142 int line_is_empty(gfxline_t*line)
2145 if(line->type != gfx_moveTo)
2152 static void swf_fillbitmap(gfxdevice_t*dev, gfxline_t*line, gfximage_t*img, gfxmatrix_t*matrix, gfxcxform_t*cxform)
2154 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
2156 if(line_is_empty(line))
2162 int targetx = (int)(sqrt(matrix->m00*matrix->m00 + matrix->m01*matrix->m01)*img->width);
2163 int targety = (int)(sqrt(matrix->m10*matrix->m10 + matrix->m11*matrix->m11)*img->height);
2165 int newwidth=0,newheight=0;
2166 int bitid = add_image(i, img, targetx, targety, &newwidth, &newheight);
2169 double fx = (double)img->width / (double)newwidth;
2170 double fy = (double)img->height / (double)newheight;
2173 m.sx = (int)(65536*20*matrix->m00*fx); m.r1 = (int)(65536*20*matrix->m10*fy);
2174 m.r0 = (int)(65536*20*matrix->m01*fx); m.sy = (int)(65536*20*matrix->m11*fy);
2175 m.tx = (int)(matrix->tx*20);
2176 m.ty = (int)(matrix->ty*20);
2179 int myshapeid = getNewID(dev);
2180 i->tag = swf_InsertTag(i->tag,ST_DEFINESHAPE);
2182 swf_ShapeNew(&shape);
2183 int fsid = swf_ShapeAddBitmapFillStyle(shape,&m,bitid,1);
2184 swf_SetU16(i->tag, myshapeid);
2185 SRECT r = gfxline_getSWFbbox(line);
2186 r = swf_ClipRect(i->pagebbox, r);
2187 swf_SetRect(i->tag,&r);
2188 swf_SetShapeStyles(i->tag,shape);
2189 swf_ShapeCountBits(shape,NULL,NULL);
2190 swf_SetShapeBits(i->tag,shape);
2191 swf_ShapeSetAll(i->tag,shape,UNDEFINED_COORD,UNDEFINED_COORD,0,fsid,0);
2192 i->swflastx = i->swflasty = UNDEFINED_COORD;
2193 drawgfxline(dev, line, 1);
2194 swf_ShapeSetEnd(i->tag);
2195 swf_ShapeFree(shape);
2197 msg("<trace> Placing image, shape ID %d, bitmap ID %d", myshapeid, bitid);
2198 i->tag = swf_InsertTag(i->tag,ST_PLACEOBJECT2);
2199 CXFORM cxform2 = gfxcxform_to_cxform(cxform);
2200 swf_ObjectPlace(i->tag,myshapeid,getNewDepth(dev),&i->page_matrix,&cxform2,NULL);
2203 static RGBA col_black = {255,0,0,0};
2205 static void drawoutline(gfxdevice_t*dev, gfxline_t*line)
2207 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
2209 int myshapeid = getNewID(dev);
2210 i->tag = swf_InsertTag(i->tag,ST_DEFINESHAPE3);
2213 swf_ShapeNew(&shape);
2214 int lsid = swf_ShapeAddLineStyle(shape,1,&col_black);
2216 swf_SetU16(i->tag,myshapeid);
2217 SRECT r = gfxline_getSWFbbox(line);
2218 r = swf_ClipRect(i->pagebbox, r);
2219 swf_SetRect(i->tag,&r);
2220 swf_SetShapeStyles(i->tag,shape);
2221 swf_ShapeCountBits(shape,NULL,NULL);
2222 swf_SetShapeBits(i->tag,shape);
2223 swf_ShapeSetAll(i->tag,shape,UNDEFINED_COORD,UNDEFINED_COORD,lsid,0,0);
2224 drawgfxline(dev, line, 1);
2225 swf_ShapeSetEnd(i->tag);
2226 swf_ShapeFree(shape);
2228 i->tag = swf_InsertTag(i->tag,ST_PLACEOBJECT2);
2229 swf_ObjectPlace(i->tag, myshapeid, getNewDepth(dev), 0,0,0);
2232 static void swf_startclip(gfxdevice_t*dev, gfxline_t*line)
2234 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
2239 if(i->clippos >= 127)
2241 msg("<warning> Too many clip levels.");
2245 if(i->config_showclipshapes)
2246 drawoutline(dev, line);
2248 int myshapeid = getNewID(dev);
2249 i->tag = swf_InsertTag(i->tag,ST_DEFINESHAPE3);
2251 memset(&col, 0, sizeof(RGBA));
2254 swf_ShapeNew(&shape);
2255 int fsid = swf_ShapeAddSolidFillStyle(shape,&col);
2257 RGBA markcol = {0,i->mark[0],i->mark[1],i->mark[2]};
2258 swf_ShapeAddSolidFillStyle(shape,&markcol);
2260 swf_SetU16(i->tag,myshapeid);
2261 SRECT r = gfxline_getSWFbbox(line);
2262 r = swf_ClipRect(i->pagebbox, r);
2263 swf_SetRect(i->tag,&r);
2264 swf_SetShapeStyles(i->tag,shape);
2265 swf_ShapeCountBits(shape,NULL,NULL);
2266 swf_SetShapeBits(i->tag,shape);
2267 swf_ShapeSetAll(i->tag,shape,UNDEFINED_COORD,UNDEFINED_COORD,0,fsid,0);
2268 i->swflastx = i->swflasty = UNDEFINED_COORD;
2269 i->shapeisempty = 1;
2270 drawgfxline(dev, line, 1);
2271 if(i->shapeisempty) {
2272 /* an empty clip shape is equivalent to a shape with no area */
2273 int x = line?line->x:0;
2274 int y = line?line->y:0;
2275 moveto(dev, i->tag, x,y);
2276 lineto(dev, i->tag, x,y);
2277 lineto(dev, i->tag, x,y);
2279 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)) {
2280 if(i->config_watermark) {
2281 gfxbbox_t r; r.xmin = r.ymin = 0;r.xmax = i->max_x;r.ymax = i->max_y;
2282 draw_watermark(dev, r, 1);
2285 swf_ShapeSetEnd(i->tag);
2286 swf_ShapeFree(shape);
2288 /* TODO: remember the bbox, and check all shapes against it */
2290 msg("<trace> Placing clip ID %d", myshapeid);
2291 i->tag = swf_InsertTag(i->tag,ST_PLACEOBJECT2);
2292 i->cliptags[i->clippos] = i->tag;
2293 i->clipshapes[i->clippos] = myshapeid;
2294 i->clipdepths[i->clippos] = getNewDepth(dev);
2298 static void swf_endclip(gfxdevice_t*dev)
2300 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
2307 msg("<error> Invalid end of clipping region");
2311 /*swf_ObjectPlaceClip(i->cliptags[i->clippos],i->clipshapes[i->clippos],i->clipdepths[i->clippos],&i->page_matrix,NULL,NULL,
2312 / * clip to depth: * / i->depth <= i->clipdepths[i->clippos]? i->depth : i->depth - 1);
2314 swf_ObjectPlaceClip(i->cliptags[i->clippos],i->clipshapes[i->clippos],i->clipdepths[i->clippos],&i->page_matrix,NULL,NULL,i->depth);
2316 static int gfxline_type(gfxline_t*line)
2322 int haszerosegments=0;
2325 if(line->type == gfx_moveTo) {
2328 } else if(line->type == gfx_lineTo) {
2332 } else if(line->type == gfx_splineTo) {
2334 if(tmpsplines>lines)
2342 if(lines==0 && splines==0) return 0;
2343 else if(lines==1 && splines==0) return 1;
2344 else if(lines==0 && splines==1) return 2;
2345 else if(splines==0) return 3;
2349 static int gfxline_has_dots(gfxline_t*line)
2357 if(line->type == gfx_moveTo) {
2358 /* test the length of the preceding line, and assume it is a dot if
2359 it's length is less than 1.0. But *only* if there's a noticable
2360 gap between the previous line and the next moveTo. (I've come
2361 across a PDF where thousands of "dots" were stringed together,
2363 int last_short_gap = short_gap;
2364 if((fabs(line->x - x) + fabs(line->y - y)) < 1.0) {
2369 if(isline && dist < 1 && !short_gap && !last_short_gap) {
2374 } else if(line->type == gfx_lineTo) {
2375 dist += fabs(line->x - x) + fabs(line->y - y);
2377 } else if(line->type == gfx_splineTo) {
2378 dist += fabs(line->sx - x) + fabs(line->sy - y) +
2379 fabs(line->x - line->sx) + fabs(line->y - line->sy);
2386 if(isline && dist < 1 && !short_gap) {
2392 static int gfxline_fix_short_edges(gfxline_t*line)
2396 if(line->type == gfx_lineTo) {
2397 if(fabs(line->x - x) + fabs(line->y - y) < 0.01) {
2400 } else if(line->type == gfx_splineTo) {
2401 if(fabs(line->sx - x) + fabs(line->sy - y) +
2402 fabs(line->x - line->sx) + fabs(line->y - line->sy) < 0.01) {
2413 static char is_inside_page(gfxdevice_t*dev, gfxcoord_t x, gfxcoord_t y)
2415 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
2416 if(x<i->min_x || x>i->max_x) return 0;
2417 if(y<i->min_y || y>i->max_y) return 0;
2421 gfxline_t* gfxline_move(gfxline_t*line, double x, double y)
2423 gfxline_t*l = line = gfxline_clone(line);
2435 //#define NORMALIZE_POLYGON_POSITIONS
2437 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)
2439 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
2440 if(line_is_empty(line))
2442 int type = gfxline_type(line);
2443 int has_dots = gfxline_has_dots(line);
2444 gfxbbox_t r = gfxline_getbbox(line);
2445 int is_outside_page = !is_inside_page(dev, r.xmin, r.ymin) || !is_inside_page(dev, r.xmax, r.ymax);
2447 /* TODO: * split line into segments, and perform this check for all segments */
2449 if(i->config_disable_polygon_conversion || type>=5 ||
2451 (width <= i->config_caplinewidth
2452 || (cap_style == gfx_capRound && joint_style == gfx_joinRound)
2453 || (cap_style == gfx_capRound && type<=2)))) {} else
2455 /* convert line to polygon */
2456 msg("<trace> draw as polygon, type=%d dots=%d", type, has_dots);
2458 gfxline_fix_short_edges(line);
2459 /* we need to convert the line into a polygon */
2460 gfxpoly_t* poly = gfxpoly_strokeToPoly(line, width, cap_style, joint_style, miterLimit);
2461 gfxline_t*gfxline = gfxpoly_to_gfxline(poly);
2462 dev->fill(dev, gfxline, color);
2463 gfxline_free(gfxline);
2468 msg("<trace> draw as stroke, type=%d dots=%d", type, has_dots);
2471 if(i->config_normalize_polygon_positions) {
2473 double startx = 0, starty = 0;
2474 if(line && line->type == gfx_moveTo) {
2478 line = gfxline_move(line, -startx, -starty);
2479 i->shapeposx = (int)(startx*20);
2480 i->shapeposy = (int)(starty*20);
2483 swfoutput_setstrokecolor(dev, color->r, color->g, color->b, color->a);
2484 swfoutput_setlinewidth(dev, width);
2487 drawgfxline(dev, line, 0);
2489 if(i->config_normalize_polygon_positions) {
2490 free(line); //account for _move
2495 static void swf_fill(gfxdevice_t*dev, gfxline_t*line, gfxcolor_t*color)
2497 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
2498 if(line_is_empty(line))
2502 gfxbbox_t r = gfxline_getbbox(line);
2503 int is_outside_page = !is_inside_page(dev, r.xmin, r.ymin) || !is_inside_page(dev, r.xmax, r.ymax);
2505 //if(i->chardatapos && i->chardata[i->chardatapos-1].color.a) {
2508 if(!i->config_ignoredraworder)
2511 if(i->config_normalize_polygon_positions) {
2513 double startx = 0, starty = 0;
2514 if(line && line->type == gfx_moveTo) {
2518 line = gfxline_move(line, -startx, -starty);
2519 i->shapeposx = (int)(startx*20);
2520 i->shapeposy = (int)(starty*20);
2523 swfoutput_setfillcolor(dev, color->r, color->g, color->b, color->a);
2526 drawgfxline(dev, line, 1);
2528 if(i->currentswfid==2 && r.xmin==0 && r.ymin==0 && r.xmax==i->max_x && r.ymax==i->max_y) {
2529 if(i->config_watermark) {
2530 draw_watermark(dev, r, 1);
2534 msg("<trace> end of swf_fill (shapeid=%d)", i->shapeid);
2536 if(i->config_normalize_polygon_positions) {
2537 free(line); //account for _move
2541 static GRADIENT* gfxgradient_to_GRADIENT(gfxgradient_t*gradient)
2544 gfxgradient_t*g = gradient;
2549 GRADIENT* swfgradient = malloc(sizeof(GRADIENT));
2550 swfgradient->num = num;
2551 swfgradient->rgba = malloc(sizeof(swfgradient->rgba[0])*num);
2552 swfgradient->ratios = malloc(sizeof(swfgradient->ratios[0])*num);
2557 swfgradient->ratios[num] = g->pos*255;
2558 swfgradient->rgba[num] = *(RGBA*)&g->color;
2565 static void swf_fillgradient(gfxdevice_t*dev, gfxline_t*line, gfxgradient_t*gradient, gfxgradienttype_t type, gfxmatrix_t*matrix)
2567 if(line_is_empty(line))
2569 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
2571 if(line_is_empty(line))
2574 GRADIENT* swfgradient = gfxgradient_to_GRADIENT(gradient);
2581 double f = type==gfxgradient_radial?4:4;
2583 m.sx = (int)(matrix->m00*20*f); m.r1 = (int)(matrix->m10*20*f);
2584 m.r0 = (int)(matrix->m01*20*f); m.sy = (int)(matrix->m11*20*f);
2585 m.tx = (int)(matrix->tx*20);
2586 m.ty = (int)(matrix->ty*20);
2589 int myshapeid = getNewID(dev);
2590 i->tag = swf_InsertTag(i->tag, ST_DEFINESHAPE2);
2592 swf_ShapeNew(&shape);
2593 int fsid = swf_ShapeAddGradientFillStyle(shape,&m,swfgradient,type==gfxgradient_radial);
2594 swf_SetU16(i->tag, myshapeid);
2595 SRECT r = gfxline_getSWFbbox(line);
2596 r = swf_ClipRect(i->pagebbox, r);
2597 swf_SetRect(i->tag,&r);
2598 swf_SetShapeStyles(i->tag,shape);
2599 swf_ShapeCountBits(shape,NULL,NULL);
2600 swf_SetShapeBits(i->tag,shape);
2601 swf_ShapeSetAll(i->tag,shape,UNDEFINED_COORD,UNDEFINED_COORD,0,fsid,0);
2602 i->swflastx = i->swflasty = UNDEFINED_COORD;
2603 drawgfxline(dev, line, 1);
2604 swf_ShapeSetEnd(i->tag);
2605 swf_ShapeFree(shape);
2607 int depth = getNewDepth(dev);
2608 msg("<trace> Placing gradient, shape ID %d, depth %d", myshapeid, depth);
2609 i->tag = swf_InsertTag(i->tag,ST_PLACEOBJECT2);
2610 swf_ObjectPlace(i->tag,myshapeid,depth,&i->page_matrix,NULL,NULL);
2612 swf_FreeGradient(swfgradient);free(swfgradient);
2615 static SWFFONT* gfxfont_to_swffont(gfxfont_t*font, const char* id)
2617 SWFFONT*swffont = (SWFFONT*)rfx_calloc(sizeof(SWFFONT));
2619 SRECT bounds = {0,0,0,0};
2621 swffont->version = 2;
2622 swffont->name = (U8*)strdup(id);
2623 swffont->layout = (SWFLAYOUT*)rfx_calloc(sizeof(SWFLAYOUT));
2624 swffont->layout->ascent = 0;
2625 swffont->layout->descent = 0;
2626 swffont->layout->leading = 0;
2627 swffont->layout->bounds = (SRECT*)rfx_calloc(sizeof(SRECT)*font->num_glyphs);
2628 swffont->encoding = FONT_ENCODING_UNICODE;
2629 swffont->numchars = font->num_glyphs;
2630 swffont->maxascii = font->max_unicode;
2631 swffont->ascii2glyph = (int*)rfx_calloc(sizeof(int)*swffont->maxascii);
2632 swffont->glyph2ascii = (U16*)rfx_calloc(sizeof(U16)*swffont->numchars);
2633 swffont->glyph = (SWFGLYPH*)rfx_calloc(sizeof(SWFGLYPH)*swffont->numchars);
2634 swffont->glyphnames = (char**)rfx_calloc(sizeof(char*)*swffont->numchars);
2635 for(t=0;t<font->max_unicode;t++) {
2636 swffont->ascii2glyph[t] = font->unicode2glyph[t];
2638 for(t=0;t<font->num_glyphs;t++) {
2642 swffont->glyph2ascii[t] = font->glyphs[t].unicode;
2643 if(swffont->glyph2ascii[t] == 0xffff || swffont->glyph2ascii[t] == 0x0000) {
2644 /* flash 8 flashtype requires unique unicode IDs for each character.
2645 We use the Unicode private user area to assign characters, hoping that
2646 the font doesn't contain more than 2048 glyphs */
2647 swffont->glyph2ascii[t] = 0xe000 + (t&0x1fff);
2650 if(font->glyphs[t].name) {
2651 swffont->glyphnames[t] = strdup(font->glyphs[t].name);
2653 swffont->glyphnames[t] = 0;
2655 advance = (int)(font->glyphs[t].advance);
2657 swf_Shape01DrawerInit(&draw, 0);
2658 line = font->glyphs[t].line;
2661 c.x = line->sx * GLYPH_SCALE; c.y = line->sy * GLYPH_SCALE;
2662 to.x = line->x * GLYPH_SCALE; to.y = line->y * GLYPH_SCALE;
2663 if(line->type == gfx_moveTo) {
2664 draw.moveTo(&draw, &to);
2665 } else if(line->type == gfx_lineTo) {
2666 draw.lineTo(&draw, &to);
2667 } else if(line->type == gfx_splineTo) {
2668 draw.splineTo(&draw, &c, &to);
2673 swffont->glyph[t].shape = swf_ShapeDrawerToShape(&draw);
2674 swffont->layout->bounds[t] = swf_ShapeDrawerGetBBox(&draw);
2676 int xmax = swffont->layout->bounds[t].xmax / 20;
2677 if(xmax>0 && xmax*2 < advance) {
2678 printf("fix bad advance value: bbox=%d, advance=%d (%f)\n", xmax, advance, font->glyphs[t].advance);
2682 if(advance<32768/20) {
2683 swffont->glyph[t].advance = advance*20;
2685 swffont->glyph[t].advance = 32767;
2688 draw.dealloc(&draw);
2690 swf_ExpandRect2(&bounds, &swffont->layout->bounds[t]);
2694 /* Flash player will use the advance value from the char, and the ascent/descent values
2695 from the layout for text selection.
2696 ascent will extend the char into negative y direction, from the baseline, while descent
2697 will extend in positive y direction, also from the baseline.
2698 The baseline is defined as the y-position zero
2701 swffont->layout->ascent = -bounds.ymin;
2702 if(swffont->layout->ascent < 0)
2703 swffont->layout->ascent = 0;
2704 swffont->layout->descent = bounds.ymax;
2705 if(swffont->layout->descent < 0)
2706 swffont->layout->descent = 0;
2707 swffont->layout->leading = bounds.ymax - bounds.ymin;
2712 static void swf_addfont(gfxdevice_t*dev, gfxfont_t*font)
2714 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
2716 if(i->swffont && i->swffont->name && !strcmp((char*)i->swffont->name,font->id))
2717 return; // the requested font is the current font
2719 fontlist_t*last=0,*l = i->fontlist;
2722 if(!strcmp((char*)l->swffont->name, font->id)) {
2723 return; // we already know this font
2727 l = (fontlist_t*)rfx_calloc(sizeof(fontlist_t));
2728 l->swffont = gfxfont_to_swffont(font, font->id);
2735 swf_FontSetID(l->swffont, getNewID(i->dev));
2737 if(getScreenLogLevel() >= LOGLEVEL_DEBUG) {
2739 // print font information
2740 msg("<debug> Font %s",font->id);
2741 msg("<debug> | ID: %d", l->swffont->id);
2742 msg("<debug> | Version: %d", l->swffont->version);
2743 msg("<debug> | Name: %s", l->swffont->name);
2744 msg("<debug> | Numchars: %d", l->swffont->numchars);
2745 msg("<debug> | Maxascii: %d", l->swffont->maxascii);
2746 msg("<debug> | Style: %d", l->swffont->style);
2747 msg("<debug> | Encoding: %d", l->swffont->encoding);
2748 for(iii=0; iii<l->swffont->numchars;iii++) {
2749 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,
2750 l->swffont->layout->bounds[iii].xmin/20.0,
2751 l->swffont->layout->bounds[iii].ymin/20.0,
2752 l->swffont->layout->bounds[iii].xmax/20.0,
2753 l->swffont->layout->bounds[iii].ymax/20.0
2756 for(t=0;t<l->swffont->maxascii;t++) {
2757 if(l->swffont->ascii2glyph[t] == iii)
2758 msg("<debug> | - maps to %d",t);
2764 static void swf_switchfont(gfxdevice_t*dev, const char*fontid)
2766 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
2768 if(i->swffont && i->swffont->name && !strcmp((char*)i->swffont->name,fontid))
2769 return; // the requested font is the current font
2771 fontlist_t*l = i->fontlist;
2773 if(!strcmp((char*)l->swffont->name, fontid)) {
2774 i->swffont = l->swffont;
2779 msg("<error> Unknown font id: %s", fontid);
2783 static void swf_drawchar(gfxdevice_t*dev, gfxfont_t*font, int glyph, gfxcolor_t*color, gfxmatrix_t*matrix)
2785 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
2787 msg("<error> swf_drawchar called (glyph %d) without font", glyph);
2790 if(!i->swffont || !i->swffont->name || strcmp((char*)i->swffont->name,font->id)) // not equal to current font
2792 /* TODO: remove the need for this (enhance getcharacterbbox so that it can cope
2793 with multiple fonts */
2795 swf_switchfont(dev, font->id); // set the current font
2798 msg("<warning> swf_drawchar: Font is NULL");
2801 if(glyph<0 || glyph>=i->swffont->numchars) {
2802 msg("<warning> No character %d in font %s (%d chars)", glyph, FIXNULL((char*)i->swffont->name), i->swffont->numchars);
2806 setfontscale(dev, matrix->m00, matrix->m01, matrix->m10, matrix->m11, matrix->tx, matrix->ty, 0);
2808 double det = i->fontmatrix.sx/65536.0 * i->fontmatrix.sy/65536.0 -
2809 i->fontmatrix.r0/65536.0 * i->fontmatrix.r1/65536.0;
2810 if(fabs(det) < 0.0005) {
2811 /* x direction equals y direction- the text is invisible */
2812 msg("<verbose> Not drawing invisible character character %d (det=%f, m=[%f %f;%f %f]\n", glyph,
2814 i->fontmatrix.sx/65536.0, i->fontmatrix.r1/65536.0,
2815 i->fontmatrix.r0/65536.0, i->fontmatrix.sy/65536.0);
2819 /*if(i->swffont->glyph[glyph].shape->bitlen <= 16) {
2820 msg("<warning> Glyph %d in current charset (%s, %d characters) is empty",
2821 glyph, FIXNULL((char*)i->swffont->name), i->swffont->numchars);
2825 /* calculate character position with respect to the current font matrix */
2826 double s = 20 * GLYPH_SCALE / det;
2827 double px = matrix->tx - i->fontmatrix.tx/20.0;
2828 double py = matrix->ty - i->fontmatrix.ty/20.0;
2829 int x = (SCOORD)(( px * i->fontmatrix.sy/65536.0 - py * i->fontmatrix.r1/65536.0)*s);
2830 int y = (SCOORD)((- px * i->fontmatrix.r0/65536.0 + py * i->fontmatrix.sx/65536.0)*s);
2831 if(x>32767 || x<-32768 || y>32767 || y<-32768) {
2832 msg("<verbose> Moving character origin to %f %f\n", matrix->tx, matrix->ty);
2834 setfontscale(dev, matrix->m00, matrix->m01, matrix->m10, matrix->m11, matrix->tx, matrix->ty, 1);
2835 /* since we just moved the char origin to the current char's position,
2836 it now has the relative position (0,0) */
2845 msg("<trace> Drawing char %d in font %d at %d,%d in color %02x%02x%02x%02x",
2846 glyph, i->swffont->id, x, y, color->r, color->g, color->b, color->a);
2848 putcharacter(dev, i->swffont->id, glyph, x, y, i->current_font_size, *(RGBA*)color);
2849 swf_FontUseGlyph(i->swffont, glyph);