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("jpegsubpixels=<pixels> resolution adjustment for jpeg images (same as jpegdpi, but in pixels)\n");
1979 printf("ppmsubpixels=<pixels resolution adjustment for lossless images (same as ppmdpi, but in pixels)\n");
1980 printf("subpixels=<pixels> shortcut for setting both jpegsubpixels and ppmsubpixels\n");
1981 printf("drawonlyshapes convert everything to shapes (currently broken)\n");
1982 printf("ignoredraworder allow to perform a few optimizations for creating smaller SWFs\n");
1983 printf("linksopennewwindow make links open a new browser window\n");
1984 printf("linktarget target window name of new links\n");
1985 printf("linkcolor=<color) color of links (format: RRGGBBAA)\n");
1986 printf("storeallcharacters don't reduce the fonts to used characters in the output file\n");
1987 printf("enablezlib switch on zlib compression (also done if flashversion>=7)\n");
1988 printf("bboxvars store the bounding box of the SWF file in actionscript variables\n");
1989 printf("reordertags=0/1 (default: 1) perform some tag optimizations\n");
1990 printf("internallinkfunction=<name> when the user clicks a internal link (to a different page) in the converted file, this actionscript function is called\n");
1991 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");
1992 printf("disable_polygon_conversion never convert strokes to polygons (will remove capstyles and joint styles)\n");
1993 printf("caplinewidth=<width> the minimum thichness a line needs to have so that capstyles become visible (and are converted)\n");
1994 printf("insertstop put an ActionScript \"STOP\" tag in every frame\n");
1995 printf("protect add a \"protect\" tag to the file, to prevent loading in the Flash editor\n");
1996 printf("flashversion=<version> the SWF fileversion (6)\n");
1997 printf("minlinewidth=<width> convert horizontal/vertical boxes smaller than this width to lines (0.05) \n");
1998 printf("simpleviewer Add next/previous buttons to the SWF\n");
1999 printf("animate insert a showframe tag after each placeobject (animate draw order of PDF files)\n");
2000 printf("jpegquality=<quality> set compression quality of jpeg images\n");
2007 // --------------------------------------------------------------------
2009 static CXFORM gfxcxform_to_cxform(gfxcxform_t* c)
2012 swf_GetCXForm(0, &cx, 1);
2015 if(c->rg!=0 || c->rb!=0 || c->ra!=0 ||
2016 c->gr!=0 || c->gb!=0 || c->ga!=0 ||
2017 c->br!=0 || c->bg!=0 || c->ba!=0 ||
2018 c->ar!=0 || c->ag!=0 || c->ab!=0)
2019 msg("<warning> CXForm not SWF-compatible");
2021 cx.a0 = (S16)(c->aa*256);
2022 cx.r0 = (S16)(c->rr*256);
2023 cx.g0 = (S16)(c->gg*256);
2024 cx.b0 = (S16)(c->bb*256);
2033 static int imageInCache(gfxdevice_t*dev, void*data, int width, int height)
2037 static void addImageToCache(gfxdevice_t*dev, void*data, int width, int height)
2041 static int add_image(swfoutput_internal*i, gfximage_t*img, int targetwidth, int targetheight, int* newwidth, int* newheight)
2043 gfxdevice_t*dev = i->dev;
2045 RGBA*mem = (RGBA*)img->data;
2047 int sizex = img->width;
2048 int sizey = img->height;
2049 int is_jpeg = i->jpeg;
2052 int newsizex=sizex, newsizey=sizey;
2055 if(is_jpeg && i->config_jpegsubpixels) {
2056 newsizex = (int)(targetwidth*i->config_jpegsubpixels + 0.5);
2057 newsizey = (int)(targetheight*i->config_jpegsubpixels + 0.5);
2058 } else if(!is_jpeg && i->config_ppmsubpixels) {
2059 newsizex = (int)(targetwidth*i->config_ppmsubpixels + 0.5);
2060 newsizey = (int)(targetheight*i->config_ppmsubpixels + 0.5);
2064 if(sizex<=0 || sizey<=0)
2071 /* TODO: cache images */
2073 if(newsizex<sizex || newsizey<sizey) {
2074 msg("<verbose> Scaling %dx%d image to %dx%d", sizex, sizey, newsizex, newsizey);
2075 newpic = swf_ImageScale(mem, sizex, sizey, newsizex, newsizey);
2076 *newwidth = sizex = newsizex;
2077 *newheight = sizey = newsizey;
2080 *newwidth = newsizex = sizex;
2081 *newheight = newsizey = sizey;
2084 int num_colors = swf_ImageGetNumberOfPaletteEntries(mem,sizex,sizey,0);
2085 int has_alpha = swf_ImageHasAlpha(mem,sizex,sizey);
2087 msg("<verbose> Drawing %dx%d %s%simage (id %d) at size %dx%d (%dx%d), %s%d colors",
2089 has_alpha?(has_alpha==2?"semi-transparent ":"transparent "):"",
2090 is_jpeg?"jpeg-":"", i->currentswfid+1,
2092 targetwidth, targetheight,
2093 /*newsizex, newsizey,*/
2094 num_colors>256?">":"", num_colors>256?256:num_colors);
2096 /*RGBA* pal = (RGBA*)rfx_alloc(sizeof(RGBA)*num_colors);
2097 swf_ImageGetNumberOfPaletteEntries(mem,sizex,sizey,pal);
2099 for(t=0;t<num_colors;t++) {
2100 printf("%02x%02x%02x%02x ",
2101 pal[t].r, pal[t].g, pal[t].b, pal[t].a);
2108 int cacheid = imageInCache(dev, mem, sizex, sizey);
2111 bitid = getNewID(dev);
2112 i->tag = swf_AddImage(i->tag, bitid, mem, sizex, sizey, i->config_jpegquality);
2113 addImageToCache(dev, mem, sizex, sizey);
2123 static SRECT gfxline_getSWFbbox(gfxline_t*line)
2125 gfxbbox_t bbox = gfxline_getbbox(line);
2127 r.xmin = (int)(bbox.xmin*20);
2128 r.ymin = (int)(bbox.ymin*20);
2129 r.xmax = (int)(bbox.xmax*20);
2130 r.ymax = (int)(bbox.ymax*20);
2134 int line_is_empty(gfxline_t*line)
2137 if(line->type != gfx_moveTo)
2144 static void swf_fillbitmap(gfxdevice_t*dev, gfxline_t*line, gfximage_t*img, gfxmatrix_t*matrix, gfxcxform_t*cxform)
2146 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
2148 if(line_is_empty(line))
2154 int targetx = (int)(sqrt(matrix->m00*matrix->m00 + matrix->m01*matrix->m01)*img->width);
2155 int targety = (int)(sqrt(matrix->m10*matrix->m10 + matrix->m11*matrix->m11)*img->height);
2157 int newwidth=0,newheight=0;
2158 int bitid = add_image(i, img, targetx, targety, &newwidth, &newheight);
2161 double fx = (double)img->width / (double)newwidth;
2162 double fy = (double)img->height / (double)newheight;
2165 m.sx = (int)(65536*20*matrix->m00*fx); m.r1 = (int)(65536*20*matrix->m10*fy);
2166 m.r0 = (int)(65536*20*matrix->m01*fx); m.sy = (int)(65536*20*matrix->m11*fy);
2167 m.tx = (int)(matrix->tx*20);
2168 m.ty = (int)(matrix->ty*20);
2171 int myshapeid = getNewID(dev);
2172 i->tag = swf_InsertTag(i->tag,ST_DEFINESHAPE);
2174 swf_ShapeNew(&shape);
2175 int fsid = swf_ShapeAddBitmapFillStyle(shape,&m,bitid,1);
2176 swf_SetU16(i->tag, myshapeid);
2177 SRECT r = gfxline_getSWFbbox(line);
2178 r = swf_ClipRect(i->pagebbox, r);
2179 swf_SetRect(i->tag,&r);
2180 swf_SetShapeStyles(i->tag,shape);
2181 swf_ShapeCountBits(shape,NULL,NULL);
2182 swf_SetShapeBits(i->tag,shape);
2183 swf_ShapeSetAll(i->tag,shape,UNDEFINED_COORD,UNDEFINED_COORD,0,fsid,0);
2184 i->swflastx = i->swflasty = UNDEFINED_COORD;
2185 drawgfxline(dev, line, 1);
2186 swf_ShapeSetEnd(i->tag);
2187 swf_ShapeFree(shape);
2189 msg("<trace> Placing image, shape ID %d, bitmap ID %d", myshapeid, bitid);
2190 i->tag = swf_InsertTag(i->tag,ST_PLACEOBJECT2);
2191 CXFORM cxform2 = gfxcxform_to_cxform(cxform);
2192 swf_ObjectPlace(i->tag,myshapeid,getNewDepth(dev),&i->page_matrix,&cxform2,NULL);
2195 static RGBA col_black = {255,0,0,0};
2197 static void drawoutline(gfxdevice_t*dev, gfxline_t*line)
2199 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
2201 int myshapeid = getNewID(dev);
2202 i->tag = swf_InsertTag(i->tag,ST_DEFINESHAPE3);
2205 swf_ShapeNew(&shape);
2206 int lsid = swf_ShapeAddLineStyle(shape,1,&col_black);
2208 swf_SetU16(i->tag,myshapeid);
2209 SRECT r = gfxline_getSWFbbox(line);
2210 r = swf_ClipRect(i->pagebbox, r);
2211 swf_SetRect(i->tag,&r);
2212 swf_SetShapeStyles(i->tag,shape);
2213 swf_ShapeCountBits(shape,NULL,NULL);
2214 swf_SetShapeBits(i->tag,shape);
2215 swf_ShapeSetAll(i->tag,shape,UNDEFINED_COORD,UNDEFINED_COORD,lsid,0,0);
2216 drawgfxline(dev, line, 1);
2217 swf_ShapeSetEnd(i->tag);
2218 swf_ShapeFree(shape);
2220 i->tag = swf_InsertTag(i->tag,ST_PLACEOBJECT2);
2221 swf_ObjectPlace(i->tag, myshapeid, getNewDepth(dev), 0,0,0);
2224 static void swf_startclip(gfxdevice_t*dev, gfxline_t*line)
2226 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
2231 if(i->clippos >= 127)
2233 msg("<warning> Too many clip levels.");
2237 if(i->config_showclipshapes)
2238 drawoutline(dev, line);
2240 int myshapeid = getNewID(dev);
2241 i->tag = swf_InsertTag(i->tag,ST_DEFINESHAPE3);
2243 memset(&col, 0, sizeof(RGBA));
2246 swf_ShapeNew(&shape);
2247 int fsid = swf_ShapeAddSolidFillStyle(shape,&col);
2249 RGBA markcol = {0,i->mark[0],i->mark[1],i->mark[2]};
2250 swf_ShapeAddSolidFillStyle(shape,&markcol);
2252 swf_SetU16(i->tag,myshapeid);
2253 SRECT r = gfxline_getSWFbbox(line);
2254 r = swf_ClipRect(i->pagebbox, r);
2255 swf_SetRect(i->tag,&r);
2256 swf_SetShapeStyles(i->tag,shape);
2257 swf_ShapeCountBits(shape,NULL,NULL);
2258 swf_SetShapeBits(i->tag,shape);
2259 swf_ShapeSetAll(i->tag,shape,UNDEFINED_COORD,UNDEFINED_COORD,0,fsid,0);
2260 i->swflastx = i->swflasty = UNDEFINED_COORD;
2261 i->shapeisempty = 1;
2262 drawgfxline(dev, line, 1);
2263 if(i->shapeisempty) {
2264 /* an empty clip shape is equivalent to a shape with no area */
2265 int x = line?line->x:0;
2266 int y = line?line->y:0;
2267 moveto(dev, i->tag, x,y);
2268 lineto(dev, i->tag, x,y);
2269 lineto(dev, i->tag, x,y);
2271 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)) {
2272 if(i->config_watermark) {
2273 gfxbbox_t r; r.xmin = r.ymin = 0;r.xmax = i->max_x;r.ymax = i->max_y;
2274 draw_watermark(dev, r, 1);
2277 swf_ShapeSetEnd(i->tag);
2278 swf_ShapeFree(shape);
2280 /* TODO: remember the bbox, and check all shapes against it */
2282 msg("<trace> Placing clip ID %d", myshapeid);
2283 i->tag = swf_InsertTag(i->tag,ST_PLACEOBJECT2);
2284 i->cliptags[i->clippos] = i->tag;
2285 i->clipshapes[i->clippos] = myshapeid;
2286 i->clipdepths[i->clippos] = getNewDepth(dev);
2290 static void swf_endclip(gfxdevice_t*dev)
2292 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
2299 msg("<error> Invalid end of clipping region");
2303 /*swf_ObjectPlaceClip(i->cliptags[i->clippos],i->clipshapes[i->clippos],i->clipdepths[i->clippos],&i->page_matrix,NULL,NULL,
2304 / * clip to depth: * / i->depth <= i->clipdepths[i->clippos]? i->depth : i->depth - 1);
2306 swf_ObjectPlaceClip(i->cliptags[i->clippos],i->clipshapes[i->clippos],i->clipdepths[i->clippos],&i->page_matrix,NULL,NULL,i->depth);
2308 static int gfxline_type(gfxline_t*line)
2314 int haszerosegments=0;
2317 if(line->type == gfx_moveTo) {
2320 } else if(line->type == gfx_lineTo) {
2324 } else if(line->type == gfx_splineTo) {
2326 if(tmpsplines>lines)
2334 if(lines==0 && splines==0) return 0;
2335 else if(lines==1 && splines==0) return 1;
2336 else if(lines==0 && splines==1) return 2;
2337 else if(splines==0) return 3;
2341 static int gfxline_has_dots(gfxline_t*line)
2349 if(line->type == gfx_moveTo) {
2350 /* test the length of the preceding line, and assume it is a dot if
2351 it's length is less than 1.0. But *only* if there's a noticable
2352 gap between the previous line and the next moveTo. (I've come
2353 across a PDF where thousands of "dots" were stringed together,
2355 int last_short_gap = short_gap;
2356 if((fabs(line->x - x) + fabs(line->y - y)) < 1.0) {
2361 if(isline && dist < 1 && !short_gap && !last_short_gap) {
2366 } else if(line->type == gfx_lineTo) {
2367 dist += fabs(line->x - x) + fabs(line->y - y);
2369 } else if(line->type == gfx_splineTo) {
2370 dist += fabs(line->sx - x) + fabs(line->sy - y) +
2371 fabs(line->x - line->sx) + fabs(line->y - line->sy);
2378 if(isline && dist < 1 && !short_gap) {
2384 static int gfxline_fix_short_edges(gfxline_t*line)
2388 if(line->type == gfx_lineTo) {
2389 if(fabs(line->x - x) + fabs(line->y - y) < 0.01) {
2392 } else if(line->type == gfx_splineTo) {
2393 if(fabs(line->sx - x) + fabs(line->sy - y) +
2394 fabs(line->x - line->sx) + fabs(line->y - line->sy) < 0.01) {
2405 static char is_inside_page(gfxdevice_t*dev, gfxcoord_t x, gfxcoord_t y)
2407 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
2408 if(x<i->min_x || x>i->max_x) return 0;
2409 if(y<i->min_y || y>i->max_y) return 0;
2413 gfxline_t* gfxline_move(gfxline_t*line, double x, double y)
2415 gfxline_t*l = line = gfxline_clone(line);
2427 //#define NORMALIZE_POLYGON_POSITIONS
2429 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)
2431 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
2432 if(line_is_empty(line))
2434 int type = gfxline_type(line);
2435 int has_dots = gfxline_has_dots(line);
2436 gfxbbox_t r = gfxline_getbbox(line);
2437 int is_outside_page = !is_inside_page(dev, r.xmin, r.ymin) || !is_inside_page(dev, r.xmax, r.ymax);
2439 /* TODO: * split line into segments, and perform this check for all segments */
2441 if(i->config_disable_polygon_conversion || type>=5 ||
2443 (width <= i->config_caplinewidth
2444 || (cap_style == gfx_capRound && joint_style == gfx_joinRound)
2445 || (cap_style == gfx_capRound && type<=2)))) {} else
2447 /* convert line to polygon */
2448 msg("<trace> draw as polygon, type=%d dots=%d", type, has_dots);
2450 gfxline_fix_short_edges(line);
2451 /* we need to convert the line into a polygon */
2452 gfxpoly_t* poly = gfxpoly_strokeToPoly(line, width, cap_style, joint_style, miterLimit);
2453 gfxline_t*gfxline = gfxpoly_to_gfxline(poly);
2454 dev->fill(dev, gfxline, color);
2455 gfxline_free(gfxline);
2460 msg("<trace> draw as stroke, type=%d dots=%d", type, has_dots);
2463 if(i->config_normalize_polygon_positions) {
2465 double startx = 0, starty = 0;
2466 if(line && line->type == gfx_moveTo) {
2470 line = gfxline_move(line, -startx, -starty);
2471 i->shapeposx = (int)(startx*20);
2472 i->shapeposy = (int)(starty*20);
2475 swfoutput_setstrokecolor(dev, color->r, color->g, color->b, color->a);
2476 swfoutput_setlinewidth(dev, width);
2479 drawgfxline(dev, line, 0);
2481 if(i->config_normalize_polygon_positions) {
2482 free(line); //account for _move
2487 static void swf_fill(gfxdevice_t*dev, gfxline_t*line, gfxcolor_t*color)
2489 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
2490 if(line_is_empty(line))
2494 gfxbbox_t r = gfxline_getbbox(line);
2495 int is_outside_page = !is_inside_page(dev, r.xmin, r.ymin) || !is_inside_page(dev, r.xmax, r.ymax);
2497 //if(i->chardatapos && i->chardata[i->chardatapos-1].color.a) {
2500 if(!i->config_ignoredraworder)
2503 if(i->config_normalize_polygon_positions) {
2505 double startx = 0, starty = 0;
2506 if(line && line->type == gfx_moveTo) {
2510 line = gfxline_move(line, -startx, -starty);
2511 i->shapeposx = (int)(startx*20);
2512 i->shapeposy = (int)(starty*20);
2515 swfoutput_setfillcolor(dev, color->r, color->g, color->b, color->a);
2518 drawgfxline(dev, line, 1);
2520 if(i->currentswfid==2 && r.xmin==0 && r.ymin==0 && r.xmax==i->max_x && r.ymax==i->max_y) {
2521 if(i->config_watermark) {
2522 draw_watermark(dev, r, 1);
2526 msg("<trace> end of swf_fill (shapeid=%d)", i->shapeid);
2528 if(i->config_normalize_polygon_positions) {
2529 free(line); //account for _move
2533 static GRADIENT* gfxgradient_to_GRADIENT(gfxgradient_t*gradient)
2536 gfxgradient_t*g = gradient;
2541 GRADIENT* swfgradient = malloc(sizeof(GRADIENT));
2542 swfgradient->num = num;
2543 swfgradient->rgba = malloc(sizeof(swfgradient->rgba[0])*num);
2544 swfgradient->ratios = malloc(sizeof(swfgradient->ratios[0])*num);
2549 swfgradient->ratios[num] = g->pos*255;
2550 swfgradient->rgba[num] = *(RGBA*)&g->color;
2557 static void swf_fillgradient(gfxdevice_t*dev, gfxline_t*line, gfxgradient_t*gradient, gfxgradienttype_t type, gfxmatrix_t*matrix)
2559 if(line_is_empty(line))
2561 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
2563 if(line_is_empty(line))
2566 GRADIENT* swfgradient = gfxgradient_to_GRADIENT(gradient);
2573 double f = type==gfxgradient_radial?4:4;
2575 m.sx = (int)(matrix->m00*20*f); m.r1 = (int)(matrix->m10*20*f);
2576 m.r0 = (int)(matrix->m01*20*f); m.sy = (int)(matrix->m11*20*f);
2577 m.tx = (int)(matrix->tx*20);
2578 m.ty = (int)(matrix->ty*20);
2581 int myshapeid = getNewID(dev);
2582 i->tag = swf_InsertTag(i->tag, ST_DEFINESHAPE2);
2584 swf_ShapeNew(&shape);
2585 int fsid = swf_ShapeAddGradientFillStyle(shape,&m,swfgradient,type==gfxgradient_radial);
2586 swf_SetU16(i->tag, myshapeid);
2587 SRECT r = gfxline_getSWFbbox(line);
2588 r = swf_ClipRect(i->pagebbox, r);
2589 swf_SetRect(i->tag,&r);
2590 swf_SetShapeStyles(i->tag,shape);
2591 swf_ShapeCountBits(shape,NULL,NULL);
2592 swf_SetShapeBits(i->tag,shape);
2593 swf_ShapeSetAll(i->tag,shape,UNDEFINED_COORD,UNDEFINED_COORD,0,fsid,0);
2594 i->swflastx = i->swflasty = UNDEFINED_COORD;
2595 drawgfxline(dev, line, 1);
2596 swf_ShapeSetEnd(i->tag);
2597 swf_ShapeFree(shape);
2599 int depth = getNewDepth(dev);
2600 msg("<trace> Placing gradient, shape ID %d, depth %d", myshapeid, depth);
2601 i->tag = swf_InsertTag(i->tag,ST_PLACEOBJECT2);
2602 swf_ObjectPlace(i->tag,myshapeid,depth,&i->page_matrix,NULL,NULL);
2604 swf_FreeGradient(swfgradient);free(swfgradient);
2607 static SWFFONT* gfxfont_to_swffont(gfxfont_t*font, const char* id)
2609 SWFFONT*swffont = (SWFFONT*)rfx_calloc(sizeof(SWFFONT));
2611 SRECT bounds = {0,0,0,0};
2613 swffont->version = 2;
2614 swffont->name = (U8*)strdup(id);
2615 swffont->layout = (SWFLAYOUT*)rfx_calloc(sizeof(SWFLAYOUT));
2616 swffont->layout->ascent = 0;
2617 swffont->layout->descent = 0;
2618 swffont->layout->leading = 0;
2619 swffont->layout->bounds = (SRECT*)rfx_calloc(sizeof(SRECT)*font->num_glyphs);
2620 swffont->encoding = FONT_ENCODING_UNICODE;
2621 swffont->numchars = font->num_glyphs;
2622 swffont->maxascii = font->max_unicode;
2623 swffont->ascii2glyph = (int*)rfx_calloc(sizeof(int)*swffont->maxascii);
2624 swffont->glyph2ascii = (U16*)rfx_calloc(sizeof(U16)*swffont->numchars);
2625 swffont->glyph = (SWFGLYPH*)rfx_calloc(sizeof(SWFGLYPH)*swffont->numchars);
2626 swffont->glyphnames = (char**)rfx_calloc(sizeof(char*)*swffont->numchars);
2627 for(t=0;t<font->max_unicode;t++) {
2628 swffont->ascii2glyph[t] = font->unicode2glyph[t];
2630 for(t=0;t<font->num_glyphs;t++) {
2634 swffont->glyph2ascii[t] = font->glyphs[t].unicode;
2635 if(swffont->glyph2ascii[t] == 0xffff || swffont->glyph2ascii[t] == 0x0000) {
2636 /* flash 8 flashtype requires unique unicode IDs for each character.
2637 We use the Unicode private user area to assign characters, hoping that
2638 the font doesn't contain more than 2048 glyphs */
2639 swffont->glyph2ascii[t] = 0xe000 + (t&0x1fff);
2642 if(font->glyphs[t].name) {
2643 swffont->glyphnames[t] = strdup(font->glyphs[t].name);
2645 swffont->glyphnames[t] = 0;
2647 advance = (int)(font->glyphs[t].advance);
2649 swf_Shape01DrawerInit(&draw, 0);
2650 line = font->glyphs[t].line;
2653 c.x = line->sx * GLYPH_SCALE; c.y = line->sy * GLYPH_SCALE;
2654 to.x = line->x * GLYPH_SCALE; to.y = line->y * GLYPH_SCALE;
2655 if(line->type == gfx_moveTo) {
2656 draw.moveTo(&draw, &to);
2657 } else if(line->type == gfx_lineTo) {
2658 draw.lineTo(&draw, &to);
2659 } else if(line->type == gfx_splineTo) {
2660 draw.splineTo(&draw, &c, &to);
2665 swffont->glyph[t].shape = swf_ShapeDrawerToShape(&draw);
2666 swffont->layout->bounds[t] = swf_ShapeDrawerGetBBox(&draw);
2668 int xmax = swffont->layout->bounds[t].xmax / 20;
2669 if(xmax>0 && xmax*2 < advance) {
2670 printf("fix bad advance value: bbox=%d, advance=%d (%f)\n", xmax, advance, font->glyphs[t].advance);
2674 if(advance<32768/20) {
2675 swffont->glyph[t].advance = advance*20;
2677 swffont->glyph[t].advance = 32767;
2680 draw.dealloc(&draw);
2682 swf_ExpandRect2(&bounds, &swffont->layout->bounds[t]);
2686 /* Flash player will use the advance value from the char, and the ascent/descent values
2687 from the layout for text selection.
2688 ascent will extend the char into negative y direction, from the baseline, while descent
2689 will extend in positive y direction, also from the baseline.
2690 The baseline is defined as the y-position zero
2693 swffont->layout->ascent = -bounds.ymin;
2694 if(swffont->layout->ascent < 0)
2695 swffont->layout->ascent = 0;
2696 swffont->layout->descent = bounds.ymax;
2697 if(swffont->layout->descent < 0)
2698 swffont->layout->descent = 0;
2699 swffont->layout->leading = bounds.ymax - bounds.ymin;
2704 static void swf_addfont(gfxdevice_t*dev, gfxfont_t*font)
2706 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
2708 if(i->swffont && i->swffont->name && !strcmp((char*)i->swffont->name,font->id))
2709 return; // the requested font is the current font
2711 fontlist_t*last=0,*l = i->fontlist;
2714 if(!strcmp((char*)l->swffont->name, font->id)) {
2715 return; // we already know this font
2719 l = (fontlist_t*)rfx_calloc(sizeof(fontlist_t));
2720 l->swffont = gfxfont_to_swffont(font, font->id);
2727 swf_FontSetID(l->swffont, getNewID(i->dev));
2729 if(getScreenLogLevel() >= LOGLEVEL_DEBUG) {
2731 // print font information
2732 msg("<debug> Font %s",font->id);
2733 msg("<debug> | ID: %d", l->swffont->id);
2734 msg("<debug> | Version: %d", l->swffont->version);
2735 msg("<debug> | Name: %s", l->swffont->name);
2736 msg("<debug> | Numchars: %d", l->swffont->numchars);
2737 msg("<debug> | Maxascii: %d", l->swffont->maxascii);
2738 msg("<debug> | Style: %d", l->swffont->style);
2739 msg("<debug> | Encoding: %d", l->swffont->encoding);
2740 for(iii=0; iii<l->swffont->numchars;iii++) {
2741 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,
2742 l->swffont->layout->bounds[iii].xmin/20.0,
2743 l->swffont->layout->bounds[iii].ymin/20.0,
2744 l->swffont->layout->bounds[iii].xmax/20.0,
2745 l->swffont->layout->bounds[iii].ymax/20.0
2748 for(t=0;t<l->swffont->maxascii;t++) {
2749 if(l->swffont->ascii2glyph[t] == iii)
2750 msg("<debug> | - maps to %d",t);
2756 static void swf_switchfont(gfxdevice_t*dev, const char*fontid)
2758 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
2760 if(i->swffont && i->swffont->name && !strcmp((char*)i->swffont->name,fontid))
2761 return; // the requested font is the current font
2763 fontlist_t*l = i->fontlist;
2765 if(!strcmp((char*)l->swffont->name, fontid)) {
2766 i->swffont = l->swffont;
2771 msg("<error> Unknown font id: %s", fontid);
2775 static void swf_drawchar(gfxdevice_t*dev, gfxfont_t*font, int glyph, gfxcolor_t*color, gfxmatrix_t*matrix)
2777 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
2779 msg("<error> swf_drawchar called (glyph %d) without font", glyph);
2782 if(!i->swffont || !i->swffont->name || strcmp((char*)i->swffont->name,font->id)) // not equal to current font
2784 /* TODO: remove the need for this (enhance getcharacterbbox so that it can cope
2785 with multiple fonts */
2787 swf_switchfont(dev, font->id); // set the current font
2790 msg("<warning> swf_drawchar: Font is NULL");
2793 if(glyph<0 || glyph>=i->swffont->numchars) {
2794 msg("<warning> No character %d in font %s (%d chars)", glyph, FIXNULL((char*)i->swffont->name), i->swffont->numchars);
2798 setfontscale(dev, matrix->m00, matrix->m01, matrix->m10, matrix->m11, matrix->tx, matrix->ty, 0);
2800 double det = i->fontmatrix.sx/65536.0 * i->fontmatrix.sy/65536.0 -
2801 i->fontmatrix.r0/65536.0 * i->fontmatrix.r1/65536.0;
2802 if(fabs(det) < 0.0005) {
2803 /* x direction equals y direction- the text is invisible */
2804 msg("<verbose> Not drawing invisible character character %d (det=%f, m=[%f %f;%f %f]\n", glyph,
2806 i->fontmatrix.sx/65536.0, i->fontmatrix.r1/65536.0,
2807 i->fontmatrix.r0/65536.0, i->fontmatrix.sy/65536.0);
2811 /*if(i->swffont->glyph[glyph].shape->bitlen <= 16) {
2812 msg("<warning> Glyph %d in current charset (%s, %d characters) is empty",
2813 glyph, FIXNULL((char*)i->swffont->name), i->swffont->numchars);
2817 /* calculate character position with respect to the current font matrix */
2818 double s = 20 * GLYPH_SCALE / det;
2819 double px = matrix->tx - i->fontmatrix.tx/20.0;
2820 double py = matrix->ty - i->fontmatrix.ty/20.0;
2821 int x = (SCOORD)(( px * i->fontmatrix.sy/65536.0 - py * i->fontmatrix.r1/65536.0)*s);
2822 int y = (SCOORD)((- px * i->fontmatrix.r0/65536.0 + py * i->fontmatrix.sx/65536.0)*s);
2823 if(x>32767 || x<-32768 || y>32767 || y<-32768) {
2824 msg("<verbose> Moving character origin to %f %f\n", matrix->tx, matrix->ty);
2826 setfontscale(dev, matrix->m00, matrix->m01, matrix->m10, matrix->m11, matrix->tx, matrix->ty, 1);
2827 /* since we just moved the char origin to the current char's position,
2828 it now has the relative position (0,0) */
2837 msg("<trace> Drawing char %d in font %d at %d,%d in color %02x%02x%02x%02x",
2838 glyph, i->swffont->id, x, y, color->r, color->g, color->b, color->a);
2840 putcharacter(dev, i->swffont->id, glyph, x, y, i->current_font_size, *(RGBA*)color);
2841 swf_FontUseGlyph(i->swffont, glyph);