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_jpegquality;
84 int config_storeallcharacters;
85 int config_enablezlib;
86 int config_insertstoptag;
88 int config_flashversion;
89 int config_reordertags;
90 int config_showclipshapes;
91 int config_splinemaxerror;
92 int config_fontsplinemaxerror;
93 int config_filloverlap;
96 int config_disable_polygon_conversion;
97 int config_normalize_polygon_positions;
98 RGBA config_linkcolor;
99 float config_minlinewidth;
100 double config_caplinewidth;
101 char* config_linktarget;
102 char*config_internallinkfunction;
103 char*config_externallinkfunction;
105 double config_framerate;
109 fontlist_t* fontlist;
146 int pic_height[1024];
152 char fillstylechanged;
154 int jpeg; //next image type
161 chardata_t chardata[CHARDATAMAX];
168 int current_font_size;
170 double lastfontm11,lastfontm12,lastfontm21,lastfontm22;
181 } swfoutput_internal;
183 static void swf_fillbitmap(gfxdevice_t*driver, gfxline_t*line, gfximage_t*img, gfxmatrix_t*move, gfxcxform_t*cxform);
184 static int swf_setparameter(gfxdevice_t*driver, const char*key, const char*value);
185 static void swf_drawstroke(gfxdevice_t*dev, gfxline_t*line, gfxcoord_t width, gfxcolor_t*color, gfx_capType cap_style, gfx_joinType joint_style, gfxcoord_t miterLimit);
186 static void swf_startclip(gfxdevice_t*dev, gfxline_t*line);
187 static void swf_endclip(gfxdevice_t*dev);
188 static void swf_stroke(gfxdevice_t*dev, gfxline_t*line, gfxcoord_t width, gfxcolor_t*color, gfx_capType cap_style, gfx_joinType joint_style, gfxcoord_t miterLimit);
189 static void swf_fill(gfxdevice_t*dev, gfxline_t*line, gfxcolor_t*color);
190 static void swf_fillbitmap(gfxdevice_t*dev, gfxline_t*line, gfximage_t*img, gfxmatrix_t*matrix, gfxcxform_t*cxform);
191 static void swf_fillgradient(gfxdevice_t*dev, gfxline_t*line, gfxgradient_t*gradient, gfxgradienttype_t type, gfxmatrix_t*matrix);
192 static void swf_drawchar(gfxdevice_t*dev, gfxfont_t*font, int glyph, gfxcolor_t*color, gfxmatrix_t*matrix);
193 static void swf_addfont(gfxdevice_t*dev, gfxfont_t*font);
194 static void swf_drawlink(gfxdevice_t*dev, gfxline_t*line, const char*action);
195 static void swf_startframe(gfxdevice_t*dev, int width, int height);
196 static void swf_endframe(gfxdevice_t*dev);
197 static gfxresult_t* swf_finish(gfxdevice_t*driver);
199 static swfoutput_internal* init_internal_struct()
201 swfoutput_internal*i = (swfoutput_internal*)malloc(sizeof(swfoutput_internal));
202 memset(i, 0, sizeof(swfoutput_internal));
226 i->fillstylechanged = 0;
233 i->config_dumpfonts=0;
234 i->config_ppmsubpixels=0;
235 i->config_jpegsubpixels=0;
236 i->config_opennewwindow=1;
237 i->config_ignoredraworder=0;
238 i->config_drawonlyshapes=0;
239 i->config_jpegquality=85;
240 i->config_storeallcharacters=0;
241 i->config_enablezlib=0;
242 i->config_insertstoptag=0;
243 i->config_flashversion=6;
244 i->config_framerate=0.25;
245 i->config_splinemaxerror=1;
246 i->config_fontsplinemaxerror=1;
247 i->config_filloverlap=0;
249 i->config_bboxvars=0;
250 i->config_showclipshapes=0;
251 i->config_minlinewidth=0.05;
252 i->config_caplinewidth=1;
253 i->config_linktarget=0;
254 i->config_internallinkfunction=0;
255 i->config_externallinkfunction=0;
256 i->config_reordertags=1;
258 i->config_linkcolor.r = i->config_linkcolor.g = i->config_linkcolor.b = 255;
259 i->config_linkcolor.a = 0x40;
264 static int id_error = 0;
266 static U16 getNewID(gfxdevice_t* dev)
268 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
269 if(i->currentswfid == 65535) {
271 msg("<error> ID Table overflow");
272 msg("<error> This file is too complex to render- SWF only supports 65536 shapes at once");
278 return ++i->currentswfid;
280 static U16 getNewDepth(gfxdevice_t* dev)
282 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
283 if(i->depth == 65520) {
285 msg("<error> Depth Table overflow");
286 msg("<error> This file is too complex to render- SWF only supports 65536 shapes at once");
295 static void startshape(gfxdevice_t* dev);
296 static void starttext(gfxdevice_t* dev);
297 static void endshape(gfxdevice_t* dev);
298 static void endtext(gfxdevice_t* dev);
300 typedef struct _plotxy
305 // write a move-to command into the swf
306 static int movetoxy(gfxdevice_t*dev, TAG*tag, plotxy_t p0)
308 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
309 int rx = (int)(p0.x*20);
310 int ry = (int)(p0.y*20);
311 if(rx!=i->swflastx || ry!=i->swflasty || i->fillstylechanged) {
312 swf_ShapeSetMove (tag, i->shape, rx,ry);
313 i->fillstylechanged = 0;
320 static int moveto(gfxdevice_t*dev, TAG*tag, double x, double y)
322 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
326 return movetoxy(dev, tag, p);
328 static void addPointToBBox(gfxdevice_t*dev, int px, int py)
330 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
336 swf_ExpandRect(&i->bboxrect, p);
338 swf_ExpandRect3(&i->bboxrect, p, i->linewidth*3/2);
342 /*static void plot(gfxdevice_t*dev, int x, int y, TAG*tag)
344 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
345 int width = i->linewidth/4;
349 //swf_ShapeSetLine(tag, i->shape,-width,-width);
350 //swf_ShapeSetLine(tag, i->shape,width*2,0);
351 //swf_ShapeSetLine(tag, i->shape,0,width*2);
352 //swf_ShapeSetLine(tag, i->shape,-width*2,0);
353 //swf_ShapeSetLine(tag, i->shape,0,-width*2);
354 //swf_ShapeSetLine(tag, i->shape,width,width);
357 swf_ShapeSetLine(tag, i->shape,-width,0);
358 swf_ShapeSetLine(tag, i->shape,width,-width);
359 swf_ShapeSetLine(tag, i->shape,width,width);
360 swf_ShapeSetLine(tag, i->shape,-width,width);
361 swf_ShapeSetLine(tag, i->shape,-width,-width);
362 swf_ShapeSetLine(tag, i->shape,width,0);
364 addPointToBBox(dev, x-width ,y-width);
365 addPointToBBox(dev, x+width ,y+width);
368 // write a line-to command into the swf
369 static void linetoxy(gfxdevice_t*dev, TAG*tag, plotxy_t p0)
371 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
372 int px = (int)(p0.x*20);
373 int py = (int)(p0.y*20);
374 int rx = (px-i->swflastx);
375 int ry = (py-i->swflasty);
377 swf_ShapeSetLine (tag, i->shape, rx,ry);
378 addPointToBBox(dev, i->swflastx,i->swflasty);
379 addPointToBBox(dev, px,py);
380 }/* else if(!i->fill) {
381 // treat lines of length 0 as plots, making them
382 // at least 1 twip wide so Flash will display them
383 plot(dev, i->swflastx, i->swflasty, tag);
390 static void lineto(gfxdevice_t*dev, TAG*tag, double x, double y)
395 linetoxy(dev,tag, p);
398 // write a spline-to command into the swf
399 static void splineto(gfxdevice_t*dev, TAG*tag, plotxy_t control,plotxy_t end)
401 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
402 int lastlastx = i->swflastx;
403 int lastlasty = i->swflasty;
405 int cx = ((int)(control.x*20)-i->swflastx);
406 int cy = ((int)(control.y*20)-i->swflasty);
409 int ex = ((int)(end.x*20)-i->swflastx);
410 int ey = ((int)(end.y*20)-i->swflasty);
414 if((cx || cy) && (ex || ey)) {
415 swf_ShapeSetCurve(tag, i->shape, cx,cy,ex,ey);
416 addPointToBBox(dev, lastlastx ,lastlasty );
417 addPointToBBox(dev, lastlastx+cx,lastlasty+cy);
418 addPointToBBox(dev, lastlastx+cx+ex,lastlasty+cy+ey);
419 } else if(cx || cy || ex || ey) {
420 swf_ShapeSetLine(tag, i->shape, cx+ex,cy+ey);
421 addPointToBBox(dev, lastlastx ,lastlasty );
422 addPointToBBox(dev, lastlastx+cx,lastlasty+cy);
423 addPointToBBox(dev, lastlastx+cx+ex,lastlasty+cy+ey);
429 /* write a line, given two points and the transformation
431 /*static void line(gfxdevice_t*dev, TAG*tag, plotxy_t p0, plotxy_t p1)
433 moveto(dev, tag, p0);
434 lineto(dev, tag, p1);
437 void resetdrawer(gfxdevice_t*dev)
439 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
444 static void stopFill(gfxdevice_t*dev)
446 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
447 if(i->lastwasfill!=0)
449 swf_ShapeSetStyle(i->tag,i->shape,i->linestyleid,0x8000,0);
450 i->fillstylechanged = 1;
454 static void startFill(gfxdevice_t*dev)
456 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
457 if(i->lastwasfill!=1)
459 swf_ShapeSetStyle(i->tag,i->shape,0x8000,i->fillstyleid,0);
460 i->fillstylechanged = 1;
465 static inline int colorcompare(gfxdevice_t*dev, RGBA*a,RGBA*b)
477 static SRECT getcharacterbbox(gfxdevice_t*dev, SWFFONT*font, MATRIX* m)
479 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
482 memset(&r, 0, sizeof(r));
485 if(debug) printf("\n");
486 for(t=0;t<i->chardatapos;t++)
488 if(i->chardata[t].fontid != font->id) {
489 msg("<error> Internal error: fontid %d != fontid %d", i->chardata[t].fontid, font->id);
492 SRECT b = font->layout->bounds[i->chardata[t].charid];
493 b.xmin *= i->chardata[t].size;
494 b.ymin *= i->chardata[t].size;
495 b.xmax *= i->chardata[t].size;
496 b.ymax *= i->chardata[t].size;
498 /* divide by 1024, rounding xmax/ymax up */
499 b.xmax += 1023; b.ymax += 1023; b.xmin /= 1024; b.ymin /= 1024; b.xmax /= 1024; b.ymax /= 1024;
501 b.xmin += i->chardata[t].x;
502 b.ymin += i->chardata[t].y;
503 b.xmax += i->chardata[t].x;
504 b.ymax += i->chardata[t].y;
506 /* until we solve the INTERNAL_SCALING problem (see below)
507 make sure the bounding box is big enough */
513 b = swf_TurnRect(b, m);
515 if(debug) printf("(%f,%f,%f,%f) -> (%f,%f,%f,%f) [font %d/%d, char %d]\n",
516 font->layout->bounds[i->chardata[t].charid].xmin/20.0,
517 font->layout->bounds[i->chardata[t].charid].ymin/20.0,
518 font->layout->bounds[i->chardata[t].charid].xmax/20.0,
519 font->layout->bounds[i->chardata[t].charid].ymax/20.0,
524 i->chardata[t].fontid,
526 i->chardata[t].charid
528 swf_ExpandRect2(&r, &b);
530 if(debug) printf("-----> (%f,%f,%f,%f)\n",
538 static void putcharacters(gfxdevice_t*dev, TAG*tag)
540 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
544 color.r = i->chardata[0].color.r^255;
553 int charadvance[128];
556 int glyphbits=1; //TODO: can this be zero?
559 if(tag->id != ST_DEFINETEXT &&
560 tag->id != ST_DEFINETEXT2) {
561 msg("<error> internal error: putcharacters needs an text tag, not %d\n",tag->id);
564 if(!i->chardatapos) {
565 msg("<warning> putcharacters called with zero characters");
568 for(pass = 0; pass < 2; pass++)
578 advancebits++; // add sign bit
579 swf_SetU8(tag, glyphbits);
580 swf_SetU8(tag, advancebits);
583 for(t=0;t<=i->chardatapos;t++)
585 if(lastfontid != i->chardata[t].fontid ||
586 lastx!=i->chardata[t].x ||
587 lasty!=i->chardata[t].y ||
588 !colorcompare(dev,&color, &i->chardata[t].color) ||
590 lastsize != i->chardata[t].size ||
593 if(charstorepos && pass==0)
596 for(s=0;s<charstorepos;s++)
598 while(charids[s]>=(1<<glyphbits))
600 while(charadvance[s]>=(1<<advancebits))
604 if(charstorepos && pass==1)
606 tag->writeBit = 0; // Q&D
607 swf_SetBits(tag, 0, 1); // GLYPH Record
608 swf_SetBits(tag, charstorepos, 7); // number of glyphs
610 for(s=0;s<charstorepos;s++)
612 swf_SetBits(tag, charids[s], glyphbits);
613 swf_SetBits(tag, charadvance[s], advancebits);
618 if(pass == 1 && t<i->chardatapos)
624 if(lastx != i->chardata[t].x ||
625 lasty != i->chardata[t].y)
627 newx = i->chardata[t].x;
628 newy = i->chardata[t].y;
634 if(!colorcompare(dev,&color, &i->chardata[t].color))
636 color = i->chardata[t].color;
639 font.id = i->chardata[t].fontid;
640 if(lastfontid != i->chardata[t].fontid || lastsize != i->chardata[t].size)
643 tag->writeBit = 0; // Q&D
644 swf_TextSetInfoRecord(tag, newfont, i->chardata[t].size, newcolor, newx,newy);
647 lastfontid = i->chardata[t].fontid;
648 lastx = i->chardata[t].x;
649 lasty = i->chardata[t].y;
650 lastsize = i->chardata[t].size;
653 if(t==i->chardatapos)
657 int nextt = t==i->chardatapos-1?t:t+1;
658 int rel = i->chardata[nextt].x-i->chardata[t].x;
659 if(rel>=0 && (rel<(1<<(advancebits-1)) || pass==0)) {
661 lastx=i->chardata[nextt].x;
665 lastx=i->chardata[t].x;
667 charids[charstorepos] = i->chardata[t].charid;
668 charadvance[charstorepos] = advance;
675 static void putcharacter(gfxdevice_t*dev, int fontid, int charid, int x,int y, int size, RGBA color)
677 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
678 if(i->chardatapos == CHARDATAMAX)
680 msg("<warning> Character buffer too small. SWF will be slightly bigger");
684 i->chardata[i->chardatapos].fontid = fontid;
685 i->chardata[i->chardatapos].charid = charid;
686 i->chardata[i->chardatapos].x = x;
687 i->chardata[i->chardatapos].y = y;
688 i->chardata[i->chardatapos].color = color;
689 i->chardata[i->chardatapos].size = size;
693 /* Notice: we can only put chars in the range -1639,1638 (-32768/20,32768/20).
694 So if we set this value to high, the char coordinates will overflow.
695 If we set it to low, however, the char positions will be inaccurate */
696 #define GLYPH_SCALE 1
698 static void endtext(gfxdevice_t*dev)
700 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
704 i->tag = swf_InsertTag(i->tag,ST_DEFINETEXT2);
705 swf_SetU16(i->tag, i->textid);
708 r = getcharacterbbox(dev, i->swffont, &i->fontmatrix);
709 r = swf_ClipRect(i->pagebbox, r);
710 swf_SetRect(i->tag,&r);
712 swf_SetMatrix(i->tag,&i->fontmatrix);
714 msg("<trace> Placing text (%d characters) as ID %d", i->chardatapos, i->textid);
716 putcharacters(dev, i->tag);
719 if(i->swf->fileVersion >= 8) {
720 i->tag = swf_InsertTag(i->tag, ST_CSMTEXTSETTINGS);
721 swf_SetU16(i->tag, i->textid);
723 //swf_SetU8(i->tag, /*subpixel grid*/(2<<3)|/*flashtype*/0x40);
724 swf_SetU8(i->tag, /*grid*/(1<<3)|/*flashtype*/0x40);
725 //swf_SetU8(i->tag, /*no grid*/(0<<3)|/*flashtype*/0x40);
727 swf_SetU32(i->tag, 0);//thickness
728 swf_SetU32(i->tag, 0);//sharpness
729 swf_SetU8(i->tag, 0);//reserved
731 i->tag = swf_InsertTag(i->tag,ST_PLACEOBJECT2);
733 swf_ObjectPlace(i->tag,i->textid,getNewDepth(dev),&i->page_matrix,NULL,NULL);
737 /* set's the matrix which is to be applied to characters drawn by swfoutput_drawchar() */
738 static void setfontscale(gfxdevice_t*dev,double m11,double m12, double m21,double m22,double x, double y, char force)
744 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
745 if(i->lastfontm11 == m11 &&
746 i->lastfontm12 == m12 &&
747 i->lastfontm21 == m21 &&
748 i->lastfontm22 == m22 && !force)
753 i->lastfontm11 = m11;
754 i->lastfontm12 = m12;
755 i->lastfontm21 = m21;
756 i->lastfontm22 = m22;
758 double xsize = sqrt(m11*m11 + m12*m12);
759 double ysize = sqrt(m21*m21 + m22*m22);
760 i->current_font_size = (xsize>ysize?xsize:ysize)*1;
761 if(i->current_font_size < 1)
762 i->current_font_size = 1;
763 double ifs = 1.0 / (i->current_font_size*GLYPH_SCALE);
766 m.sx = (S32)((m11*ifs)*65536); m.r1 = (S32)((m21*ifs)*65536);
767 m.r0 = (S32)((m12*ifs)*65536); m.sy = (S32)((m22*ifs)*65536);
768 /* this is the position of the first char to set a new fontmatrix-
769 we hope that it's close enough to all other characters using the
770 font, so we use its position as origin for the matrix */
776 static int watermark2_width=47;
777 static int watermark2_height=11;
778 static int watermark2[47] = {95,1989,71,0,2015,337,1678,0,2015,5,1921,320,1938,25,2006,1024,
779 1042,21,13,960,1039,976,8,2000,1359,1088,31,1989,321,1728,0,1152,
780 1344,832,0,1984,0,896,1088,1088,896,0,1984,128,256,512,1984};
782 static void draw_watermark(gfxdevice_t*dev, gfxbbox_t r, char drawall)
784 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
785 double wx = r.xmax / 5.0;
786 double tx = r.xmax*4.0 / 5.0;
787 double ty = r.ymax-wx*watermark2_height/watermark2_width;
788 double sx = (r.xmax - tx) / watermark2_width;
789 double sy = (r.ymax - ty) / watermark2_height;
792 if(ty > 0 && px > 1.0 && py > 1.0) {
794 for(y=0;y<watermark2_height;y++)
795 for(x=0;x<watermark2_width;x++) {
796 if(((watermark2[x]>>y)&1)) {
797 if(!drawall && rand()%5)
799 unsigned int b = rand();
800 moveto(dev, i->tag, x*sx+tx+((b>>1)&1)/20.0, y*sy+ty+((b>>3)&1)/20.0);
801 lineto(dev, i->tag, x*sx+px+tx+((b>>2)&1)/20.0, y*sy+ty+((b>>3)&1)/20.0);
802 lineto(dev, i->tag, x*sx+px+tx+((b>>2)&1)/20.0, y*sy+py+ty+((b>>4)&1)/20.0);
803 lineto(dev, i->tag, x*sx+tx+((b>>1)&1)/20.0, y*sy+py+ty+((b>>4)&1)/20.0);
804 lineto(dev, i->tag, x*sx+tx+((b>>1)&1)/20.0, y*sy+ty+((b>>3)&1)/20.0);
810 static void swfoutput_setfillcolor(gfxdevice_t* dev, U8 r, U8 g, U8 b, U8 a)
812 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
813 if(i->fillrgb.r == r &&
816 i->fillrgb.a == a) return;
825 static void insert_watermark(gfxdevice_t*dev, char drawall)
827 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
828 if(!drawall && i->watermarks>20)
834 swfoutput_setfillcolor(dev, 0,0,255,192);
836 swfoutput_setfillcolor(dev, rand(),rand(),rand(),(rand()&127)+128);
841 gfxbbox_t r; r.xmin = r.ymin = 0;
844 draw_watermark(dev, r, drawall);
850 static void endpage(gfxdevice_t*dev)
852 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
863 if(i->config_watermark) {
864 insert_watermark(dev, 1);
870 static void addViewer(gfxdevice_t* dev)
872 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
875 RGBA button_colors[3]= {{0xbf,0x00,0x00,0x80},{0xbf,0x20,0x20,0xc0}, {0xbf,0xc0,0xc0,0xff}};
877 int button_sizex = 20;
878 int button_sizey = 20;
880 RGBA black = {255,0,0,0};
882 i->tag = swf_InsertTag(i->tag,ST_DEFINESHAPE3);
884 int ls1 = swf_ShapeAddLineStyle(s,40,&black);
885 int fs1 = swf_ShapeAddSolidFillStyle(s,&button_colors[t/2]);
886 int shapeid = ids[t] = getNewID(dev);
887 swf_SetU16(i->tag,shapeid);
889 r.xmin = -20*button_sizex;
890 r.xmax = 20*button_sizex;
892 r.ymax = 40*button_sizey;
893 swf_SetRect(i->tag,&r); // set shape bounds
894 swf_SetShapeHeader(i->tag,s); // write all styles to tag
895 swf_ShapeSetAll(i->tag,s,0*button_sizex,0,ls1,fs1,0);
896 swf_ShapeSetLine(i->tag,s,(1-(t&1)*2)*20*button_sizex,20*button_sizey);
897 swf_ShapeSetLine(i->tag,s,-(1-(t&1)*2)*20*button_sizex,20*button_sizey);
898 swf_ShapeSetLine(i->tag,s,0,-40*button_sizey);
899 swf_ShapeSetEnd(i->tag); // finish drawing
900 swf_ShapeFree(s); // clean shape structure (which isn't needed anymore after writing the tag)
902 ActionTAG*a1=0,*a2=0,*a3=0;
903 a1 = action_NextFrame(a1);
904 a1 = action_Stop(a1);
907 a2 = action_PreviousFrame(a2);
908 a2 = action_Stop(a2);
911 a3 = action_Stop(a3);
914 i->tag = swf_InsertTag(i->tag, ST_DOACTION);
915 swf_ActionSet(i->tag,a3);
917 i->tag = swf_InsertTag(i->tag,ST_DEFINEBUTTON);
918 int buttonid1 = getNewID(dev);
919 swf_SetU16(i->tag, buttonid1);
920 swf_ButtonSetRecord(i->tag,BS_UP|BS_HIT,ids[0],1,NULL,NULL);
921 swf_ButtonSetRecord(i->tag,BS_OVER,ids[2],1,NULL,NULL);
922 swf_ButtonSetRecord(i->tag,BS_DOWN,ids[4],1,NULL,NULL);
923 swf_SetU8(i->tag,0); // end of button records
924 swf_ActionSet(i->tag,a1);
926 i->tag = swf_InsertTag(i->tag,ST_DEFINEBUTTON);
927 int buttonid2 = getNewID(dev);
928 swf_SetU16(i->tag, buttonid2);
929 swf_ButtonSetRecord(i->tag,BS_UP|BS_HIT,ids[1],1,NULL,NULL);
930 swf_ButtonSetRecord(i->tag,BS_OVER,ids[3],1,NULL,NULL);
931 swf_ButtonSetRecord(i->tag,BS_DOWN,ids[5],1,NULL,NULL);
932 swf_SetU8(i->tag,0); // end of button records
933 swf_ActionSet(i->tag,a2);
935 i->tag = swf_InsertTag(i->tag,ST_PLACEOBJECT2);
937 swf_GetMatrix(0, &m);
938 m.tx = button_sizex*20+200;
939 swf_ObjectPlace(i->tag, buttonid2, 65534,&m,0,0);
940 i->tag = swf_InsertTag(i->tag,ST_PLACEOBJECT2);
941 m.tx = button_sizex*20+200+200;
942 swf_ObjectPlace(i->tag, buttonid1, 65535,&m,0,0);
946 void swf_startframe(gfxdevice_t*dev, int width, int height)
948 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
950 if(i->config_protect) {
951 i->tag = swf_InsertTag(i->tag, ST_PROTECT);
952 i->config_protect = 0;
954 if(i->config_simpleviewer) {
959 if(!i->firstpage && !i->pagefinished)
962 msg("<verbose> Starting new SWF page of size %dx%d", width, height);
964 swf_GetMatrix(0, &i->page_matrix);
965 i->page_matrix.tx = 0;
966 i->page_matrix.ty = 0;
973 /* create a bbox structure with the page size. This is used
974 for clipping shape and text bounding boxes. As we don't want to
975 generate bounding boxes which extend beyond the movie size (in
976 order to not confuse Flash), we clip everything against i->pagebbox */
977 i->pagebbox.xmin = 0;
978 i->pagebbox.ymin = 0;
979 i->pagebbox.xmax = width*20;
980 i->pagebbox.ymax = height*20;
982 /* increase SWF's bounding box */
983 swf_ExpandRect2(&i->swf->movieSize, &i->pagebbox);
985 i->lastframeno = i->frameno;
990 void swf_endframe(gfxdevice_t*dev)
992 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
997 if( (i->swf->fileVersion <= 8) && (i->config_insertstoptag) ) {
999 atag = action_Stop(atag);
1000 atag = action_End(atag);
1001 i->tag = swf_InsertTag(i->tag,ST_DOACTION);
1002 swf_ActionSet(i->tag,atag);
1004 i->tag = swf_InsertTag(i->tag,ST_SHOWFRAME);
1007 for(i->depth;i->depth>i->startdepth;i->depth--) {
1008 i->tag = swf_InsertTag(i->tag,ST_REMOVEOBJECT2);
1009 swf_SetU16(i->tag,i->depth);
1011 i->depth = i->startdepth;
1014 static void setBackground(gfxdevice_t*dev, int x1, int y1, int x2, int y2)
1016 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1018 rgb.a = rgb.r = rgb.g = rgb.b = 0xff;
1022 int shapeid = getNewID(dev);
1027 i->tag = swf_InsertTag(i->tag, ST_DEFINESHAPE);
1029 fs1 = swf_ShapeAddSolidFillStyle(s, &rgb);
1030 swf_SetU16(i->tag,shapeid);
1031 swf_SetRect(i->tag,&r);
1032 swf_SetShapeHeader(i->tag,s);
1033 swf_ShapeSetAll(i->tag,s,x1,y1,ls1,fs1,0);
1034 swf_ShapeSetLine(i->tag,s,(x2-x1),0);
1035 swf_ShapeSetLine(i->tag,s,0,(y2-y1));
1036 swf_ShapeSetLine(i->tag,s,(x1-x2),0);
1037 swf_ShapeSetLine(i->tag,s,0,(y1-y2));
1038 swf_ShapeSetEnd(i->tag);
1040 i->tag = swf_InsertTag(i->tag, ST_PLACEOBJECT2);
1041 swf_ObjectPlace(i->tag,shapeid,getNewDepth(dev),0,0,0);
1042 i->tag = swf_InsertTag(i->tag, ST_PLACEOBJECT2);
1043 swf_ObjectPlaceClip(i->tag,shapeid,getNewDepth(dev),0,0,0,65535);
1046 /* initialize the swf writer */
1047 void gfxdevice_swf_init(gfxdevice_t* dev)
1049 memset(dev, 0, sizeof(gfxdevice_t));
1053 dev->internal = init_internal_struct(); // set config to default values
1055 dev->startpage = swf_startframe;
1056 dev->endpage = swf_endframe;
1057 dev->finish = swf_finish;
1058 dev->fillbitmap = swf_fillbitmap;
1059 dev->setparameter = swf_setparameter;
1060 dev->stroke = swf_stroke;
1061 dev->startclip = swf_startclip;
1062 dev->endclip = swf_endclip;
1063 dev->fill = swf_fill;
1064 dev->fillbitmap = swf_fillbitmap;
1065 dev->fillgradient = swf_fillgradient;
1066 dev->addfont = swf_addfont;
1067 dev->drawchar = swf_drawchar;
1068 dev->drawlink = swf_drawlink;
1070 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1073 msg("<verbose> initializing swf output\n", i->max_x,i->max_y);
1077 i->swf = (SWF*)rfx_calloc(sizeof(SWF));
1078 i->swf->fileVersion = 0;
1079 i->swf->frameRate = 0x80;
1080 i->swf->movieSize.xmin = 0;
1081 i->swf->movieSize.ymin = 0;
1082 i->swf->movieSize.xmax = 0;
1083 i->swf->movieSize.ymax = 0;
1085 i->swf->firstTag = swf_InsertTag(NULL,ST_SETBACKGROUNDCOLOR);
1086 i->tag = i->swf->firstTag;
1088 rgb.a = rgb.r = rgb.g = rgb.b = 0xff;
1090 swf_SetRGB(i->tag,&rgb);
1092 i->startdepth = i->depth = 0;
1095 static void startshape(gfxdevice_t*dev)
1097 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1102 //if(i->chardatapos && i->chardata[i->chardatapos-1].color.a)
1105 i->tag = swf_InsertTag(i->tag,ST_DEFINESHAPE3);
1107 swf_ShapeNew(&i->shape);
1108 i->linestyleid = swf_ShapeAddLineStyle(i->shape,i->linewidth,&i->strokergb);
1109 i->fillstyleid = swf_ShapeAddSolidFillStyle(i->shape,&i->fillrgb);
1111 RGBA markcol = {0,i->mark[0],i->mark[1],i->mark[2]};
1112 swf_ShapeAddSolidFillStyle(i->shape,&markcol);
1115 i->shapeid = getNewID(dev);
1117 msg("<debug> Using shape id %d", i->shapeid);
1119 swf_SetU16(i->tag,i->shapeid); // ID
1121 i->bboxrectpos = i->tag->len;
1123 swf_SetRect(i->tag,&i->pagebbox);
1125 memset(&i->bboxrect, 0, sizeof(i->bboxrect));
1127 swf_SetShapeStyles(i->tag,i->shape);
1128 swf_ShapeCountBits(i->shape,NULL,NULL);
1129 swf_SetShapeBits(i->tag,i->shape);
1131 /* TODO: do we really need this? */
1132 //swf_ShapeSetAll(i->tag,i->shape,/*x*/0,/*y*/0,i->linestyleid,0,0);
1133 //swf_ShapeSetAll(i->tag,i->shape,/*x*/UNDEFINED_COORD,/*y*/UNDEFINED_COORD,i->linestyleid,0,0);
1134 i->swflastx=i->swflasty=UNDEFINED_COORD;
1135 i->lastwasfill = -1;
1136 i->shapeisempty = 1;
1139 static void starttext(gfxdevice_t*dev)
1141 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1145 if(i->config_watermark) {
1146 insert_watermark(dev, 0);
1148 i->textid = getNewID(dev);
1149 i->swflastx=i->swflasty=0;
1153 /* TODO: move to ../lib/rfxswf */
1154 void changeRect(gfxdevice_t*dev, TAG*tag, int pos, SRECT*newrect)
1156 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1157 /* determine length of old rect */
1161 swf_GetRect(tag, &old);
1162 swf_ResetReadBits(tag);
1163 int pos_end = tag->pos;
1165 int len = tag->len - pos_end;
1166 U8*data = (U8*)malloc(len);
1167 memcpy(data, &tag->data[pos_end], len);
1170 swf_SetRect(tag, newrect);
1171 swf_SetBlock(tag, data, len);
1173 tag->pos = tag->readBit = 0;
1176 void cancelshape(gfxdevice_t*dev)
1178 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1179 /* delete old shape tag */
1181 i->tag = i->tag->prev;
1182 swf_DeleteTag(todel);
1183 if(i->shape) {swf_ShapeFree(i->shape);i->shape=0;}
1185 i->bboxrectpos = -1;
1187 // i->currentswfid--; // doesn't work, for some reason
1190 void fixAreas(gfxdevice_t*dev)
1192 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1193 if(!i->shapeisempty && i->fill &&
1194 (i->bboxrect.xmin == i->bboxrect.xmax ||
1195 i->bboxrect.ymin == i->bboxrect.ymax) &&
1196 i->config_minlinewidth >= 0.001
1198 msg("<debug> Shape has size 0: width=%.2f height=%.2f",
1199 (i->bboxrect.xmax-i->bboxrect.xmin)/20.0,
1200 (i->bboxrect.ymax-i->bboxrect.ymin)/20.0
1203 SRECT r = i->bboxrect;
1205 if(r.xmin == r.xmax && r.ymin == r.ymax) {
1206 /* this thing comes down to a single dot- nothing to fix here */
1212 RGBA save_col = i->strokergb;
1213 int save_width = i->linewidth;
1215 i->strokergb = i->fillrgb;
1216 i->linewidth = (int)(i->config_minlinewidth*20);
1217 if(i->linewidth==0) i->linewidth = 1;
1222 moveto(dev, i->tag, r.xmin/20.0,r.ymin/20.0);
1223 lineto(dev, i->tag, r.xmax/20.0,r.ymax/20.0);
1225 i->strokergb = save_col;
1226 i->linewidth = save_width;
1231 static void endshape_noput(gfxdevice_t*dev)
1233 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1236 //changeRect(dev, i->tag, i->bboxrectpos, &i->bboxrect);
1239 swf_ShapeFree(i->shape);
1247 static void endshape(gfxdevice_t*dev)
1249 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1255 if(i->shapeisempty ||
1257 (i->bboxrect.xmin == i->bboxrect.xmax &&
1258 i->bboxrect.ymin == i->bboxrect.ymax))
1260 // delete the shape again, we didn't do anything
1261 msg("<debug> cancelling shape: bbox is (%f,%f,%f,%f)",
1262 i->bboxrect.xmin /20.0,
1263 i->bboxrect.ymin /20.0,
1264 i->bboxrect.xmax /20.0,
1265 i->bboxrect.ymax /20.0
1271 swf_ShapeSetEnd(i->tag);
1273 SRECT r = swf_ClipRect(i->pagebbox, i->bboxrect);
1274 changeRect(dev, i->tag, i->bboxrectpos, &r);
1276 msg("<trace> Placing shape ID %d", i->shapeid);
1278 i->tag = swf_InsertTag(i->tag,ST_PLACEOBJECT2);
1279 MATRIX m = i->page_matrix;
1280 m.tx += i->shapeposx;
1281 m.ty += i->shapeposy;
1282 swf_ObjectPlace(i->tag,i->shapeid,getNewDepth(dev),&m,NULL,NULL);
1284 if(i->config_animate) {
1285 i->tag = swf_InsertTag(i->tag,ST_SHOWFRAME);
1288 swf_ShapeFree(i->shape);
1291 i->bboxrectpos = -1;
1298 void wipeSWF(SWF*swf)
1300 TAG*tag = swf->firstTag;
1302 TAG*next = tag->next;
1303 if(tag->id != ST_SETBACKGROUNDCOLOR &&
1304 tag->id != ST_END &&
1305 tag->id != ST_DOACTION &&
1306 tag->id != ST_SHOWFRAME) {
1313 void swfoutput_finalize(gfxdevice_t*dev)
1315 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1317 if(i->tag && i->tag->id == ST_END)
1318 return; //already done
1320 i->swf->fileVersion = i->config_flashversion;
1321 i->swf->frameRate = i->config_framerate*0x100;
1323 if(i->config_bboxvars) {
1324 TAG* tag = swf_InsertTag(i->swf->firstTag, ST_DOACTION);
1326 a = action_PushString(a, "xmin");
1327 a = action_PushFloat(a, i->swf->movieSize.xmin / 20.0);
1328 a = action_SetVariable(a);
1329 a = action_PushString(a, "ymin");
1330 a = action_PushFloat(a, i->swf->movieSize.ymin / 20.0);
1331 a = action_SetVariable(a);
1332 a = action_PushString(a, "xmax");
1333 a = action_PushFloat(a, i->swf->movieSize.xmax / 20.0);
1334 a = action_SetVariable(a);
1335 a = action_PushString(a, "ymax");
1336 a = action_PushFloat(a, i->swf->movieSize.ymax / 20.0);
1337 a = action_SetVariable(a);
1338 a = action_PushString(a, "width");
1339 a = action_PushFloat(a, (i->swf->movieSize.xmax - i->swf->movieSize.xmin) / 20.0);
1340 a = action_SetVariable(a);
1341 a = action_PushString(a, "height");
1342 a = action_PushFloat(a, (i->swf->movieSize.ymax - i->swf->movieSize.ymin) / 20.0);
1343 a = action_SetVariable(a);
1345 swf_ActionSet(tag, a);
1350 free(i->mark);i->mark = 0;
1354 fontlist_t *iterator = i->fontlist;
1356 TAG*mtag = i->swf->firstTag;
1357 if(iterator->swffont) {
1358 if(!i->config_storeallcharacters) {
1359 msg("<debug> Reducing font %s", iterator->swffont->name);
1360 swf_FontReduce(iterator->swffont);
1362 int used = iterator->swffont->use && iterator->swffont->use->used_glyphs;
1364 mtag = swf_InsertTag(mtag, ST_DEFINEFONT2);
1365 swf_FontSetDefine2(mtag, iterator->swffont);
1369 iterator = iterator->next;
1372 i->tag = swf_InsertTag(i->tag,ST_END);
1373 TAG* tag = i->tag->prev;
1375 /* remove the removeobject2 tags between the last ST_SHOWFRAME
1376 and the ST_END- they confuse the flash player */
1377 while(tag->id == ST_REMOVEOBJECT2) {
1378 TAG* prev = tag->prev;
1386 if(i->config_enablezlib || i->config_flashversion>=6) {
1387 i->swf->compressed = 1;
1390 /* Initialize AVM2 if it is a Flash9 file */
1391 if(i->config_flashversion>=9 && i->config_insertstoptag) {
1392 AVM2_InsertStops(i->swf);
1394 // if(i->config_reordertags)
1395 // swf_Optimize(i->swf);
1398 int swfresult_save(gfxresult_t*gfx, const char*filename)
1400 SWF*swf = (SWF*)gfx->internal;
1403 fi = open(filename, O_BINARY|O_CREAT|O_TRUNC|O_WRONLY, 0777);
1408 msg("<fatal> Could not create \"%s\". ", FIXNULL(filename));
1412 if FAILED(swf_WriteSWF(fi,swf))
1413 msg("<error> WriteSWF() failed.\n");
1419 void* swfresult_get(gfxresult_t*gfx, const char*name)
1421 SWF*swf = (SWF*)gfx->internal;
1422 if(!strcmp(name, "swf")) {
1423 return (void*)swf_CopySWF(swf);
1424 } else if(!strcmp(name, "xmin")) {
1425 return (void*)(swf->movieSize.xmin/20);
1426 } else if(!strcmp(name, "ymin")) {
1427 return (void*)(swf->movieSize.ymin/20);
1428 } else if(!strcmp(name, "xmax")) {
1429 return (void*)(swf->movieSize.xmax/20);
1430 } else if(!strcmp(name, "ymax")) {
1431 return (void*)(swf->movieSize.ymax/20);
1432 } else if(!strcmp(name, "width")) {
1433 return (void*)((swf->movieSize.xmax - swf->movieSize.xmin)/20);
1434 } else if(!strcmp(name, "height")) {
1435 return (void*)((swf->movieSize.ymax - swf->movieSize.ymin)/20);
1439 void swfresult_destroy(gfxresult_t*gfx)
1442 swf_FreeTags((SWF*)gfx->internal);
1443 free(gfx->internal);
1446 memset(gfx, 0, sizeof(gfxresult_t));
1450 static void swfoutput_destroy(gfxdevice_t* dev);
1452 gfxresult_t* swf_finish(gfxdevice_t* dev)
1454 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1457 if(i->config_linktarget) {
1458 free(i->config_linktarget);
1459 i->config_linktarget = 0;
1462 swfoutput_finalize(dev);
1463 SWF* swf = i->swf;i->swf = 0;
1464 swfoutput_destroy(dev);
1466 result = (gfxresult_t*)rfx_calloc(sizeof(gfxresult_t));
1467 result->internal = swf;
1468 result->save = swfresult_save;
1470 result->get = swfresult_get;
1471 result->destroy = swfresult_destroy;
1475 /* Perform cleaning up */
1476 static void swfoutput_destroy(gfxdevice_t* dev)
1478 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1480 /* not initialized yet- nothing to destroy */
1484 fontlist_t *tmp,*iterator = i->fontlist;
1486 if(iterator->swffont) {
1487 swf_FontFree(iterator->swffont);iterator->swffont=0;
1490 iterator = iterator->next;
1493 if(i->swf) {swf_FreeTags(i->swf);free(i->swf);i->swf = 0;}
1496 memset(dev, 0, sizeof(gfxdevice_t));
1499 static void swfoutput_setstrokecolor(gfxdevice_t* dev, U8 r, U8 g, U8 b, U8 a)
1501 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1502 if(i->strokergb.r == r &&
1503 i->strokergb.g == g &&
1504 i->strokergb.b == b &&
1505 i->strokergb.a == a) return;
1515 //#define ROUND_UP 19
1516 //#define ROUND_UP 10
1518 static void swfoutput_setlinewidth(gfxdevice_t*dev, double _linewidth)
1520 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1521 if(i->linewidth == (U16)(_linewidth*20+19.0/20.0))
1525 i->linewidth = (U16)(_linewidth*20+19.0/20.0);
1529 static void drawlink(gfxdevice_t*dev, ActionTAG*,ActionTAG*, gfxline_t*points, char mouseover);
1530 static void swfoutput_namedlink(gfxdevice_t*dev, char*name, gfxline_t*points);
1531 static void swfoutput_linktopage(gfxdevice_t*dev, int page, gfxline_t*points);
1532 static void swfoutput_linktourl(gfxdevice_t*dev, const char*url, gfxline_t*points);
1534 /*void swfoutput_drawlink(gfxdevice_t*dev, char*url, gfxline_t*points)
1536 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1537 dev->drawlink(dev, points, url);
1540 void swf_drawlink(gfxdevice_t*dev, gfxline_t*points, const char*url)
1542 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1544 if(!strncmp("http://pdf2swf:", url, 15)) {
1545 char*tmp = strdup(url);
1546 int l = strlen(tmp);
1549 swfoutput_namedlink(dev, tmp+15, points);
1552 } else if(!strncmp("page", url, 4)) {
1555 if(url[t]<'0' || url[t]>'9')
1558 int page = atoi(&url[4]);
1559 if(page<0) page = 0;
1560 swfoutput_linktopage(dev, page, points);
1563 swfoutput_linktourl(dev, url, points);
1566 void swfoutput_linktourl(gfxdevice_t*dev, const char*url, gfxline_t*points)
1568 ActionTAG* actions = 0;
1569 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1575 if(i->config_externallinkfunction) {
1576 actions = action_PushString(actions, url); //parameter
1577 actions = action_PushInt(actions, 1); //number of parameters (1)
1578 actions = action_PushString(actions, i->config_externallinkfunction); //function name
1579 actions = action_CallFunction(actions);
1580 } else if(!i->config_linktarget) {
1581 if(!i->config_opennewwindow)
1582 actions = action_GetUrl(actions, url, "_parent");
1584 actions = action_GetUrl(actions, url, "_this");
1586 actions = action_GetUrl(actions, url, i->config_linktarget);
1588 actions = action_End(actions);
1590 drawlink(dev, actions, 0, points, 0);
1592 void swfoutput_linktopage(gfxdevice_t*dev, int page, gfxline_t*points)
1594 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1595 ActionTAG* actions = 0;
1602 if(!i->config_internallinkfunction) {
1603 actions = action_GotoFrame(actions, page-1);
1604 actions = action_End(actions);
1606 actions = action_PushInt(actions, page); //parameter
1607 actions = action_PushInt(actions, 1); //number of parameters (1)
1608 actions = action_PushString(actions, i->config_internallinkfunction); //function name
1609 actions = action_CallFunction(actions);
1610 actions = action_End(actions);
1613 drawlink(dev, actions, 0, points, 0);
1616 /* Named Links (a.k.a. Acrobatmenu) are used to implement various gadgets
1617 of the viewer objects, like subtitles, index elements etc.
1619 void swfoutput_namedlink(gfxdevice_t*dev, char*name, gfxline_t*points)
1621 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1622 ActionTAG *actions1,*actions2;
1623 char*tmp = strdup(name);
1631 if(!strncmp(tmp, "call:", 5))
1633 char*x = strchr(&tmp[5], ':');
1635 actions1 = action_PushInt(0, 0); //number of parameters (0)
1636 actions1 = action_PushString(actions1, &tmp[5]); //function name
1637 actions1 = action_CallFunction(actions1);
1638 actions1 = action_End(actions1);
1641 actions1 = action_PushString(0, x+1); //parameter
1642 actions1 = action_PushInt(actions1, 1); //number of parameters (1)
1643 actions1 = action_PushString(actions1, &tmp[5]); //function name
1644 actions1 = action_CallFunction(actions1);
1645 actions1 = action_End(actions1);
1647 actions2 = action_End(0);
1652 actions1 = action_PushString(0, "/:subtitle");
1653 actions1 = action_PushString(actions1, name);
1654 actions1 = action_SetVariable(actions1);
1655 actions1 = action_End(actions1);
1657 actions2 = action_PushString(0, "/:subtitle");
1658 actions2 = action_PushString(actions2, "");
1659 actions2 = action_SetVariable(actions2);
1660 actions2 = action_End(actions2);
1663 drawlink(dev, actions1, actions2, points, mouseover);
1665 swf_ActionFree(actions1);
1666 swf_ActionFree(actions2);
1670 static void drawgfxline(gfxdevice_t*dev, gfxline_t*line)
1672 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1673 gfxcoord_t lastx=0,lasty=0,px=0,py=0;
1675 int lines= 0, splines=0;
1679 /* check whether the next segment is zero */
1680 if(line->type == gfx_moveTo) {
1681 moveto(dev, i->tag, line->x, line->y);
1682 px = lastx = line->x;
1683 py = lasty = line->y;
1685 } if(line->type == gfx_lineTo) {
1686 lineto(dev, i->tag, line->x, line->y);
1691 } else if(line->type == gfx_splineTo) {
1693 s.x = line->sx;p.x = line->x;
1694 s.y = line->sy;p.y = line->y;
1695 splineto(dev, i->tag, s, p);
1703 msg("<trace> drawgfxline, %d lines, %d splines", lines, splines);
1707 static void drawlink(gfxdevice_t*dev, ActionTAG*actions1, ActionTAG*actions2, gfxline_t*points, char mouseover)
1709 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1718 int buttonid = getNewID(dev);
1719 gfxbbox_t bbox = gfxline_getbbox(points);
1722 myshapeid = getNewID(dev);
1723 i->tag = swf_InsertTag(i->tag,ST_DEFINESHAPE3);
1724 swf_ShapeNew(&i->shape);
1725 rgb.r = rgb.b = rgb.a = rgb.g = 0;
1726 fsid = swf_ShapeAddSolidFillStyle(i->shape,&rgb);
1727 swf_SetU16(i->tag, myshapeid);
1728 r.xmin = (int)(bbox.xmin*20);
1729 r.ymin = (int)(bbox.ymin*20);
1730 r.xmax = (int)(bbox.xmax*20);
1731 r.ymax = (int)(bbox.ymax*20);
1732 r = swf_ClipRect(i->pagebbox, r);
1733 swf_SetRect(i->tag,&r);
1734 swf_SetShapeStyles(i->tag,i->shape);
1735 swf_ShapeCountBits(i->shape,NULL,NULL);
1736 swf_SetShapeBits(i->tag,i->shape);
1737 swf_ShapeSetAll(i->tag,i->shape,/*x*/0,/*y*/0,0,fsid,0);
1738 i->swflastx = i->swflasty = 0;
1739 drawgfxline(dev, points);
1740 swf_ShapeSetEnd(i->tag);
1743 myshapeid2 = getNewID(dev);
1744 i->tag = swf_InsertTag(i->tag,ST_DEFINESHAPE3);
1745 swf_ShapeNew(&i->shape);
1747 rgb = i->config_linkcolor;
1749 fsid = swf_ShapeAddSolidFillStyle(i->shape,&rgb);
1750 swf_SetU16(i->tag, myshapeid2);
1751 r.xmin = (int)(bbox.xmin*20);
1752 r.ymin = (int)(bbox.ymin*20);
1753 r.xmax = (int)(bbox.xmax*20);
1754 r.ymax = (int)(bbox.ymax*20);
1755 r = swf_ClipRect(i->pagebbox, r);
1756 swf_SetRect(i->tag,&r);
1757 swf_SetShapeStyles(i->tag,i->shape);
1758 swf_ShapeCountBits(i->shape,NULL,NULL);
1759 swf_SetShapeBits(i->tag,i->shape);
1760 swf_ShapeSetAll(i->tag,i->shape,/*x*/0,/*y*/0,0,fsid,0);
1761 i->swflastx = i->swflasty = 0;
1762 drawgfxline(dev, points);
1763 swf_ShapeSetEnd(i->tag);
1767 i->tag = swf_InsertTag(i->tag,ST_DEFINEBUTTON);
1768 swf_SetU16(i->tag,buttonid); //id
1769 swf_ButtonSetFlags(i->tag, 0); //menu=no
1770 swf_ButtonSetRecord(i->tag,0x01,myshapeid,i->depth,0,0);
1771 swf_ButtonSetRecord(i->tag,0x02,myshapeid2,i->depth,0,0);
1772 swf_ButtonSetRecord(i->tag,0x04,myshapeid2,i->depth,0,0);
1773 swf_ButtonSetRecord(i->tag,0x08,myshapeid,i->depth,0,0);
1774 swf_SetU8(i->tag,0);
1775 swf_ActionSet(i->tag,actions1);
1776 swf_SetU8(i->tag,0);
1780 i->tag = swf_InsertTag(i->tag,ST_DEFINEBUTTON2);
1781 swf_SetU16(i->tag,buttonid); //id
1782 swf_ButtonSetFlags(i->tag, 0); //menu=no
1783 swf_ButtonSetRecord(i->tag,0x01,myshapeid,i->depth,0,0);
1784 swf_ButtonSetRecord(i->tag,0x02,myshapeid2,i->depth,0,0);
1785 swf_ButtonSetRecord(i->tag,0x04,myshapeid2,i->depth,0,0);
1786 swf_ButtonSetRecord(i->tag,0x08,myshapeid,i->depth,0,0);
1787 swf_SetU8(i->tag,0); // end of button records
1788 swf_ButtonSetCondition(i->tag, BC_IDLE_OVERUP);
1789 swf_ActionSet(i->tag,actions1);
1791 swf_ButtonSetCondition(i->tag, BC_OVERUP_IDLE);
1792 swf_ActionSet(i->tag,actions2);
1793 swf_SetU8(i->tag,0);
1794 swf_ButtonPostProcess(i->tag, 2);
1796 swf_SetU8(i->tag,0);
1797 swf_ButtonPostProcess(i->tag, 1);
1801 sprintf(name, "link%d", buttonid);
1803 msg("<trace> Placing link ID %d", buttonid);
1804 i->tag = swf_InsertTag(i->tag,ST_PLACEOBJECT2);
1806 if(posx!=0 || posy!=0) {
1808 p.x = (int)(posx*20);
1809 p.y = (int)(posy*20);
1810 p = swf_TurnPoint(p, &i->page_matrix);
1815 swf_ObjectPlace(i->tag, buttonid, getNewDepth(dev),&m,0,name);
1817 swf_ObjectPlace(i->tag, buttonid, getNewDepth(dev),&i->page_matrix,0,name);
1824 for(t=0;t<picpos;t++)
1826 if(pic_xids[t] == xid &&
1827 pic_yids[t] == yid) {
1828 width = pic_width[t];
1829 height = pic_height[t];
1833 pic_ids[picpos] = swfoutput_drawimagelosslessN(&output, pic, pal, width, height, x1,y1,x2,y2,x3,y3,x4,y4, numpalette);
1834 pic_xids[picpos] = xid;
1835 pic_yids[picpos] = yid;
1836 pic_width[picpos] = width;
1837 pic_height[picpos] = height;
1840 pic[width*y+x] = buf[0];
1844 xid += pal[1].r*3 + pal[1].g*11 + pal[1].b*17;
1845 yid += pal[1].r*7 + pal[1].g*5 + pal[1].b*23;
1849 xid += x*r+x*b*3+x*g*7+x*a*11;
1850 yid += y*r*3+y*b*17+y*g*19+y*a*11;
1852 for(t=0;t<picpos;t++)
1854 if(pic_xids[t] == xid &&
1855 pic_yids[t] == yid) {
1864 int swf_setparameter(gfxdevice_t*dev, const char*name, const char*value)
1866 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1868 msg("<trace> swfdevice: %s=%s", name, value);
1869 if(!strcmp(name, "jpegsubpixels")) {
1870 i->config_jpegsubpixels = atof(value);
1871 } else if(!strcmp(name, "ppmsubpixels")) {
1872 i->config_ppmsubpixels = atof(value);
1873 } else if(!strcmp(name, "subpixels")) {
1874 i->config_ppmsubpixels = i->config_jpegsubpixels = atof(value);
1875 } else if(!strcmp(name, "drawonlyshapes")) {
1876 i->config_drawonlyshapes = atoi(value);
1877 } else if(!strcmp(name, "ignoredraworder")) {
1878 i->config_ignoredraworder = atoi(value);
1879 } else if(!strcmp(name, "mark")) {
1880 if(!value || !value[0]) {
1881 if(i->mark) free(i->mark);
1885 i->mark = strdup("...");
1886 for(t=0;t<3;t++) if(value[t]) i->mark[t] = value[t];
1888 } else if(!strcmp(name, "filloverlap")) {
1889 i->config_filloverlap = atoi(value);
1890 } else if(!strcmp(name, "linksopennewwindow")) {
1891 i->config_opennewwindow = atoi(value);
1892 } else if(!strcmp(name, "opennewwindow")) {
1893 i->config_opennewwindow = atoi(value);
1894 } else if(!strcmp(name, "storeallcharacters")) {
1895 i->config_storeallcharacters = atoi(value);
1896 } else if(!strcmp(name, "enablezlib")) {
1897 i->config_enablezlib = atoi(value);
1898 } else if(!strcmp(name, "bboxvars")) {
1899 i->config_bboxvars = atoi(value);
1900 } else if(!strcmp(name, "showclipshapes")) {
1901 i->config_showclipshapes = atoi(value);
1902 } else if(!strcmp(name, "reordertags")) {
1903 i->config_reordertags = atoi(value);
1904 } else if(!strcmp(name, "internallinkfunction")) {
1905 i->config_internallinkfunction = strdup(value);
1906 } else if(!strcmp(name, "externallinkfunction")) {
1907 i->config_externallinkfunction = strdup(value);
1908 } else if(!strcmp(name, "linkfunction")) { //sets both internallinkfunction and externallinkfunction
1909 i->config_internallinkfunction = strdup(value);
1910 i->config_externallinkfunction = strdup(value);
1911 } else if(!strcmp(name, "disable_polygon_conversion")) {
1912 i->config_disable_polygon_conversion = atoi(value);
1913 } else if(!strcmp(name, "normalize_polygon_positions")) {
1914 i->config_normalize_polygon_positions = atoi(value);
1915 } else if(!strcmp(name, "wxwindowparams")) {
1916 i->config_watermark = atoi(value);
1917 } else if(!strcmp(name, "insertstop")) {
1918 i->config_insertstoptag = atoi(value);
1919 } else if(!strcmp(name, "protect")) {
1920 i->config_protect = atoi(value);
1921 if(i->config_protect && i->tag) {
1922 i->tag = swf_InsertTag(i->tag, ST_PROTECT);
1924 } else if(!strcmp(name, "flashversion")) {
1925 i->config_flashversion = atoi(value);
1927 i->swf->fileVersion = i->config_flashversion;
1929 } else if(!strcmp(name, "framerate")) {
1930 i->config_framerate = atoi(value);
1932 i->swf->frameRate = i->config_framerate*0x100;
1934 } else if(!strcmp(name, "minlinewidth")) {
1935 i->config_minlinewidth = atof(value);
1936 } else if(!strcmp(name, "caplinewidth")) {
1937 i->config_caplinewidth = atof(value);
1938 } else if(!strcmp(name, "linktarget")) {
1939 i->config_linktarget = strdup(value);
1940 } else if(!strcmp(name, "dumpfonts")) {
1941 i->config_dumpfonts = atoi(value);
1942 } else if(!strcmp(name, "animate")) {
1943 i->config_animate = atoi(value);
1944 } else if(!strcmp(name, "simpleviewer")) {
1945 i->config_simpleviewer = atoi(value);
1946 } else if(!strcmp(name, "next_bitmap_is_jpeg")) {
1948 } else if(!strcmp(name, "jpegquality")) {
1949 int val = atoi(value);
1951 if(val>101) val=101;
1952 i->config_jpegquality = val;
1953 } else if(!strcmp(name, "splinequality")) {
1954 int v = atoi(value);
1955 v = 500-(v*5); // 100% = 0.25 pixel, 0% = 25 pixel
1957 i->config_splinemaxerror = v;
1958 } else if(!strcmp(name, "fontquality")) {
1959 int v = atoi(value);
1960 v = 500-(v*5); // 100% = 0.25 pixel, 0% = 25 pixel
1962 i->config_fontsplinemaxerror = v;
1963 } else if(!strcmp(name, "linkcolor")) {
1964 if(strlen(value)!=8) {
1965 fprintf(stderr, "Unknown format for option 'linkcolor'. (%s <-> RRGGBBAA)\n", value);
1968 # define NIBBLE(s) (((s)>='0' && (s)<='9')?((s)-'0'):((s)&0x0f)+9)
1969 i->config_linkcolor.r = NIBBLE(value[0])<<4 | NIBBLE(value[1]);
1970 i->config_linkcolor.g = NIBBLE(value[2])<<4 | NIBBLE(value[3]);
1971 i->config_linkcolor.b = NIBBLE(value[4])<<4 | NIBBLE(value[5]);
1972 i->config_linkcolor.a = NIBBLE(value[6])<<4 | NIBBLE(value[7]);
1973 } else if(!strcmp(name, "help")) {
1974 printf("\nSWF layer options:\n");
1975 printf("jpegdpi=<dpi> resolution adjustment for jpeg images\n");
1976 printf("jpegsubpixels=<pixels> resolution adjustment for jpeg images (same as jpegdpi, but in pixels)\n");
1977 printf("ppmdpi=<dpi> resolution adjustment for lossless images\n");
1978 printf("ppmsubpixels=<pixels resolution adjustment for lossless images (same as ppmdpi, but in pixels)\n");
1979 printf("subpixels=<pixels> shortcut for setting both jpegsubpixels and ppmsubpixels\n");
1980 printf("drawonlyshapes convert everything to shapes (currently broken)\n");
1981 printf("ignoredraworder allow to perform a few optimizations for creating smaller SWFs\n");
1982 printf("linksopennewwindow make links open a new browser window\n");
1983 printf("linktarget target window name of new links\n");
1984 printf("linkcolor=<color) color of links (format: RRGGBBAA)\n");
1985 printf("storeallcharacters don't reduce the fonts to used characters in the output file\n");
1986 printf("enablezlib switch on zlib compression (also done if flashversion>=7)\n");
1987 printf("bboxvars store the bounding box of the SWF file in actionscript variables\n");
1988 printf("reordertags=0/1 (default: 1) perform some tag optimizations\n");
1989 printf("internallinkfunction=<name> when the user clicks a internal link (to a different page) in the converted file, this actionscript function is called\n");
1990 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");
1991 printf("disable_polygon_conversion never convert strokes to polygons (will remove capstyles and joint styles)\n");
1992 printf("caplinewidth=<width> the minimum thichness a line needs to have so that capstyles become visible (and are converted)\n");
1993 printf("insertstop put an ActionScript \"STOP\" tag in every frame\n");
1994 printf("protect add a \"protect\" tag to the file, to prevent loading in the Flash editor\n");
1995 printf("flashversion=<version> the SWF fileversion (6)\n");
1996 printf("minlinewidth=<width> convert horizontal/vertical boxes smaller than this width to lines (0.05) \n");
1997 printf("animate insert a showframe tag after each placeobject (animate draw order of PDF files)\n");
1998 printf("jpegquality=<quality> set compression quality of jpeg images\n");
2005 // --------------------------------------------------------------------
2007 static CXFORM gfxcxform_to_cxform(gfxcxform_t* c)
2010 swf_GetCXForm(0, &cx, 1);
2013 if(c->rg!=0 || c->rb!=0 || c->ra!=0 ||
2014 c->gr!=0 || c->gb!=0 || c->ga!=0 ||
2015 c->br!=0 || c->bg!=0 || c->ba!=0 ||
2016 c->ar!=0 || c->ag!=0 || c->ab!=0)
2017 msg("<warning> CXForm not SWF-compatible");
2019 cx.a0 = (S16)(c->aa*256);
2020 cx.r0 = (S16)(c->rr*256);
2021 cx.g0 = (S16)(c->gg*256);
2022 cx.b0 = (S16)(c->bb*256);
2031 static int imageInCache(gfxdevice_t*dev, void*data, int width, int height)
2035 static void addImageToCache(gfxdevice_t*dev, void*data, int width, int height)
2039 static int add_image(swfoutput_internal*i, gfximage_t*img, int targetwidth, int targetheight, int* newwidth, int* newheight)
2041 gfxdevice_t*dev = i->dev;
2043 RGBA*mem = (RGBA*)img->data;
2045 int sizex = img->width;
2046 int sizey = img->height;
2047 int is_jpeg = i->jpeg;
2050 int newsizex=sizex, newsizey=sizey;
2053 if(is_jpeg && i->config_jpegsubpixels) {
2054 newsizex = (int)(targetwidth*i->config_jpegsubpixels + 0.5);
2055 newsizey = (int)(targetheight*i->config_jpegsubpixels + 0.5);
2056 } else if(!is_jpeg && i->config_ppmsubpixels) {
2057 newsizex = (int)(targetwidth*i->config_ppmsubpixels + 0.5);
2058 newsizey = (int)(targetheight*i->config_ppmsubpixels + 0.5);
2062 if(sizex<=0 || sizey<=0)
2069 /* TODO: cache images */
2071 if(newsizex<sizex || newsizey<sizey) {
2072 msg("<verbose> Scaling %dx%d image to %dx%d", sizex, sizey, newsizex, newsizey);
2073 newpic = swf_ImageScale(mem, sizex, sizey, newsizex, newsizey);
2074 *newwidth = sizex = newsizex;
2075 *newheight = sizey = newsizey;
2078 *newwidth = newsizex = sizex;
2079 *newheight = newsizey = sizey;
2082 int num_colors = swf_ImageGetNumberOfPaletteEntries(mem,sizex,sizey,0);
2083 int has_alpha = swf_ImageHasAlpha(mem,sizex,sizey);
2085 msg("<verbose> Drawing %dx%d %s%simage (id %d) at size %dx%d (%dx%d), %s%d colors",
2087 has_alpha?(has_alpha==2?"semi-transparent ":"transparent "):"",
2088 is_jpeg?"jpeg-":"", i->currentswfid+1,
2090 targetwidth, targetheight,
2091 /*newsizex, newsizey,*/
2092 num_colors>256?">":"", num_colors>256?256:num_colors);
2094 /*RGBA* pal = (RGBA*)rfx_alloc(sizeof(RGBA)*num_colors);
2095 swf_ImageGetNumberOfPaletteEntries(mem,sizex,sizey,pal);
2097 for(t=0;t<num_colors;t++) {
2098 printf("%02x%02x%02x%02x ",
2099 pal[t].r, pal[t].g, pal[t].b, pal[t].a);
2106 int cacheid = imageInCache(dev, mem, sizex, sizey);
2109 bitid = getNewID(dev);
2110 i->tag = swf_AddImage(i->tag, bitid, mem, sizex, sizey, i->config_jpegquality);
2111 addImageToCache(dev, mem, sizex, sizey);
2121 static SRECT gfxline_getSWFbbox(gfxline_t*line)
2123 gfxbbox_t bbox = gfxline_getbbox(line);
2125 r.xmin = (int)(bbox.xmin*20);
2126 r.ymin = (int)(bbox.ymin*20);
2127 r.xmax = (int)(bbox.xmax*20);
2128 r.ymax = (int)(bbox.ymax*20);
2132 int line_is_empty(gfxline_t*line)
2135 if(line->type != gfx_moveTo)
2142 static void swf_fillbitmap(gfxdevice_t*dev, gfxline_t*line, gfximage_t*img, gfxmatrix_t*matrix, gfxcxform_t*cxform)
2144 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
2146 if(line_is_empty(line))
2152 int targetx = (int)(sqrt(matrix->m00*matrix->m00 + matrix->m01*matrix->m01)*img->width);
2153 int targety = (int)(sqrt(matrix->m10*matrix->m10 + matrix->m11*matrix->m11)*img->height);
2155 int newwidth=0,newheight=0;
2156 int bitid = add_image(i, img, targetx, targety, &newwidth, &newheight);
2159 double fx = (double)img->width / (double)newwidth;
2160 double fy = (double)img->height / (double)newheight;
2163 m.sx = (int)(65536*20*matrix->m00*fx); m.r1 = (int)(65536*20*matrix->m10*fy);
2164 m.r0 = (int)(65536*20*matrix->m01*fx); m.sy = (int)(65536*20*matrix->m11*fy);
2165 m.tx = (int)(matrix->tx*20);
2166 m.ty = (int)(matrix->ty*20);
2169 int myshapeid = getNewID(dev);
2170 i->tag = swf_InsertTag(i->tag,ST_DEFINESHAPE);
2172 swf_ShapeNew(&shape);
2173 int fsid = swf_ShapeAddBitmapFillStyle(shape,&m,bitid,1);
2174 swf_SetU16(i->tag, myshapeid);
2175 SRECT r = gfxline_getSWFbbox(line);
2176 r = swf_ClipRect(i->pagebbox, r);
2177 swf_SetRect(i->tag,&r);
2178 swf_SetShapeStyles(i->tag,shape);
2179 swf_ShapeCountBits(shape,NULL,NULL);
2180 swf_SetShapeBits(i->tag,shape);
2181 swf_ShapeSetAll(i->tag,shape,UNDEFINED_COORD,UNDEFINED_COORD,0,fsid,0);
2182 i->swflastx = i->swflasty = UNDEFINED_COORD;
2183 drawgfxline(dev, line);
2184 swf_ShapeSetEnd(i->tag);
2185 swf_ShapeFree(shape);
2187 msg("<trace> Placing image, shape ID %d, bitmap ID %d", myshapeid, bitid);
2188 i->tag = swf_InsertTag(i->tag,ST_PLACEOBJECT2);
2189 CXFORM cxform2 = gfxcxform_to_cxform(cxform);
2190 swf_ObjectPlace(i->tag,myshapeid,getNewDepth(dev),&i->page_matrix,&cxform2,NULL);
2193 static RGBA col_black = {255,0,0,0};
2195 static void drawoutline(gfxdevice_t*dev, gfxline_t*line)
2197 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
2199 int myshapeid = getNewID(dev);
2200 i->tag = swf_InsertTag(i->tag,ST_DEFINESHAPE3);
2203 swf_ShapeNew(&shape);
2204 int lsid = swf_ShapeAddLineStyle(shape,1,&col_black);
2206 swf_SetU16(i->tag,myshapeid);
2207 SRECT r = gfxline_getSWFbbox(line);
2208 r = swf_ClipRect(i->pagebbox, r);
2209 swf_SetRect(i->tag,&r);
2210 swf_SetShapeStyles(i->tag,shape);
2211 swf_ShapeCountBits(shape,NULL,NULL);
2212 swf_SetShapeBits(i->tag,shape);
2213 swf_ShapeSetAll(i->tag,shape,UNDEFINED_COORD,UNDEFINED_COORD,lsid,0,0);
2214 drawgfxline(dev, line);
2215 swf_ShapeSetEnd(i->tag);
2216 swf_ShapeFree(shape);
2218 i->tag = swf_InsertTag(i->tag,ST_PLACEOBJECT2);
2219 swf_ObjectPlace(i->tag, myshapeid, getNewDepth(dev), 0,0,0);
2222 static void swf_startclip(gfxdevice_t*dev, gfxline_t*line)
2224 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
2229 if(i->clippos >= 127)
2231 msg("<warning> Too many clip levels.");
2235 if(i->config_showclipshapes)
2236 drawoutline(dev, line);
2238 int myshapeid = getNewID(dev);
2239 i->tag = swf_InsertTag(i->tag,ST_DEFINESHAPE3);
2241 memset(&col, 0, sizeof(RGBA));
2244 swf_ShapeNew(&shape);
2245 int fsid = swf_ShapeAddSolidFillStyle(shape,&col);
2247 RGBA markcol = {0,i->mark[0],i->mark[1],i->mark[2]};
2248 swf_ShapeAddSolidFillStyle(shape,&markcol);
2250 swf_SetU16(i->tag,myshapeid);
2251 SRECT r = gfxline_getSWFbbox(line);
2252 r = swf_ClipRect(i->pagebbox, r);
2253 swf_SetRect(i->tag,&r);
2254 swf_SetShapeStyles(i->tag,shape);
2255 swf_ShapeCountBits(shape,NULL,NULL);
2256 swf_SetShapeBits(i->tag,shape);
2257 swf_ShapeSetAll(i->tag,shape,UNDEFINED_COORD,UNDEFINED_COORD,0,fsid,0);
2258 i->swflastx = i->swflasty = UNDEFINED_COORD;
2259 i->shapeisempty = 1;
2260 drawgfxline(dev, line);
2261 if(i->shapeisempty) {
2262 /* an empty clip shape is equivalent to a shape with no area */
2263 int x = line?line->x:0;
2264 int y = line?line->y:0;
2265 moveto(dev, i->tag, x,y);
2266 lineto(dev, i->tag, x,y);
2267 lineto(dev, i->tag, x,y);
2269 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)) {
2270 if(i->config_watermark) {
2271 gfxbbox_t r; r.xmin = r.ymin = 0;r.xmax = i->max_x;r.ymax = i->max_y;
2272 draw_watermark(dev, r, 1);
2275 swf_ShapeSetEnd(i->tag);
2276 swf_ShapeFree(shape);
2278 /* TODO: remember the bbox, and check all shapes against it */
2280 msg("<trace> Placing clip ID %d", myshapeid);
2281 i->tag = swf_InsertTag(i->tag,ST_PLACEOBJECT2);
2282 i->cliptags[i->clippos] = i->tag;
2283 i->clipshapes[i->clippos] = myshapeid;
2284 i->clipdepths[i->clippos] = getNewDepth(dev);
2288 static void swf_endclip(gfxdevice_t*dev)
2290 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
2297 msg("<error> Invalid end of clipping region");
2301 /*swf_ObjectPlaceClip(i->cliptags[i->clippos],i->clipshapes[i->clippos],i->clipdepths[i->clippos],&i->page_matrix,NULL,NULL,
2302 / * clip to depth: * / i->depth <= i->clipdepths[i->clippos]? i->depth : i->depth - 1);
2304 swf_ObjectPlaceClip(i->cliptags[i->clippos],i->clipshapes[i->clippos],i->clipdepths[i->clippos],&i->page_matrix,NULL,NULL,i->depth);
2306 static int gfxline_type(gfxline_t*line)
2312 int haszerosegments=0;
2315 if(line->type == gfx_moveTo) {
2318 } else if(line->type == gfx_lineTo) {
2322 } else if(line->type == gfx_splineTo) {
2324 if(tmpsplines>lines)
2332 if(lines==0 && splines==0) return 0;
2333 else if(lines==1 && splines==0) return 1;
2334 else if(lines==0 && splines==1) return 2;
2335 else if(splines==0) return 3;
2339 static int gfxline_has_dots(gfxline_t*line)
2347 if(line->type == gfx_moveTo) {
2348 /* test the length of the preceding line, and assume it is a dot if
2349 it's length is less than 1.0. But *only* if there's a noticable
2350 gap between the previous line and the next moveTo. (I've come
2351 across a PDF where thousands of "dots" were stringed together,
2353 int last_short_gap = short_gap;
2354 if((fabs(line->x - x) + fabs(line->y - y)) < 1.0) {
2359 if(isline && dist < 1 && !short_gap && !last_short_gap) {
2364 } else if(line->type == gfx_lineTo) {
2365 dist += fabs(line->x - x) + fabs(line->y - y);
2367 } else if(line->type == gfx_splineTo) {
2368 dist += fabs(line->sx - x) + fabs(line->sy - y) +
2369 fabs(line->x - line->sx) + fabs(line->y - line->sy);
2376 if(isline && dist < 1 && !short_gap) {
2382 static int gfxline_fix_short_edges(gfxline_t*line)
2386 if(line->type == gfx_lineTo) {
2387 if(fabs(line->x - x) + fabs(line->y - y) < 0.01) {
2390 } else if(line->type == gfx_splineTo) {
2391 if(fabs(line->sx - x) + fabs(line->sy - y) +
2392 fabs(line->x - line->sx) + fabs(line->y - line->sy) < 0.01) {
2403 static char is_inside_page(gfxdevice_t*dev, gfxcoord_t x, gfxcoord_t y)
2405 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
2406 if(x<i->min_x || x>i->max_x) return 0;
2407 if(y<i->min_y || y>i->max_y) return 0;
2411 gfxline_t* gfxline_move(gfxline_t*line, double x, double y)
2413 gfxline_t*l = line = gfxline_clone(line);
2425 //#define NORMALIZE_POLYGON_POSITIONS
2427 static void swf_stroke(gfxdevice_t*dev, gfxline_t*line, gfxcoord_t width, gfxcolor_t*color, gfx_capType cap_style, gfx_joinType joint_style, gfxcoord_t miterLimit)
2429 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
2430 if(line_is_empty(line))
2432 int type = gfxline_type(line);
2433 int has_dots = gfxline_has_dots(line);
2434 gfxbbox_t r = gfxline_getbbox(line);
2435 int is_outside_page = !is_inside_page(dev, r.xmin, r.ymin) || !is_inside_page(dev, r.xmax, r.ymax);
2437 /* TODO: * split line into segments, and perform this check for all segments */
2439 if(i->config_disable_polygon_conversion || type>=5 ||
2441 (width <= i->config_caplinewidth
2442 || (cap_style == gfx_capRound && joint_style == gfx_joinRound)
2443 || (cap_style == gfx_capRound && type<=2)))) {} else
2445 /* convert line to polygon */
2446 msg("<trace> draw as polygon, type=%d dots=%d", type, has_dots);
2448 gfxline_fix_short_edges(line);
2449 /* we need to convert the line into a polygon */
2450 gfxpoly_t* poly = gfxpoly_strokeToPoly(line, width, cap_style, joint_style, miterLimit);
2451 gfxline_t*gfxline = gfxpoly_to_gfxline(poly);
2452 dev->fill(dev, gfxline, color);
2453 gfxline_free(gfxline);
2458 msg("<trace> draw as stroke, type=%d dots=%d", type, has_dots);
2461 if(i->config_normalize_polygon_positions) {
2463 double startx = 0, starty = 0;
2464 if(line && line->type == gfx_moveTo) {
2468 line = gfxline_move(line, -startx, -starty);
2469 i->shapeposx = (int)(startx*20);
2470 i->shapeposy = (int)(starty*20);
2473 swfoutput_setstrokecolor(dev, color->r, color->g, color->b, color->a);
2474 swfoutput_setlinewidth(dev, width);
2477 drawgfxline(dev, line);
2479 if(i->config_normalize_polygon_positions) {
2480 free(line); //account for _move
2485 static void swf_fill(gfxdevice_t*dev, gfxline_t*line, gfxcolor_t*color)
2487 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
2488 if(line_is_empty(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 //if(i->chardatapos && i->chardata[i->chardatapos-1].color.a) {
2498 if(!i->config_ignoredraworder)
2501 if(i->config_normalize_polygon_positions) {
2503 double startx = 0, starty = 0;
2504 if(line && line->type == gfx_moveTo) {
2508 line = gfxline_move(line, -startx, -starty);
2509 i->shapeposx = (int)(startx*20);
2510 i->shapeposy = (int)(starty*20);
2513 swfoutput_setfillcolor(dev, color->r, color->g, color->b, color->a);
2517 drawgfxline(dev, line);
2519 if(i->currentswfid==2 && r.xmin==0 && r.ymin==0 && r.xmax==i->max_x && r.ymax==i->max_y) {
2520 if(i->config_watermark) {
2521 draw_watermark(dev, r, 1);
2525 msg("<trace> end of swf_fill (shapeid=%d)", i->shapeid);
2527 if(i->config_normalize_polygon_positions) {
2528 free(line); //account for _move
2532 static GRADIENT* gfxgradient_to_GRADIENT(gfxgradient_t*gradient)
2535 gfxgradient_t*g = gradient;
2540 GRADIENT* swfgradient = malloc(sizeof(GRADIENT));
2541 swfgradient->num = num;
2542 swfgradient->rgba = malloc(sizeof(swfgradient->rgba[0])*num);
2543 swfgradient->ratios = malloc(sizeof(swfgradient->ratios[0])*num);
2548 swfgradient->ratios[num] = g->pos*255;
2549 swfgradient->rgba[num] = *(RGBA*)&g->color;
2556 static void swf_fillgradient(gfxdevice_t*dev, gfxline_t*line, gfxgradient_t*gradient, gfxgradienttype_t type, gfxmatrix_t*matrix)
2558 if(line_is_empty(line))
2560 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
2562 if(line_is_empty(line))
2565 GRADIENT* swfgradient = gfxgradient_to_GRADIENT(gradient);
2572 double f = type==gfxgradient_radial?4:4;
2574 m.sx = (int)(matrix->m00*20*f); m.r1 = (int)(matrix->m10*20*f);
2575 m.r0 = (int)(matrix->m01*20*f); m.sy = (int)(matrix->m11*20*f);
2576 m.tx = (int)(matrix->tx*20);
2577 m.ty = (int)(matrix->ty*20);
2580 int myshapeid = getNewID(dev);
2581 i->tag = swf_InsertTag(i->tag, ST_DEFINESHAPE2);
2583 swf_ShapeNew(&shape);
2584 int fsid = swf_ShapeAddGradientFillStyle(shape,&m,swfgradient,type==gfxgradient_radial);
2585 swf_SetU16(i->tag, myshapeid);
2586 SRECT r = gfxline_getSWFbbox(line);
2587 r = swf_ClipRect(i->pagebbox, r);
2588 swf_SetRect(i->tag,&r);
2589 swf_SetShapeStyles(i->tag,shape);
2590 swf_ShapeCountBits(shape,NULL,NULL);
2591 swf_SetShapeBits(i->tag,shape);
2592 swf_ShapeSetAll(i->tag,shape,UNDEFINED_COORD,UNDEFINED_COORD,0,fsid,0);
2593 i->swflastx = i->swflasty = UNDEFINED_COORD;
2594 drawgfxline(dev, line);
2595 swf_ShapeSetEnd(i->tag);
2596 swf_ShapeFree(shape);
2598 int depth = getNewDepth(dev);
2599 msg("<trace> Placing gradient, shape ID %d, depth %d", myshapeid, depth);
2600 i->tag = swf_InsertTag(i->tag,ST_PLACEOBJECT2);
2601 swf_ObjectPlace(i->tag,myshapeid,depth,&i->page_matrix,NULL,NULL);
2603 swf_FreeGradient(swfgradient);free(swfgradient);
2606 static SWFFONT* gfxfont_to_swffont(gfxfont_t*font, const char* id)
2608 SWFFONT*swffont = (SWFFONT*)rfx_calloc(sizeof(SWFFONT));
2610 SRECT bounds = {0,0,0,0};
2612 swffont->version = 2;
2613 swffont->name = (U8*)strdup(id);
2614 swffont->layout = (SWFLAYOUT*)rfx_calloc(sizeof(SWFLAYOUT));
2615 swffont->layout->ascent = 0;
2616 swffont->layout->descent = 0;
2617 swffont->layout->leading = 0;
2618 swffont->layout->bounds = (SRECT*)rfx_calloc(sizeof(SRECT)*font->num_glyphs);
2619 swffont->encoding = FONT_ENCODING_UNICODE;
2620 swffont->numchars = font->num_glyphs;
2621 swffont->maxascii = font->max_unicode;
2622 swffont->ascii2glyph = (int*)rfx_calloc(sizeof(int)*swffont->maxascii);
2623 swffont->glyph2ascii = (U16*)rfx_calloc(sizeof(U16)*swffont->numchars);
2624 swffont->glyph = (SWFGLYPH*)rfx_calloc(sizeof(SWFGLYPH)*swffont->numchars);
2625 swffont->glyphnames = (char**)rfx_calloc(sizeof(char*)*swffont->numchars);
2626 for(t=0;t<font->max_unicode;t++) {
2627 swffont->ascii2glyph[t] = font->unicode2glyph[t];
2629 for(t=0;t<font->num_glyphs;t++) {
2633 swffont->glyph2ascii[t] = font->glyphs[t].unicode;
2634 if(swffont->glyph2ascii[t] == 0xffff || swffont->glyph2ascii[t] == 0x0000) {
2635 /* flash 8 flashtype requires unique unicode IDs for each character.
2636 We use the Unicode private user area to assign characters, hoping that
2637 the font doesn't contain more than 2048 glyphs */
2638 swffont->glyph2ascii[t] = 0xe000 + (t&0x1fff);
2641 if(font->glyphs[t].name) {
2642 swffont->glyphnames[t] = strdup(font->glyphs[t].name);
2644 swffont->glyphnames[t] = 0;
2646 advance = (int)(font->glyphs[t].advance);
2648 swf_Shape01DrawerInit(&draw, 0);
2649 line = font->glyphs[t].line;
2652 c.x = line->sx * GLYPH_SCALE; c.y = line->sy * GLYPH_SCALE;
2653 to.x = line->x * GLYPH_SCALE; to.y = line->y * GLYPH_SCALE;
2654 if(line->type == gfx_moveTo) {
2655 draw.moveTo(&draw, &to);
2656 } else if(line->type == gfx_lineTo) {
2657 draw.lineTo(&draw, &to);
2658 } else if(line->type == gfx_splineTo) {
2659 draw.splineTo(&draw, &c, &to);
2664 swffont->glyph[t].shape = swf_ShapeDrawerToShape(&draw);
2665 swffont->layout->bounds[t] = swf_ShapeDrawerGetBBox(&draw);
2667 int xmax = swffont->layout->bounds[t].xmax / 20;
2668 if(xmax>0 && xmax*2 < advance) {
2669 printf("fix bad advance value: bbox=%d, advance=%d (%f)\n", xmax, advance, font->glyphs[t].advance);
2673 if(advance<32768/20) {
2674 swffont->glyph[t].advance = advance*20;
2676 swffont->glyph[t].advance = 32767;
2679 draw.dealloc(&draw);
2681 swf_ExpandRect2(&bounds, &swffont->layout->bounds[t]);
2685 /* Flash player will use the advance value from the char, and the ascent/descent values
2686 from the layout for text selection.
2687 ascent will extend the char into negative y direction, from the baseline, while descent
2688 will extend in positive y direction, also from the baseline.
2689 The baseline is defined as the y-position zero
2692 swffont->layout->ascent = -bounds.ymin;
2693 if(swffont->layout->ascent < 0)
2694 swffont->layout->ascent = 0;
2695 swffont->layout->descent = bounds.ymax;
2696 if(swffont->layout->descent < 0)
2697 swffont->layout->descent = 0;
2698 swffont->layout->leading = bounds.ymax - bounds.ymin;
2703 static void swf_addfont(gfxdevice_t*dev, gfxfont_t*font)
2705 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
2707 if(i->swffont && i->swffont->name && !strcmp((char*)i->swffont->name,font->id))
2708 return; // the requested font is the current font
2710 fontlist_t*last=0,*l = i->fontlist;
2713 if(!strcmp((char*)l->swffont->name, font->id)) {
2714 return; // we already know this font
2718 l = (fontlist_t*)rfx_calloc(sizeof(fontlist_t));
2719 l->swffont = gfxfont_to_swffont(font, font->id);
2726 swf_FontSetID(l->swffont, getNewID(i->dev));
2728 if(getScreenLogLevel() >= LOGLEVEL_DEBUG) {
2730 // print font information
2731 msg("<debug> Font %s",font->id);
2732 msg("<debug> | ID: %d", l->swffont->id);
2733 msg("<debug> | Version: %d", l->swffont->version);
2734 msg("<debug> | Name: %s", l->swffont->name);
2735 msg("<debug> | Numchars: %d", l->swffont->numchars);
2736 msg("<debug> | Maxascii: %d", l->swffont->maxascii);
2737 msg("<debug> | Style: %d", l->swffont->style);
2738 msg("<debug> | Encoding: %d", l->swffont->encoding);
2739 for(iii=0; iii<l->swffont->numchars;iii++) {
2740 msg("<debug> | Glyph %d) name=%s, unicode=%d size=%d bbox=(%.2f,%.2f,%.2f,%.2f)\n", iii, l->swffont->glyphnames?l->swffont->glyphnames[iii]:"<nonames>", l->swffont->glyph2ascii[iii], l->swffont->glyph[iii].shape->bitlen,
2741 l->swffont->layout->bounds[iii].xmin/20.0,
2742 l->swffont->layout->bounds[iii].ymin/20.0,
2743 l->swffont->layout->bounds[iii].xmax/20.0,
2744 l->swffont->layout->bounds[iii].ymax/20.0
2747 for(t=0;t<l->swffont->maxascii;t++) {
2748 if(l->swffont->ascii2glyph[t] == iii)
2749 msg("<debug> | - maps to %d",t);
2755 static void swf_switchfont(gfxdevice_t*dev, const char*fontid)
2757 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
2759 if(i->swffont && i->swffont->name && !strcmp((char*)i->swffont->name,fontid))
2760 return; // the requested font is the current font
2762 fontlist_t*l = i->fontlist;
2764 if(!strcmp((char*)l->swffont->name, fontid)) {
2765 i->swffont = l->swffont;
2770 msg("<error> Unknown font id: %s", fontid);
2774 static void swf_drawchar(gfxdevice_t*dev, gfxfont_t*font, int glyph, gfxcolor_t*color, gfxmatrix_t*matrix)
2776 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
2778 msg("<error> swf_drawchar called (glyph %d) without font", glyph);
2781 if(!i->swffont || !i->swffont->name || strcmp((char*)i->swffont->name,font->id)) // not equal to current font
2783 /* TODO: remove the need for this (enhance getcharacterbbox so that it can cope
2784 with multiple fonts */
2786 swf_switchfont(dev, font->id); // set the current font
2789 msg("<warning> swf_drawchar: Font is NULL");
2792 if(glyph<0 || glyph>=i->swffont->numchars) {
2793 msg("<warning> No character %d in font %s (%d chars)", glyph, FIXNULL((char*)i->swffont->name), i->swffont->numchars);
2797 setfontscale(dev, matrix->m00, matrix->m01, matrix->m10, matrix->m11, matrix->tx, matrix->ty, 0);
2799 double det = i->fontmatrix.sx/65536.0 * i->fontmatrix.sy/65536.0 -
2800 i->fontmatrix.r0/65536.0 * i->fontmatrix.r1/65536.0;
2801 if(fabs(det) < 0.0005) {
2802 /* x direction equals y direction- the text is invisible */
2803 msg("<verbose> Not drawing invisible character character %d (det=%f, m=[%f %f;%f %f]\n", glyph,
2805 i->fontmatrix.sx/65536.0, i->fontmatrix.r1/65536.0,
2806 i->fontmatrix.r0/65536.0, i->fontmatrix.sy/65536.0);
2810 /*if(i->swffont->glyph[glyph].shape->bitlen <= 16) {
2811 msg("<warning> Glyph %d in current charset (%s, %d characters) is empty",
2812 glyph, FIXNULL((char*)i->swffont->name), i->swffont->numchars);
2816 /* calculate character position with respect to the current font matrix */
2817 double s = 20 * GLYPH_SCALE / det;
2818 double px = matrix->tx - i->fontmatrix.tx/20.0;
2819 double py = matrix->ty - i->fontmatrix.ty/20.0;
2820 int x = (SCOORD)(( px * i->fontmatrix.sy/65536.0 - py * i->fontmatrix.r1/65536.0)*s);
2821 int y = (SCOORD)((- px * i->fontmatrix.r0/65536.0 + py * i->fontmatrix.sx/65536.0)*s);
2822 if(x>32767 || x<-32768 || y>32767 || y<-32768) {
2823 msg("<verbose> Moving character origin to %f %f\n", matrix->tx, matrix->ty);
2825 setfontscale(dev, matrix->m00, matrix->m01, matrix->m10, matrix->m11, matrix->tx, matrix->ty, 1);
2833 msg("<trace> Drawing char %d in font %d at %d,%d in color %02x%02x%02x%02x",
2834 glyph, i->swffont->id, x, y, color->r, color->g, color->b, color->a);
2836 putcharacter(dev, i->swffont->id, glyph, x, y, i->current_font_size, *(RGBA*)color);
2837 swf_FontUseGlyph(i->swffont, glyph);