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, int fill)
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;
1682 /* check whether the next segment is zero */
1683 if(line->type == gfx_moveTo) {
1684 moveto(dev, i->tag, line->x, line->y);
1685 px = lastx = line->x;
1686 py = lasty = line->y;
1688 } if(line->type == gfx_lineTo) {
1689 lineto(dev, i->tag, line->x, line->y);
1694 } else if(line->type == gfx_splineTo) {
1696 s.x = line->sx;p.x = line->x;
1697 s.y = line->sy;p.y = line->y;
1698 splineto(dev, i->tag, s, p);
1706 msg("<trace> drawgfxline, %d lines, %d splines", lines, splines);
1710 static void drawlink(gfxdevice_t*dev, ActionTAG*actions1, ActionTAG*actions2, gfxline_t*points, char mouseover)
1712 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1721 int buttonid = getNewID(dev);
1722 gfxbbox_t bbox = gfxline_getbbox(points);
1725 myshapeid = getNewID(dev);
1726 i->tag = swf_InsertTag(i->tag,ST_DEFINESHAPE3);
1727 swf_ShapeNew(&i->shape);
1728 rgb.r = rgb.b = rgb.a = rgb.g = 0;
1729 fsid = swf_ShapeAddSolidFillStyle(i->shape,&rgb);
1730 swf_SetU16(i->tag, myshapeid);
1731 r.xmin = (int)(bbox.xmin*20);
1732 r.ymin = (int)(bbox.ymin*20);
1733 r.xmax = (int)(bbox.xmax*20);
1734 r.ymax = (int)(bbox.ymax*20);
1735 r = swf_ClipRect(i->pagebbox, r);
1736 swf_SetRect(i->tag,&r);
1737 swf_SetShapeStyles(i->tag,i->shape);
1738 swf_ShapeCountBits(i->shape,NULL,NULL);
1739 swf_SetShapeBits(i->tag,i->shape);
1740 swf_ShapeSetAll(i->tag,i->shape,/*x*/0,/*y*/0,0,fsid,0);
1741 i->swflastx = i->swflasty = 0;
1742 drawgfxline(dev, points, 1);
1743 swf_ShapeSetEnd(i->tag);
1746 myshapeid2 = getNewID(dev);
1747 i->tag = swf_InsertTag(i->tag,ST_DEFINESHAPE3);
1748 swf_ShapeNew(&i->shape);
1750 rgb = i->config_linkcolor;
1752 fsid = swf_ShapeAddSolidFillStyle(i->shape,&rgb);
1753 swf_SetU16(i->tag, myshapeid2);
1754 r.xmin = (int)(bbox.xmin*20);
1755 r.ymin = (int)(bbox.ymin*20);
1756 r.xmax = (int)(bbox.xmax*20);
1757 r.ymax = (int)(bbox.ymax*20);
1758 r = swf_ClipRect(i->pagebbox, r);
1759 swf_SetRect(i->tag,&r);
1760 swf_SetShapeStyles(i->tag,i->shape);
1761 swf_ShapeCountBits(i->shape,NULL,NULL);
1762 swf_SetShapeBits(i->tag,i->shape);
1763 swf_ShapeSetAll(i->tag,i->shape,/*x*/0,/*y*/0,0,fsid,0);
1764 i->swflastx = i->swflasty = 0;
1765 drawgfxline(dev, points, 1);
1766 swf_ShapeSetEnd(i->tag);
1770 i->tag = swf_InsertTag(i->tag,ST_DEFINEBUTTON);
1771 swf_SetU16(i->tag,buttonid); //id
1772 swf_ButtonSetFlags(i->tag, 0); //menu=no
1773 swf_ButtonSetRecord(i->tag,0x01,myshapeid,i->depth,0,0);
1774 swf_ButtonSetRecord(i->tag,0x02,myshapeid2,i->depth,0,0);
1775 swf_ButtonSetRecord(i->tag,0x04,myshapeid2,i->depth,0,0);
1776 swf_ButtonSetRecord(i->tag,0x08,myshapeid,i->depth,0,0);
1777 swf_SetU8(i->tag,0);
1778 swf_ActionSet(i->tag,actions1);
1779 swf_SetU8(i->tag,0);
1783 i->tag = swf_InsertTag(i->tag,ST_DEFINEBUTTON2);
1784 swf_SetU16(i->tag,buttonid); //id
1785 swf_ButtonSetFlags(i->tag, 0); //menu=no
1786 swf_ButtonSetRecord(i->tag,0x01,myshapeid,i->depth,0,0);
1787 swf_ButtonSetRecord(i->tag,0x02,myshapeid2,i->depth,0,0);
1788 swf_ButtonSetRecord(i->tag,0x04,myshapeid2,i->depth,0,0);
1789 swf_ButtonSetRecord(i->tag,0x08,myshapeid,i->depth,0,0);
1790 swf_SetU8(i->tag,0); // end of button records
1791 swf_ButtonSetCondition(i->tag, BC_IDLE_OVERUP);
1792 swf_ActionSet(i->tag,actions1);
1794 swf_ButtonSetCondition(i->tag, BC_OVERUP_IDLE);
1795 swf_ActionSet(i->tag,actions2);
1796 swf_SetU8(i->tag,0);
1797 swf_ButtonPostProcess(i->tag, 2);
1799 swf_SetU8(i->tag,0);
1800 swf_ButtonPostProcess(i->tag, 1);
1804 sprintf(name, "link%d", buttonid);
1806 msg("<trace> Placing link ID %d", buttonid);
1807 i->tag = swf_InsertTag(i->tag,ST_PLACEOBJECT2);
1809 if(posx!=0 || posy!=0) {
1811 p.x = (int)(posx*20);
1812 p.y = (int)(posy*20);
1813 p = swf_TurnPoint(p, &i->page_matrix);
1818 swf_ObjectPlace(i->tag, buttonid, getNewDepth(dev),&m,0,name);
1820 swf_ObjectPlace(i->tag, buttonid, getNewDepth(dev),&i->page_matrix,0,name);
1827 for(t=0;t<picpos;t++)
1829 if(pic_xids[t] == xid &&
1830 pic_yids[t] == yid) {
1831 width = pic_width[t];
1832 height = pic_height[t];
1836 pic_ids[picpos] = swfoutput_drawimagelosslessN(&output, pic, pal, width, height, x1,y1,x2,y2,x3,y3,x4,y4, numpalette);
1837 pic_xids[picpos] = xid;
1838 pic_yids[picpos] = yid;
1839 pic_width[picpos] = width;
1840 pic_height[picpos] = height;
1843 pic[width*y+x] = buf[0];
1847 xid += pal[1].r*3 + pal[1].g*11 + pal[1].b*17;
1848 yid += pal[1].r*7 + pal[1].g*5 + pal[1].b*23;
1852 xid += x*r+x*b*3+x*g*7+x*a*11;
1853 yid += y*r*3+y*b*17+y*g*19+y*a*11;
1855 for(t=0;t<picpos;t++)
1857 if(pic_xids[t] == xid &&
1858 pic_yids[t] == yid) {
1867 int swf_setparameter(gfxdevice_t*dev, const char*name, const char*value)
1869 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1871 msg("<trace> swfdevice: %s=%s", name, value);
1872 if(!strcmp(name, "jpegsubpixels")) {
1873 i->config_jpegsubpixels = atof(value);
1874 } else if(!strcmp(name, "ppmsubpixels")) {
1875 i->config_ppmsubpixels = atof(value);
1876 } else if(!strcmp(name, "subpixels")) {
1877 i->config_ppmsubpixels = i->config_jpegsubpixels = atof(value);
1878 } else if(!strcmp(name, "drawonlyshapes")) {
1879 i->config_drawonlyshapes = atoi(value);
1880 } else if(!strcmp(name, "ignoredraworder")) {
1881 i->config_ignoredraworder = atoi(value);
1882 } else if(!strcmp(name, "mark")) {
1883 if(!value || !value[0]) {
1884 if(i->mark) free(i->mark);
1888 i->mark = strdup("...");
1889 for(t=0;t<3;t++) if(value[t]) i->mark[t] = value[t];
1891 } else if(!strcmp(name, "filloverlap")) {
1892 i->config_filloverlap = atoi(value);
1893 } else if(!strcmp(name, "linksopennewwindow")) {
1894 i->config_opennewwindow = atoi(value);
1895 } else if(!strcmp(name, "opennewwindow")) {
1896 i->config_opennewwindow = atoi(value);
1897 } else if(!strcmp(name, "storeallcharacters")) {
1898 i->config_storeallcharacters = atoi(value);
1899 } else if(!strcmp(name, "enablezlib")) {
1900 i->config_enablezlib = atoi(value);
1901 } else if(!strcmp(name, "bboxvars")) {
1902 i->config_bboxvars = atoi(value);
1903 } else if(!strcmp(name, "showclipshapes")) {
1904 i->config_showclipshapes = atoi(value);
1905 } else if(!strcmp(name, "reordertags")) {
1906 i->config_reordertags = atoi(value);
1907 } else if(!strcmp(name, "internallinkfunction")) {
1908 i->config_internallinkfunction = strdup(value);
1909 } else if(!strcmp(name, "externallinkfunction")) {
1910 i->config_externallinkfunction = strdup(value);
1911 } else if(!strcmp(name, "linkfunction")) { //sets both internallinkfunction and externallinkfunction
1912 i->config_internallinkfunction = strdup(value);
1913 i->config_externallinkfunction = strdup(value);
1914 } else if(!strcmp(name, "disable_polygon_conversion")) {
1915 i->config_disable_polygon_conversion = atoi(value);
1916 } else if(!strcmp(name, "normalize_polygon_positions")) {
1917 i->config_normalize_polygon_positions = atoi(value);
1918 } else if(!strcmp(name, "wxwindowparams")) {
1919 i->config_watermark = atoi(value);
1920 } else if(!strcmp(name, "insertstop")) {
1921 i->config_insertstoptag = atoi(value);
1922 } else if(!strcmp(name, "protect")) {
1923 i->config_protect = atoi(value);
1924 if(i->config_protect && i->tag) {
1925 i->tag = swf_InsertTag(i->tag, ST_PROTECT);
1927 } else if(!strcmp(name, "flashversion")) {
1928 i->config_flashversion = atoi(value);
1930 i->swf->fileVersion = i->config_flashversion;
1932 } else if(!strcmp(name, "framerate")) {
1933 i->config_framerate = atoi(value);
1935 i->swf->frameRate = i->config_framerate*0x100;
1937 } else if(!strcmp(name, "minlinewidth")) {
1938 i->config_minlinewidth = atof(value);
1939 } else if(!strcmp(name, "caplinewidth")) {
1940 i->config_caplinewidth = atof(value);
1941 } else if(!strcmp(name, "linktarget")) {
1942 i->config_linktarget = strdup(value);
1943 } else if(!strcmp(name, "dumpfonts")) {
1944 i->config_dumpfonts = atoi(value);
1945 } else if(!strcmp(name, "animate")) {
1946 i->config_animate = atoi(value);
1947 } else if(!strcmp(name, "simpleviewer")) {
1948 i->config_simpleviewer = atoi(value);
1949 } else if(!strcmp(name, "next_bitmap_is_jpeg")) {
1951 } else if(!strcmp(name, "jpegquality")) {
1952 int val = atoi(value);
1954 if(val>101) val=101;
1955 i->config_jpegquality = val;
1956 } else if(!strcmp(name, "splinequality")) {
1957 int v = atoi(value);
1958 v = 500-(v*5); // 100% = 0.25 pixel, 0% = 25 pixel
1960 i->config_splinemaxerror = v;
1961 } else if(!strcmp(name, "fontquality")) {
1962 int v = atoi(value);
1963 v = 500-(v*5); // 100% = 0.25 pixel, 0% = 25 pixel
1965 i->config_fontsplinemaxerror = v;
1966 } else if(!strcmp(name, "linkcolor")) {
1967 if(strlen(value)!=8) {
1968 fprintf(stderr, "Unknown format for option 'linkcolor'. (%s <-> RRGGBBAA)\n", value);
1971 # define NIBBLE(s) (((s)>='0' && (s)<='9')?((s)-'0'):((s)&0x0f)+9)
1972 i->config_linkcolor.r = NIBBLE(value[0])<<4 | NIBBLE(value[1]);
1973 i->config_linkcolor.g = NIBBLE(value[2])<<4 | NIBBLE(value[3]);
1974 i->config_linkcolor.b = NIBBLE(value[4])<<4 | NIBBLE(value[5]);
1975 i->config_linkcolor.a = NIBBLE(value[6])<<4 | NIBBLE(value[7]);
1976 } else if(!strcmp(name, "help")) {
1977 printf("\nSWF layer options:\n");
1978 printf("jpegdpi=<dpi> resolution adjustment for jpeg images\n");
1979 printf("jpegsubpixels=<pixels> resolution adjustment for jpeg images (same as jpegdpi, but in pixels)\n");
1980 printf("ppmdpi=<dpi> resolution adjustment for lossless images\n");
1981 printf("ppmsubpixels=<pixels resolution adjustment for lossless images (same as ppmdpi, but in pixels)\n");
1982 printf("subpixels=<pixels> shortcut for setting both jpegsubpixels and ppmsubpixels\n");
1983 printf("drawonlyshapes convert everything to shapes (currently broken)\n");
1984 printf("ignoredraworder allow to perform a few optimizations for creating smaller SWFs\n");
1985 printf("linksopennewwindow make links open a new browser window\n");
1986 printf("linktarget target window name of new links\n");
1987 printf("linkcolor=<color) color of links (format: RRGGBBAA)\n");
1988 printf("storeallcharacters don't reduce the fonts to used characters in the output file\n");
1989 printf("enablezlib switch on zlib compression (also done if flashversion>=7)\n");
1990 printf("bboxvars store the bounding box of the SWF file in actionscript variables\n");
1991 printf("reordertags=0/1 (default: 1) perform some tag optimizations\n");
1992 printf("internallinkfunction=<name> when the user clicks a internal link (to a different page) in the converted file, this actionscript function is called\n");
1993 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");
1994 printf("disable_polygon_conversion never convert strokes to polygons (will remove capstyles and joint styles)\n");
1995 printf("caplinewidth=<width> the minimum thichness a line needs to have so that capstyles become visible (and are converted)\n");
1996 printf("insertstop put an ActionScript \"STOP\" tag in every frame\n");
1997 printf("protect add a \"protect\" tag to the file, to prevent loading in the Flash editor\n");
1998 printf("flashversion=<version> the SWF fileversion (6)\n");
1999 printf("minlinewidth=<width> convert horizontal/vertical boxes smaller than this width to lines (0.05) \n");
2000 printf("animate insert a showframe tag after each placeobject (animate draw order of PDF files)\n");
2001 printf("jpegquality=<quality> set compression quality of jpeg images\n");
2008 // --------------------------------------------------------------------
2010 static CXFORM gfxcxform_to_cxform(gfxcxform_t* c)
2013 swf_GetCXForm(0, &cx, 1);
2016 if(c->rg!=0 || c->rb!=0 || c->ra!=0 ||
2017 c->gr!=0 || c->gb!=0 || c->ga!=0 ||
2018 c->br!=0 || c->bg!=0 || c->ba!=0 ||
2019 c->ar!=0 || c->ag!=0 || c->ab!=0)
2020 msg("<warning> CXForm not SWF-compatible");
2022 cx.a0 = (S16)(c->aa*256);
2023 cx.r0 = (S16)(c->rr*256);
2024 cx.g0 = (S16)(c->gg*256);
2025 cx.b0 = (S16)(c->bb*256);
2034 static int imageInCache(gfxdevice_t*dev, void*data, int width, int height)
2038 static void addImageToCache(gfxdevice_t*dev, void*data, int width, int height)
2042 static int add_image(swfoutput_internal*i, gfximage_t*img, int targetwidth, int targetheight, int* newwidth, int* newheight)
2044 gfxdevice_t*dev = i->dev;
2046 RGBA*mem = (RGBA*)img->data;
2048 int sizex = img->width;
2049 int sizey = img->height;
2050 int is_jpeg = i->jpeg;
2053 int newsizex=sizex, newsizey=sizey;
2056 if(is_jpeg && i->config_jpegsubpixels) {
2057 newsizex = (int)(targetwidth*i->config_jpegsubpixels + 0.5);
2058 newsizey = (int)(targetheight*i->config_jpegsubpixels + 0.5);
2059 } else if(!is_jpeg && i->config_ppmsubpixels) {
2060 newsizex = (int)(targetwidth*i->config_ppmsubpixels + 0.5);
2061 newsizey = (int)(targetheight*i->config_ppmsubpixels + 0.5);
2065 if(sizex<=0 || sizey<=0)
2072 /* TODO: cache images */
2074 if(newsizex<sizex || newsizey<sizey) {
2075 msg("<verbose> Scaling %dx%d image to %dx%d", sizex, sizey, newsizex, newsizey);
2076 newpic = swf_ImageScale(mem, sizex, sizey, newsizex, newsizey);
2077 *newwidth = sizex = newsizex;
2078 *newheight = sizey = newsizey;
2081 *newwidth = newsizex = sizex;
2082 *newheight = newsizey = sizey;
2085 int num_colors = swf_ImageGetNumberOfPaletteEntries(mem,sizex,sizey,0);
2086 int has_alpha = swf_ImageHasAlpha(mem,sizex,sizey);
2088 msg("<verbose> Drawing %dx%d %s%simage (id %d) at size %dx%d (%dx%d), %s%d colors",
2090 has_alpha?(has_alpha==2?"semi-transparent ":"transparent "):"",
2091 is_jpeg?"jpeg-":"", i->currentswfid+1,
2093 targetwidth, targetheight,
2094 /*newsizex, newsizey,*/
2095 num_colors>256?">":"", num_colors>256?256:num_colors);
2097 /*RGBA* pal = (RGBA*)rfx_alloc(sizeof(RGBA)*num_colors);
2098 swf_ImageGetNumberOfPaletteEntries(mem,sizex,sizey,pal);
2100 for(t=0;t<num_colors;t++) {
2101 printf("%02x%02x%02x%02x ",
2102 pal[t].r, pal[t].g, pal[t].b, pal[t].a);
2109 int cacheid = imageInCache(dev, mem, sizex, sizey);
2112 bitid = getNewID(dev);
2113 i->tag = swf_AddImage(i->tag, bitid, mem, sizex, sizey, i->config_jpegquality);
2114 addImageToCache(dev, mem, sizex, sizey);
2124 static SRECT gfxline_getSWFbbox(gfxline_t*line)
2126 gfxbbox_t bbox = gfxline_getbbox(line);
2128 r.xmin = (int)(bbox.xmin*20);
2129 r.ymin = (int)(bbox.ymin*20);
2130 r.xmax = (int)(bbox.xmax*20);
2131 r.ymax = (int)(bbox.ymax*20);
2135 int line_is_empty(gfxline_t*line)
2138 if(line->type != gfx_moveTo)
2145 static void swf_fillbitmap(gfxdevice_t*dev, gfxline_t*line, gfximage_t*img, gfxmatrix_t*matrix, gfxcxform_t*cxform)
2147 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
2149 if(line_is_empty(line))
2155 int targetx = (int)(sqrt(matrix->m00*matrix->m00 + matrix->m01*matrix->m01)*img->width);
2156 int targety = (int)(sqrt(matrix->m10*matrix->m10 + matrix->m11*matrix->m11)*img->height);
2158 int newwidth=0,newheight=0;
2159 int bitid = add_image(i, img, targetx, targety, &newwidth, &newheight);
2162 double fx = (double)img->width / (double)newwidth;
2163 double fy = (double)img->height / (double)newheight;
2166 m.sx = (int)(65536*20*matrix->m00*fx); m.r1 = (int)(65536*20*matrix->m10*fy);
2167 m.r0 = (int)(65536*20*matrix->m01*fx); m.sy = (int)(65536*20*matrix->m11*fy);
2168 m.tx = (int)(matrix->tx*20);
2169 m.ty = (int)(matrix->ty*20);
2172 int myshapeid = getNewID(dev);
2173 i->tag = swf_InsertTag(i->tag,ST_DEFINESHAPE);
2175 swf_ShapeNew(&shape);
2176 int fsid = swf_ShapeAddBitmapFillStyle(shape,&m,bitid,1);
2177 swf_SetU16(i->tag, myshapeid);
2178 SRECT r = gfxline_getSWFbbox(line);
2179 r = swf_ClipRect(i->pagebbox, r);
2180 swf_SetRect(i->tag,&r);
2181 swf_SetShapeStyles(i->tag,shape);
2182 swf_ShapeCountBits(shape,NULL,NULL);
2183 swf_SetShapeBits(i->tag,shape);
2184 swf_ShapeSetAll(i->tag,shape,UNDEFINED_COORD,UNDEFINED_COORD,0,fsid,0);
2185 i->swflastx = i->swflasty = UNDEFINED_COORD;
2186 drawgfxline(dev, line, 1);
2187 swf_ShapeSetEnd(i->tag);
2188 swf_ShapeFree(shape);
2190 msg("<trace> Placing image, shape ID %d, bitmap ID %d", myshapeid, bitid);
2191 i->tag = swf_InsertTag(i->tag,ST_PLACEOBJECT2);
2192 CXFORM cxform2 = gfxcxform_to_cxform(cxform);
2193 swf_ObjectPlace(i->tag,myshapeid,getNewDepth(dev),&i->page_matrix,&cxform2,NULL);
2196 static RGBA col_black = {255,0,0,0};
2198 static void drawoutline(gfxdevice_t*dev, gfxline_t*line)
2200 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
2202 int myshapeid = getNewID(dev);
2203 i->tag = swf_InsertTag(i->tag,ST_DEFINESHAPE3);
2206 swf_ShapeNew(&shape);
2207 int lsid = swf_ShapeAddLineStyle(shape,1,&col_black);
2209 swf_SetU16(i->tag,myshapeid);
2210 SRECT r = gfxline_getSWFbbox(line);
2211 r = swf_ClipRect(i->pagebbox, r);
2212 swf_SetRect(i->tag,&r);
2213 swf_SetShapeStyles(i->tag,shape);
2214 swf_ShapeCountBits(shape,NULL,NULL);
2215 swf_SetShapeBits(i->tag,shape);
2216 swf_ShapeSetAll(i->tag,shape,UNDEFINED_COORD,UNDEFINED_COORD,lsid,0,0);
2217 drawgfxline(dev, line, 1);
2218 swf_ShapeSetEnd(i->tag);
2219 swf_ShapeFree(shape);
2221 i->tag = swf_InsertTag(i->tag,ST_PLACEOBJECT2);
2222 swf_ObjectPlace(i->tag, myshapeid, getNewDepth(dev), 0,0,0);
2225 static void swf_startclip(gfxdevice_t*dev, gfxline_t*line)
2227 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
2232 if(i->clippos >= 127)
2234 msg("<warning> Too many clip levels.");
2238 if(i->config_showclipshapes)
2239 drawoutline(dev, line);
2241 int myshapeid = getNewID(dev);
2242 i->tag = swf_InsertTag(i->tag,ST_DEFINESHAPE3);
2244 memset(&col, 0, sizeof(RGBA));
2247 swf_ShapeNew(&shape);
2248 int fsid = swf_ShapeAddSolidFillStyle(shape,&col);
2250 RGBA markcol = {0,i->mark[0],i->mark[1],i->mark[2]};
2251 swf_ShapeAddSolidFillStyle(shape,&markcol);
2253 swf_SetU16(i->tag,myshapeid);
2254 SRECT r = gfxline_getSWFbbox(line);
2255 r = swf_ClipRect(i->pagebbox, r);
2256 swf_SetRect(i->tag,&r);
2257 swf_SetShapeStyles(i->tag,shape);
2258 swf_ShapeCountBits(shape,NULL,NULL);
2259 swf_SetShapeBits(i->tag,shape);
2260 swf_ShapeSetAll(i->tag,shape,UNDEFINED_COORD,UNDEFINED_COORD,0,fsid,0);
2261 i->swflastx = i->swflasty = UNDEFINED_COORD;
2262 i->shapeisempty = 1;
2263 drawgfxline(dev, line, 1);
2264 if(i->shapeisempty) {
2265 /* an empty clip shape is equivalent to a shape with no area */
2266 int x = line?line->x:0;
2267 int y = line?line->y:0;
2268 moveto(dev, i->tag, x,y);
2269 lineto(dev, i->tag, x,y);
2270 lineto(dev, i->tag, x,y);
2272 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)) {
2273 if(i->config_watermark) {
2274 gfxbbox_t r; r.xmin = r.ymin = 0;r.xmax = i->max_x;r.ymax = i->max_y;
2275 draw_watermark(dev, r, 1);
2278 swf_ShapeSetEnd(i->tag);
2279 swf_ShapeFree(shape);
2281 /* TODO: remember the bbox, and check all shapes against it */
2283 msg("<trace> Placing clip ID %d", myshapeid);
2284 i->tag = swf_InsertTag(i->tag,ST_PLACEOBJECT2);
2285 i->cliptags[i->clippos] = i->tag;
2286 i->clipshapes[i->clippos] = myshapeid;
2287 i->clipdepths[i->clippos] = getNewDepth(dev);
2291 static void swf_endclip(gfxdevice_t*dev)
2293 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
2300 msg("<error> Invalid end of clipping region");
2304 /*swf_ObjectPlaceClip(i->cliptags[i->clippos],i->clipshapes[i->clippos],i->clipdepths[i->clippos],&i->page_matrix,NULL,NULL,
2305 / * clip to depth: * / i->depth <= i->clipdepths[i->clippos]? i->depth : i->depth - 1);
2307 swf_ObjectPlaceClip(i->cliptags[i->clippos],i->clipshapes[i->clippos],i->clipdepths[i->clippos],&i->page_matrix,NULL,NULL,i->depth);
2309 static int gfxline_type(gfxline_t*line)
2315 int haszerosegments=0;
2318 if(line->type == gfx_moveTo) {
2321 } else if(line->type == gfx_lineTo) {
2325 } else if(line->type == gfx_splineTo) {
2327 if(tmpsplines>lines)
2335 if(lines==0 && splines==0) return 0;
2336 else if(lines==1 && splines==0) return 1;
2337 else if(lines==0 && splines==1) return 2;
2338 else if(splines==0) return 3;
2342 static int gfxline_has_dots(gfxline_t*line)
2350 if(line->type == gfx_moveTo) {
2351 /* test the length of the preceding line, and assume it is a dot if
2352 it's length is less than 1.0. But *only* if there's a noticable
2353 gap between the previous line and the next moveTo. (I've come
2354 across a PDF where thousands of "dots" were stringed together,
2356 int last_short_gap = short_gap;
2357 if((fabs(line->x - x) + fabs(line->y - y)) < 1.0) {
2362 if(isline && dist < 1 && !short_gap && !last_short_gap) {
2367 } else if(line->type == gfx_lineTo) {
2368 dist += fabs(line->x - x) + fabs(line->y - y);
2370 } else if(line->type == gfx_splineTo) {
2371 dist += fabs(line->sx - x) + fabs(line->sy - y) +
2372 fabs(line->x - line->sx) + fabs(line->y - line->sy);
2379 if(isline && dist < 1 && !short_gap) {
2385 static int gfxline_fix_short_edges(gfxline_t*line)
2389 if(line->type == gfx_lineTo) {
2390 if(fabs(line->x - x) + fabs(line->y - y) < 0.01) {
2393 } else if(line->type == gfx_splineTo) {
2394 if(fabs(line->sx - x) + fabs(line->sy - y) +
2395 fabs(line->x - line->sx) + fabs(line->y - line->sy) < 0.01) {
2406 static char is_inside_page(gfxdevice_t*dev, gfxcoord_t x, gfxcoord_t y)
2408 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
2409 if(x<i->min_x || x>i->max_x) return 0;
2410 if(y<i->min_y || y>i->max_y) return 0;
2414 gfxline_t* gfxline_move(gfxline_t*line, double x, double y)
2416 gfxline_t*l = line = gfxline_clone(line);
2428 //#define NORMALIZE_POLYGON_POSITIONS
2430 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)
2432 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
2433 if(line_is_empty(line))
2435 int type = gfxline_type(line);
2436 int has_dots = gfxline_has_dots(line);
2437 gfxbbox_t r = gfxline_getbbox(line);
2438 int is_outside_page = !is_inside_page(dev, r.xmin, r.ymin) || !is_inside_page(dev, r.xmax, r.ymax);
2440 /* TODO: * split line into segments, and perform this check for all segments */
2442 if(i->config_disable_polygon_conversion || type>=5 ||
2444 (width <= i->config_caplinewidth
2445 || (cap_style == gfx_capRound && joint_style == gfx_joinRound)
2446 || (cap_style == gfx_capRound && type<=2)))) {} else
2448 /* convert line to polygon */
2449 msg("<trace> draw as polygon, type=%d dots=%d", type, has_dots);
2451 gfxline_fix_short_edges(line);
2452 /* we need to convert the line into a polygon */
2453 gfxpoly_t* poly = gfxpoly_strokeToPoly(line, width, cap_style, joint_style, miterLimit);
2454 gfxline_t*gfxline = gfxpoly_to_gfxline(poly);
2455 dev->fill(dev, gfxline, color);
2456 gfxline_free(gfxline);
2461 msg("<trace> draw as stroke, type=%d dots=%d", type, has_dots);
2464 if(i->config_normalize_polygon_positions) {
2466 double startx = 0, starty = 0;
2467 if(line && line->type == gfx_moveTo) {
2471 line = gfxline_move(line, -startx, -starty);
2472 i->shapeposx = (int)(startx*20);
2473 i->shapeposy = (int)(starty*20);
2476 swfoutput_setstrokecolor(dev, color->r, color->g, color->b, color->a);
2477 swfoutput_setlinewidth(dev, width);
2480 drawgfxline(dev, line, 0);
2482 if(i->config_normalize_polygon_positions) {
2483 free(line); //account for _move
2488 static void swf_fill(gfxdevice_t*dev, gfxline_t*line, gfxcolor_t*color)
2490 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
2491 if(line_is_empty(line))
2495 gfxbbox_t r = gfxline_getbbox(line);
2496 int is_outside_page = !is_inside_page(dev, r.xmin, r.ymin) || !is_inside_page(dev, r.xmax, r.ymax);
2498 //if(i->chardatapos && i->chardata[i->chardatapos-1].color.a) {
2501 if(!i->config_ignoredraworder)
2504 if(i->config_normalize_polygon_positions) {
2506 double startx = 0, starty = 0;
2507 if(line && line->type == gfx_moveTo) {
2511 line = gfxline_move(line, -startx, -starty);
2512 i->shapeposx = (int)(startx*20);
2513 i->shapeposy = (int)(starty*20);
2516 swfoutput_setfillcolor(dev, color->r, color->g, color->b, color->a);
2519 drawgfxline(dev, line, 1);
2521 if(i->currentswfid==2 && r.xmin==0 && r.ymin==0 && r.xmax==i->max_x && r.ymax==i->max_y) {
2522 if(i->config_watermark) {
2523 draw_watermark(dev, r, 1);
2527 msg("<trace> end of swf_fill (shapeid=%d)", i->shapeid);
2529 if(i->config_normalize_polygon_positions) {
2530 free(line); //account for _move
2534 static GRADIENT* gfxgradient_to_GRADIENT(gfxgradient_t*gradient)
2537 gfxgradient_t*g = gradient;
2542 GRADIENT* swfgradient = malloc(sizeof(GRADIENT));
2543 swfgradient->num = num;
2544 swfgradient->rgba = malloc(sizeof(swfgradient->rgba[0])*num);
2545 swfgradient->ratios = malloc(sizeof(swfgradient->ratios[0])*num);
2550 swfgradient->ratios[num] = g->pos*255;
2551 swfgradient->rgba[num] = *(RGBA*)&g->color;
2558 static void swf_fillgradient(gfxdevice_t*dev, gfxline_t*line, gfxgradient_t*gradient, gfxgradienttype_t type, gfxmatrix_t*matrix)
2560 if(line_is_empty(line))
2562 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
2564 if(line_is_empty(line))
2567 GRADIENT* swfgradient = gfxgradient_to_GRADIENT(gradient);
2574 double f = type==gfxgradient_radial?4:4;
2576 m.sx = (int)(matrix->m00*20*f); m.r1 = (int)(matrix->m10*20*f);
2577 m.r0 = (int)(matrix->m01*20*f); m.sy = (int)(matrix->m11*20*f);
2578 m.tx = (int)(matrix->tx*20);
2579 m.ty = (int)(matrix->ty*20);
2582 int myshapeid = getNewID(dev);
2583 i->tag = swf_InsertTag(i->tag, ST_DEFINESHAPE2);
2585 swf_ShapeNew(&shape);
2586 int fsid = swf_ShapeAddGradientFillStyle(shape,&m,swfgradient,type==gfxgradient_radial);
2587 swf_SetU16(i->tag, myshapeid);
2588 SRECT r = gfxline_getSWFbbox(line);
2589 r = swf_ClipRect(i->pagebbox, r);
2590 swf_SetRect(i->tag,&r);
2591 swf_SetShapeStyles(i->tag,shape);
2592 swf_ShapeCountBits(shape,NULL,NULL);
2593 swf_SetShapeBits(i->tag,shape);
2594 swf_ShapeSetAll(i->tag,shape,UNDEFINED_COORD,UNDEFINED_COORD,0,fsid,0);
2595 i->swflastx = i->swflasty = UNDEFINED_COORD;
2596 drawgfxline(dev, line, 1);
2597 swf_ShapeSetEnd(i->tag);
2598 swf_ShapeFree(shape);
2600 int depth = getNewDepth(dev);
2601 msg("<trace> Placing gradient, shape ID %d, depth %d", myshapeid, depth);
2602 i->tag = swf_InsertTag(i->tag,ST_PLACEOBJECT2);
2603 swf_ObjectPlace(i->tag,myshapeid,depth,&i->page_matrix,NULL,NULL);
2605 swf_FreeGradient(swfgradient);free(swfgradient);
2608 static SWFFONT* gfxfont_to_swffont(gfxfont_t*font, const char* id)
2610 SWFFONT*swffont = (SWFFONT*)rfx_calloc(sizeof(SWFFONT));
2612 SRECT bounds = {0,0,0,0};
2614 swffont->version = 2;
2615 swffont->name = (U8*)strdup(id);
2616 swffont->layout = (SWFLAYOUT*)rfx_calloc(sizeof(SWFLAYOUT));
2617 swffont->layout->ascent = 0;
2618 swffont->layout->descent = 0;
2619 swffont->layout->leading = 0;
2620 swffont->layout->bounds = (SRECT*)rfx_calloc(sizeof(SRECT)*font->num_glyphs);
2621 swffont->encoding = FONT_ENCODING_UNICODE;
2622 swffont->numchars = font->num_glyphs;
2623 swffont->maxascii = font->max_unicode;
2624 swffont->ascii2glyph = (int*)rfx_calloc(sizeof(int)*swffont->maxascii);
2625 swffont->glyph2ascii = (U16*)rfx_calloc(sizeof(U16)*swffont->numchars);
2626 swffont->glyph = (SWFGLYPH*)rfx_calloc(sizeof(SWFGLYPH)*swffont->numchars);
2627 swffont->glyphnames = (char**)rfx_calloc(sizeof(char*)*swffont->numchars);
2628 for(t=0;t<font->max_unicode;t++) {
2629 swffont->ascii2glyph[t] = font->unicode2glyph[t];
2631 for(t=0;t<font->num_glyphs;t++) {
2635 swffont->glyph2ascii[t] = font->glyphs[t].unicode;
2636 if(swffont->glyph2ascii[t] == 0xffff || swffont->glyph2ascii[t] == 0x0000) {
2637 /* flash 8 flashtype requires unique unicode IDs for each character.
2638 We use the Unicode private user area to assign characters, hoping that
2639 the font doesn't contain more than 2048 glyphs */
2640 swffont->glyph2ascii[t] = 0xe000 + (t&0x1fff);
2643 if(font->glyphs[t].name) {
2644 swffont->glyphnames[t] = strdup(font->glyphs[t].name);
2646 swffont->glyphnames[t] = 0;
2648 advance = (int)(font->glyphs[t].advance);
2650 swf_Shape01DrawerInit(&draw, 0);
2651 line = font->glyphs[t].line;
2654 c.x = line->sx * GLYPH_SCALE; c.y = line->sy * GLYPH_SCALE;
2655 to.x = line->x * GLYPH_SCALE; to.y = line->y * GLYPH_SCALE;
2656 if(line->type == gfx_moveTo) {
2657 draw.moveTo(&draw, &to);
2658 } else if(line->type == gfx_lineTo) {
2659 draw.lineTo(&draw, &to);
2660 } else if(line->type == gfx_splineTo) {
2661 draw.splineTo(&draw, &c, &to);
2666 swffont->glyph[t].shape = swf_ShapeDrawerToShape(&draw);
2667 swffont->layout->bounds[t] = swf_ShapeDrawerGetBBox(&draw);
2669 int xmax = swffont->layout->bounds[t].xmax / 20;
2670 if(xmax>0 && xmax*2 < advance) {
2671 printf("fix bad advance value: bbox=%d, advance=%d (%f)\n", xmax, advance, font->glyphs[t].advance);
2675 if(advance<32768/20) {
2676 swffont->glyph[t].advance = advance*20;
2678 swffont->glyph[t].advance = 32767;
2681 draw.dealloc(&draw);
2683 swf_ExpandRect2(&bounds, &swffont->layout->bounds[t]);
2687 /* Flash player will use the advance value from the char, and the ascent/descent values
2688 from the layout for text selection.
2689 ascent will extend the char into negative y direction, from the baseline, while descent
2690 will extend in positive y direction, also from the baseline.
2691 The baseline is defined as the y-position zero
2694 swffont->layout->ascent = -bounds.ymin;
2695 if(swffont->layout->ascent < 0)
2696 swffont->layout->ascent = 0;
2697 swffont->layout->descent = bounds.ymax;
2698 if(swffont->layout->descent < 0)
2699 swffont->layout->descent = 0;
2700 swffont->layout->leading = bounds.ymax - bounds.ymin;
2705 static void swf_addfont(gfxdevice_t*dev, gfxfont_t*font)
2707 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
2709 if(i->swffont && i->swffont->name && !strcmp((char*)i->swffont->name,font->id))
2710 return; // the requested font is the current font
2712 fontlist_t*last=0,*l = i->fontlist;
2715 if(!strcmp((char*)l->swffont->name, font->id)) {
2716 return; // we already know this font
2720 l = (fontlist_t*)rfx_calloc(sizeof(fontlist_t));
2721 l->swffont = gfxfont_to_swffont(font, font->id);
2728 swf_FontSetID(l->swffont, getNewID(i->dev));
2730 if(getScreenLogLevel() >= LOGLEVEL_DEBUG) {
2732 // print font information
2733 msg("<debug> Font %s",font->id);
2734 msg("<debug> | ID: %d", l->swffont->id);
2735 msg("<debug> | Version: %d", l->swffont->version);
2736 msg("<debug> | Name: %s", l->swffont->name);
2737 msg("<debug> | Numchars: %d", l->swffont->numchars);
2738 msg("<debug> | Maxascii: %d", l->swffont->maxascii);
2739 msg("<debug> | Style: %d", l->swffont->style);
2740 msg("<debug> | Encoding: %d", l->swffont->encoding);
2741 for(iii=0; iii<l->swffont->numchars;iii++) {
2742 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,
2743 l->swffont->layout->bounds[iii].xmin/20.0,
2744 l->swffont->layout->bounds[iii].ymin/20.0,
2745 l->swffont->layout->bounds[iii].xmax/20.0,
2746 l->swffont->layout->bounds[iii].ymax/20.0
2749 for(t=0;t<l->swffont->maxascii;t++) {
2750 if(l->swffont->ascii2glyph[t] == iii)
2751 msg("<debug> | - maps to %d",t);
2757 static void swf_switchfont(gfxdevice_t*dev, const char*fontid)
2759 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
2761 if(i->swffont && i->swffont->name && !strcmp((char*)i->swffont->name,fontid))
2762 return; // the requested font is the current font
2764 fontlist_t*l = i->fontlist;
2766 if(!strcmp((char*)l->swffont->name, fontid)) {
2767 i->swffont = l->swffont;
2772 msg("<error> Unknown font id: %s", fontid);
2776 static void swf_drawchar(gfxdevice_t*dev, gfxfont_t*font, int glyph, gfxcolor_t*color, gfxmatrix_t*matrix)
2778 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
2780 msg("<error> swf_drawchar called (glyph %d) without font", glyph);
2783 if(!i->swffont || !i->swffont->name || strcmp((char*)i->swffont->name,font->id)) // not equal to current font
2785 /* TODO: remove the need for this (enhance getcharacterbbox so that it can cope
2786 with multiple fonts */
2788 swf_switchfont(dev, font->id); // set the current font
2791 msg("<warning> swf_drawchar: Font is NULL");
2794 if(glyph<0 || glyph>=i->swffont->numchars) {
2795 msg("<warning> No character %d in font %s (%d chars)", glyph, FIXNULL((char*)i->swffont->name), i->swffont->numchars);
2799 setfontscale(dev, matrix->m00, matrix->m01, matrix->m10, matrix->m11, matrix->tx, matrix->ty, 0);
2801 double det = i->fontmatrix.sx/65536.0 * i->fontmatrix.sy/65536.0 -
2802 i->fontmatrix.r0/65536.0 * i->fontmatrix.r1/65536.0;
2803 if(fabs(det) < 0.0005) {
2804 /* x direction equals y direction- the text is invisible */
2805 msg("<verbose> Not drawing invisible character character %d (det=%f, m=[%f %f;%f %f]\n", glyph,
2807 i->fontmatrix.sx/65536.0, i->fontmatrix.r1/65536.0,
2808 i->fontmatrix.r0/65536.0, i->fontmatrix.sy/65536.0);
2812 /*if(i->swffont->glyph[glyph].shape->bitlen <= 16) {
2813 msg("<warning> Glyph %d in current charset (%s, %d characters) is empty",
2814 glyph, FIXNULL((char*)i->swffont->name), i->swffont->numchars);
2818 /* calculate character position with respect to the current font matrix */
2819 double s = 20 * GLYPH_SCALE / det;
2820 double px = matrix->tx - i->fontmatrix.tx/20.0;
2821 double py = matrix->ty - i->fontmatrix.ty/20.0;
2822 int x = (SCOORD)(( px * i->fontmatrix.sy/65536.0 - py * i->fontmatrix.r1/65536.0)*s);
2823 int y = (SCOORD)((- px * i->fontmatrix.r0/65536.0 + py * i->fontmatrix.sx/65536.0)*s);
2824 if(x>32767 || x<-32768 || y>32767 || y<-32768) {
2825 msg("<verbose> Moving character origin to %f %f\n", matrix->tx, matrix->ty);
2827 setfontscale(dev, matrix->m00, matrix->m01, matrix->m10, matrix->m11, matrix->tx, matrix->ty, 1);
2828 /* since we just moved the char origin to the current char's position,
2829 it now has the relative position (0,0) */
2838 msg("<trace> Drawing char %d in font %d at %d,%d in color %02x%02x%02x%02x",
2839 glyph, i->swffont->id, x, y, color->r, color->g, color->b, color->a);
2841 putcharacter(dev, i->swffont->id, glyph, x, y, i->current_font_size, *(RGBA*)color);
2842 swf_FontUseGlyph(i->swffont, glyph);