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"
48 #define CHARDATAMAX 8192
52 typedef struct _chardata {
54 int fontid; /* TODO: use a SWFFONT instead */
61 typedef struct _fontlist
64 struct _fontlist*next;
67 typedef long int twip;
69 typedef struct _swfmatrix {
70 double m11,m12,m21,m22,m31,m32;
73 typedef struct _swfoutput_internal
75 gfxdevice_t*dev; // the gfxdevice object where this internal struct resides
77 double config_dumpfonts;
78 double config_ppmsubpixels;
79 double config_jpegsubpixels;
80 int config_simpleviewer;
81 int config_opennewwindow;
82 int config_ignoredraworder;
83 int config_drawonlyshapes;
84 int config_frameresets;
85 int config_linknameurl;
86 int config_jpegquality;
87 int config_storeallcharacters;
88 int config_enablezlib;
89 int config_insertstoptag;
91 int config_flashversion;
92 int config_reordertags;
93 int config_showclipshapes;
94 int config_splinemaxerror;
95 int config_fontsplinemaxerror;
96 int config_filloverlap;
99 int config_disable_polygon_conversion;
100 int config_normalize_polygon_positions;
101 RGBA config_linkcolor;
102 float config_minlinewidth;
103 double config_caplinewidth;
104 char* config_linktarget;
105 char*config_internallinkfunction;
106 char*config_externallinkfunction;
108 double config_framerate;
112 fontlist_t* fontlist;
151 int pic_height[1024];
158 char fillstylechanged;
160 int jpeg; //next image type
167 chardata_t chardata[CHARDATAMAX];
174 int current_font_size;
176 double lastfontm11,lastfontm12,lastfontm21,lastfontm22;
187 } swfoutput_internal;
189 static void swf_fillbitmap(gfxdevice_t*driver, gfxline_t*line, gfximage_t*img, gfxmatrix_t*move, gfxcxform_t*cxform);
190 static int swf_setparameter(gfxdevice_t*driver, const char*key, const char*value);
191 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);
192 static void swf_startclip(gfxdevice_t*dev, gfxline_t*line);
193 static void swf_endclip(gfxdevice_t*dev);
194 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);
195 static void swf_fill(gfxdevice_t*dev, gfxline_t*line, gfxcolor_t*color);
196 static void swf_fillbitmap(gfxdevice_t*dev, gfxline_t*line, gfximage_t*img, gfxmatrix_t*matrix, gfxcxform_t*cxform);
197 static void swf_fillgradient(gfxdevice_t*dev, gfxline_t*line, gfxgradient_t*gradient, gfxgradienttype_t type, gfxmatrix_t*matrix);
198 static void swf_drawchar(gfxdevice_t*dev, gfxfont_t*font, int glyph, gfxcolor_t*color, gfxmatrix_t*matrix);
199 static void swf_addfont(gfxdevice_t*dev, gfxfont_t*font);
200 static void swf_drawlink(gfxdevice_t*dev, gfxline_t*line, const char*action);
201 static void swf_startframe(gfxdevice_t*dev, int width, int height);
202 static void swf_endframe(gfxdevice_t*dev);
203 static gfxresult_t* swf_finish(gfxdevice_t*driver);
205 static swfoutput_internal* init_internal_struct()
207 swfoutput_internal*i = (swfoutput_internal*)malloc(sizeof(swfoutput_internal));
208 memset(i, 0, sizeof(swfoutput_internal));
232 i->fillstylechanged = 0;
239 i->config_dumpfonts=0;
240 i->config_ppmsubpixels=0;
241 i->config_jpegsubpixels=0;
242 i->config_opennewwindow=1;
243 i->config_ignoredraworder=0;
244 i->config_drawonlyshapes=0;
245 i->config_jpegquality=85;
246 i->config_storeallcharacters=0;
247 i->config_enablezlib=0;
248 i->config_insertstoptag=0;
249 i->config_flashversion=6;
250 i->config_framerate=0.25;
251 i->config_splinemaxerror=1;
252 i->config_fontsplinemaxerror=1;
253 i->config_filloverlap=0;
255 i->config_bboxvars=0;
256 i->config_showclipshapes=0;
257 i->config_minlinewidth=0.05;
258 i->config_caplinewidth=1;
259 i->config_linktarget=0;
260 i->config_internallinkfunction=0;
261 i->config_externallinkfunction=0;
262 i->config_reordertags=1;
263 i->config_linknameurl=1;
265 i->config_linkcolor.r = i->config_linkcolor.g = i->config_linkcolor.b = 255;
266 i->config_linkcolor.a = 0x40;
271 static int id_error = 0;
273 static U16 getNewID(gfxdevice_t* dev)
275 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
276 if(i->currentswfid == 65535) {
278 msg("<error> ID Table overflow");
279 msg("<error> This file is too complex to render- SWF only supports 65536 shapes at once");
285 return ++i->currentswfid;
287 static U16 getNewDepth(gfxdevice_t* dev)
289 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
290 if(i->depth == 65520) {
292 msg("<error> Depth Table overflow");
293 msg("<error> This file is too complex to render- SWF only supports 65536 shapes at once");
302 static void startshape(gfxdevice_t* dev);
303 static void starttext(gfxdevice_t* dev);
304 static void endshape(gfxdevice_t* dev);
305 static void endtext(gfxdevice_t* dev);
307 typedef struct _plotxy
312 // write a move-to command into the swf
313 static int movetoxy(gfxdevice_t*dev, TAG*tag, plotxy_t p0)
315 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
316 int rx = (int)(p0.x*20);
317 int ry = (int)(p0.y*20);
318 if(rx!=i->swflastx || ry!=i->swflasty || i->fillstylechanged) {
319 swf_ShapeSetMove (tag, i->shape, rx,ry);
320 i->fillstylechanged = 0;
327 static int moveto(gfxdevice_t*dev, TAG*tag, double x, double y)
329 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
333 return movetoxy(dev, tag, p);
335 static void addPointToBBox(gfxdevice_t*dev, int px, int py)
337 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
343 swf_ExpandRect(&i->bboxrect, p);
345 swf_ExpandRect3(&i->bboxrect, p, i->linewidth*3/2);
349 /*static void plot(gfxdevice_t*dev, int x, int y, TAG*tag)
351 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
352 int width = i->linewidth/4;
356 //swf_ShapeSetLine(tag, i->shape,-width,-width);
357 //swf_ShapeSetLine(tag, i->shape,width*2,0);
358 //swf_ShapeSetLine(tag, i->shape,0,width*2);
359 //swf_ShapeSetLine(tag, i->shape,-width*2,0);
360 //swf_ShapeSetLine(tag, i->shape,0,-width*2);
361 //swf_ShapeSetLine(tag, i->shape,width,width);
364 swf_ShapeSetLine(tag, i->shape,-width,0);
365 swf_ShapeSetLine(tag, i->shape,width,-width);
366 swf_ShapeSetLine(tag, i->shape,width,width);
367 swf_ShapeSetLine(tag, i->shape,-width,width);
368 swf_ShapeSetLine(tag, i->shape,-width,-width);
369 swf_ShapeSetLine(tag, i->shape,width,0);
371 addPointToBBox(dev, x-width ,y-width);
372 addPointToBBox(dev, x+width ,y+width);
375 // write a line-to command into the swf
376 static void linetoxy(gfxdevice_t*dev, TAG*tag, plotxy_t p0)
378 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
379 int px = (int)(p0.x*20);
380 int py = (int)(p0.y*20);
381 int rx = (px-i->swflastx);
382 int ry = (py-i->swflasty);
384 swf_ShapeSetLine (tag, i->shape, rx,ry);
385 addPointToBBox(dev, i->swflastx,i->swflasty);
386 addPointToBBox(dev, px,py);
387 }/* else if(!i->fill) {
388 // treat lines of length 0 as plots, making them
389 // at least 1 twip wide so Flash will display them
390 plot(dev, i->swflastx, i->swflasty, tag);
397 static void lineto(gfxdevice_t*dev, TAG*tag, double x, double y)
402 linetoxy(dev,tag, p);
405 // write a spline-to command into the swf
406 static void splineto(gfxdevice_t*dev, TAG*tag, plotxy_t control,plotxy_t end)
408 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
409 int lastlastx = i->swflastx;
410 int lastlasty = i->swflasty;
412 int cx = ((int)(control.x*20)-i->swflastx);
413 int cy = ((int)(control.y*20)-i->swflasty);
416 int ex = ((int)(end.x*20)-i->swflastx);
417 int ey = ((int)(end.y*20)-i->swflasty);
421 if((cx || cy) && (ex || ey)) {
422 swf_ShapeSetCurve(tag, i->shape, cx,cy,ex,ey);
423 addPointToBBox(dev, lastlastx ,lastlasty );
424 addPointToBBox(dev, lastlastx+cx,lastlasty+cy);
425 addPointToBBox(dev, lastlastx+cx+ex,lastlasty+cy+ey);
426 } else if(cx || cy || ex || ey) {
427 swf_ShapeSetLine(tag, i->shape, cx+ex,cy+ey);
428 addPointToBBox(dev, lastlastx ,lastlasty );
429 addPointToBBox(dev, lastlastx+cx,lastlasty+cy);
430 addPointToBBox(dev, lastlastx+cx+ex,lastlasty+cy+ey);
436 /* write a line, given two points and the transformation
438 /*static void line(gfxdevice_t*dev, TAG*tag, plotxy_t p0, plotxy_t p1)
440 moveto(dev, tag, p0);
441 lineto(dev, tag, p1);
444 void resetdrawer(gfxdevice_t*dev)
446 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
451 static void stopFill(gfxdevice_t*dev)
453 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
454 if(i->lastwasfill!=0)
456 swf_ShapeSetStyle(i->tag,i->shape,i->linestyleid,0x8000,0);
457 i->fillstylechanged = 1;
461 static void startFill(gfxdevice_t*dev)
463 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
464 if(i->lastwasfill!=1)
466 swf_ShapeSetStyle(i->tag,i->shape,0x8000,i->fillstyleid,0);
467 i->fillstylechanged = 1;
472 static inline int colorcompare(gfxdevice_t*dev, RGBA*a,RGBA*b)
484 static SRECT getcharacterbbox(gfxdevice_t*dev, SWFFONT*font, MATRIX* m)
486 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
489 memset(&r, 0, sizeof(r));
492 if(debug) printf("\n");
493 for(t=0;t<i->chardatapos;t++)
495 if(i->chardata[t].fontid != font->id) {
496 msg("<error> Internal error: fontid %d != fontid %d", i->chardata[t].fontid, font->id);
499 SRECT b = font->layout->bounds[i->chardata[t].charid];
500 b.xmin *= i->chardata[t].size;
501 b.ymin *= i->chardata[t].size;
502 b.xmax *= i->chardata[t].size;
503 b.ymax *= i->chardata[t].size;
505 /* divide by 1024, rounding xmax/ymax up */
506 b.xmax += 1023; b.ymax += 1023; b.xmin /= 1024; b.ymin /= 1024; b.xmax /= 1024; b.ymax /= 1024;
508 b.xmin += i->chardata[t].x;
509 b.ymin += i->chardata[t].y;
510 b.xmax += i->chardata[t].x;
511 b.ymax += i->chardata[t].y;
513 /* until we solve the INTERNAL_SCALING problem (see below)
514 make sure the bounding box is big enough */
520 b = swf_TurnRect(b, m);
522 if(debug) printf("(%f,%f,%f,%f) -> (%f,%f,%f,%f) [font %d/%d, char %d]\n",
523 font->layout->bounds[i->chardata[t].charid].xmin/20.0,
524 font->layout->bounds[i->chardata[t].charid].ymin/20.0,
525 font->layout->bounds[i->chardata[t].charid].xmax/20.0,
526 font->layout->bounds[i->chardata[t].charid].ymax/20.0,
531 i->chardata[t].fontid,
533 i->chardata[t].charid
535 swf_ExpandRect2(&r, &b);
537 if(debug) printf("-----> (%f,%f,%f,%f)\n",
545 static void putcharacters(gfxdevice_t*dev, TAG*tag)
547 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
551 color.r = i->chardata[0].color.r^255;
560 int charadvance[128];
563 int glyphbits=1; //TODO: can this be zero?
566 if(tag->id != ST_DEFINETEXT &&
567 tag->id != ST_DEFINETEXT2) {
568 msg("<error> internal error: putcharacters needs an text tag, not %d\n",tag->id);
571 if(!i->chardatapos) {
572 msg("<warning> putcharacters called with zero characters");
575 for(pass = 0; pass < 2; pass++)
585 advancebits++; // add sign bit
586 swf_SetU8(tag, glyphbits);
587 swf_SetU8(tag, advancebits);
590 for(t=0;t<=i->chardatapos;t++)
592 if(lastfontid != i->chardata[t].fontid ||
593 lastx!=i->chardata[t].x ||
594 lasty!=i->chardata[t].y ||
595 !colorcompare(dev,&color, &i->chardata[t].color) ||
597 lastsize != i->chardata[t].size ||
600 if(charstorepos && pass==0)
603 for(s=0;s<charstorepos;s++)
605 while(charids[s]>=(1<<glyphbits))
607 while(charadvance[s]>=(1<<advancebits))
611 if(charstorepos && pass==1)
613 tag->writeBit = 0; // Q&D
614 swf_SetBits(tag, 0, 1); // GLYPH Record
615 swf_SetBits(tag, charstorepos, 7); // number of glyphs
617 for(s=0;s<charstorepos;s++)
619 swf_SetBits(tag, charids[s], glyphbits);
620 swf_SetBits(tag, charadvance[s], advancebits);
625 if(pass == 1 && t<i->chardatapos)
631 if(lastx != i->chardata[t].x ||
632 lasty != i->chardata[t].y)
634 newx = i->chardata[t].x;
635 newy = i->chardata[t].y;
641 if(!colorcompare(dev,&color, &i->chardata[t].color))
643 color = i->chardata[t].color;
646 font.id = i->chardata[t].fontid;
647 if(lastfontid != i->chardata[t].fontid || lastsize != i->chardata[t].size)
650 tag->writeBit = 0; // Q&D
651 swf_TextSetInfoRecord(tag, newfont, i->chardata[t].size, newcolor, newx,newy);
654 lastfontid = i->chardata[t].fontid;
655 lastx = i->chardata[t].x;
656 lasty = i->chardata[t].y;
657 lastsize = i->chardata[t].size;
660 if(t==i->chardatapos)
664 int nextt = t==i->chardatapos-1?t:t+1;
665 int rel = i->chardata[nextt].x-i->chardata[t].x;
666 if(rel>=0 && (rel<(1<<(advancebits-1)) || pass==0)) {
668 lastx=i->chardata[nextt].x;
672 lastx=i->chardata[t].x;
674 charids[charstorepos] = i->chardata[t].charid;
675 charadvance[charstorepos] = advance;
682 static void putcharacter(gfxdevice_t*dev, int fontid, int charid, int x,int y, int size, RGBA color)
684 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
685 if(i->chardatapos == CHARDATAMAX)
687 msg("<warning> Character buffer too small. SWF will be slightly bigger");
691 i->chardata[i->chardatapos].fontid = fontid;
692 i->chardata[i->chardatapos].charid = charid;
693 i->chardata[i->chardatapos].x = x;
694 i->chardata[i->chardatapos].y = y;
695 i->chardata[i->chardatapos].color = color;
696 i->chardata[i->chardatapos].size = size;
700 /* Notice: we can only put chars in the range -1639,1638 (-32768/20,32768/20).
701 So if we set this value to high, the char coordinates will overflow.
702 If we set it to low, however, the char positions will be inaccurate */
703 #define GLYPH_SCALE 1
705 static void endtext(gfxdevice_t*dev)
707 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
711 i->tag = swf_InsertTag(i->tag,ST_DEFINETEXT2);
712 swf_SetU16(i->tag, i->textid);
715 r = getcharacterbbox(dev, i->swffont, &i->fontmatrix);
716 r = swf_ClipRect(i->pagebbox, r);
717 swf_SetRect(i->tag,&r);
719 swf_SetMatrix(i->tag,&i->fontmatrix);
721 msg("<trace> Placing text (%d characters) as ID %d", i->chardatapos, i->textid);
723 putcharacters(dev, i->tag);
726 if(i->swf->fileVersion >= 8) {
727 i->tag = swf_InsertTag(i->tag, ST_CSMTEXTSETTINGS);
728 swf_SetU16(i->tag, i->textid);
730 //swf_SetU8(i->tag, /*subpixel grid*/(2<<3)|/*flashtype*/0x40);
731 swf_SetU8(i->tag, /*grid*/(1<<3)|/*flashtype*/0x40);
732 //swf_SetU8(i->tag, /*no grid*/(0<<3)|/*flashtype*/0x40);
734 swf_SetU32(i->tag, 0);//thickness
735 swf_SetU32(i->tag, 0);//sharpness
736 swf_SetU8(i->tag, 0);//reserved
738 i->tag = swf_InsertTag(i->tag,ST_PLACEOBJECT2);
740 swf_ObjectPlace(i->tag,i->textid,getNewDepth(dev),&i->page_matrix,NULL,NULL);
744 /* set's the matrix which is to be applied to characters drawn by swfoutput_drawchar() */
745 static void setfontscale(gfxdevice_t*dev,double m11,double m12, double m21,double m22,double x, double y, char force)
751 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
752 if(i->lastfontm11 == m11 &&
753 i->lastfontm12 == m12 &&
754 i->lastfontm21 == m21 &&
755 i->lastfontm22 == m22 && !force)
760 i->lastfontm11 = m11;
761 i->lastfontm12 = m12;
762 i->lastfontm21 = m21;
763 i->lastfontm22 = m22;
765 double xsize = sqrt(m11*m11 + m12*m12);
766 double ysize = sqrt(m21*m21 + m22*m22);
767 i->current_font_size = (xsize>ysize?xsize:ysize)*1;
768 if(i->current_font_size < 1)
769 i->current_font_size = 1;
770 double ifs = 1.0 / (i->current_font_size*GLYPH_SCALE);
773 m.sx = (S32)((m11*ifs)*65536); m.r1 = (S32)((m21*ifs)*65536);
774 m.r0 = (S32)((m12*ifs)*65536); m.sy = (S32)((m22*ifs)*65536);
775 /* this is the position of the first char to set a new fontmatrix-
776 we hope that it's close enough to all other characters using the
777 font, so we use its position as origin for the matrix */
783 static int watermark2_width=47;
784 static int watermark2_height=11;
785 static int watermark2[47] = {95,1989,71,0,2015,337,1678,0,2015,5,1921,320,1938,25,2006,1024,
786 1042,21,13,960,1039,976,8,2000,1359,1088,31,1989,321,1728,0,1152,
787 1344,832,0,1984,0,896,1088,1088,896,0,1984,128,256,512,1984};
789 static void draw_watermark(gfxdevice_t*dev, gfxbbox_t r, char drawall)
791 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
792 double wx = r.xmax / 5.0;
793 double tx = r.xmax*4.0 / 5.0;
794 double ty = r.ymax-wx*watermark2_height/watermark2_width;
795 double sx = (r.xmax - tx) / watermark2_width;
796 double sy = (r.ymax - ty) / watermark2_height;
799 if(ty > 0 && px > 1.0 && py > 1.0) {
801 for(y=0;y<watermark2_height;y++)
802 for(x=0;x<watermark2_width;x++) {
803 if(((watermark2[x]>>y)&1)) {
804 if(!drawall && rand()%5)
806 unsigned int b = rand();
807 moveto(dev, i->tag, x*sx+tx+((b>>1)&1)/20.0, y*sy+ty+((b>>3)&1)/20.0);
808 lineto(dev, i->tag, x*sx+px+tx+((b>>2)&1)/20.0, y*sy+ty+((b>>3)&1)/20.0);
809 lineto(dev, i->tag, x*sx+px+tx+((b>>2)&1)/20.0, y*sy+py+ty+((b>>4)&1)/20.0);
810 lineto(dev, i->tag, x*sx+tx+((b>>1)&1)/20.0, y*sy+py+ty+((b>>4)&1)/20.0);
811 lineto(dev, i->tag, x*sx+tx+((b>>1)&1)/20.0, y*sy+ty+((b>>3)&1)/20.0);
817 static void swfoutput_setfillcolor(gfxdevice_t* dev, U8 r, U8 g, U8 b, U8 a)
819 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
820 if(i->fillrgb.r == r &&
823 i->fillrgb.a == a) return;
832 static void insert_watermark(gfxdevice_t*dev, char drawall)
834 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
835 if(!drawall && i->watermarks>20)
841 swfoutput_setfillcolor(dev, 0,0,255,192);
843 swfoutput_setfillcolor(dev, rand(),rand(),rand(),(rand()&127)+128);
848 gfxbbox_t r; r.xmin = r.ymin = 0;
851 draw_watermark(dev, r, drawall);
857 static void endpage(gfxdevice_t*dev)
859 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
870 if(i->config_watermark) {
871 insert_watermark(dev, 1);
877 static void addViewer(gfxdevice_t* dev)
879 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
882 RGBA button_colors[3]= {{0xbf,0x00,0x00,0x80},{0xbf,0x20,0x20,0xc0}, {0xbf,0xc0,0xc0,0xff}};
884 int button_sizex = 20;
885 int button_sizey = 20;
887 RGBA black = {255,0,0,0};
889 i->tag = swf_InsertTag(i->tag,ST_DEFINESHAPE3);
891 int ls1 = swf_ShapeAddLineStyle(s,40,&black);
892 int fs1 = swf_ShapeAddSolidFillStyle(s,&button_colors[t/2]);
893 int shapeid = ids[t] = getNewID(dev);
894 swf_SetU16(i->tag,shapeid);
896 r.xmin = -20*button_sizex;
897 r.xmax = 20*button_sizex;
899 r.ymax = 40*button_sizey;
900 swf_SetRect(i->tag,&r); // set shape bounds
901 swf_SetShapeHeader(i->tag,s); // write all styles to tag
902 swf_ShapeSetAll(i->tag,s,0*button_sizex,0,ls1,fs1,0);
903 swf_ShapeSetLine(i->tag,s,(1-(t&1)*2)*20*button_sizex,20*button_sizey);
904 swf_ShapeSetLine(i->tag,s,-(1-(t&1)*2)*20*button_sizex,20*button_sizey);
905 swf_ShapeSetLine(i->tag,s,0,-40*button_sizey);
906 swf_ShapeSetEnd(i->tag); // finish drawing
907 swf_ShapeFree(s); // clean shape structure (which isn't needed anymore after writing the tag)
909 ActionTAG*a1=0,*a2=0,*a3=0;
910 a1 = action_NextFrame(a1);
911 a1 = action_Stop(a1);
914 a2 = action_PreviousFrame(a2);
915 a2 = action_Stop(a2);
918 a3 = action_Stop(a3);
921 i->tag = swf_InsertTag(i->tag, ST_DOACTION);
922 swf_ActionSet(i->tag,a3);
924 i->tag = swf_InsertTag(i->tag,ST_DEFINEBUTTON);
925 int buttonid1 = getNewID(dev);
926 swf_SetU16(i->tag, buttonid1);
927 swf_ButtonSetRecord(i->tag,BS_UP|BS_HIT,ids[0],1,NULL,NULL);
928 swf_ButtonSetRecord(i->tag,BS_OVER,ids[2],1,NULL,NULL);
929 swf_ButtonSetRecord(i->tag,BS_DOWN,ids[4],1,NULL,NULL);
930 swf_SetU8(i->tag,0); // end of button records
931 swf_ActionSet(i->tag,a1);
933 i->tag = swf_InsertTag(i->tag,ST_DEFINEBUTTON);
934 int buttonid2 = getNewID(dev);
935 swf_SetU16(i->tag, buttonid2);
936 swf_ButtonSetRecord(i->tag,BS_UP|BS_HIT,ids[1],1,NULL,NULL);
937 swf_ButtonSetRecord(i->tag,BS_OVER,ids[3],1,NULL,NULL);
938 swf_ButtonSetRecord(i->tag,BS_DOWN,ids[5],1,NULL,NULL);
939 swf_SetU8(i->tag,0); // end of button records
940 swf_ActionSet(i->tag,a2);
942 i->tag = swf_InsertTag(i->tag,ST_PLACEOBJECT2);
944 swf_GetMatrix(0, &m);
945 m.tx = button_sizex*20+200;
946 swf_ObjectPlace(i->tag, buttonid2, 65534,&m,0,0);
947 i->tag = swf_InsertTag(i->tag,ST_PLACEOBJECT2);
948 m.tx = button_sizex*20+200+200;
949 swf_ObjectPlace(i->tag, buttonid1, 65535,&m,0,0);
953 void swf_startframe(gfxdevice_t*dev, int width, int height)
955 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
957 if(i->config_protect) {
958 i->tag = swf_InsertTag(i->tag, ST_PROTECT);
959 i->config_protect = 0;
961 if(i->config_simpleviewer) {
966 if(!i->firstpage && !i->pagefinished)
969 msg("<verbose> Starting new SWF page of size %dx%d", width, height);
971 swf_GetMatrix(0, &i->page_matrix);
972 i->page_matrix.tx = 0;
973 i->page_matrix.ty = 0;
980 /* create a bbox structure with the page size. This is used
981 for clipping shape and text bounding boxes. As we don't want to
982 generate bounding boxes which extend beyond the movie size (in
983 order to not confuse Flash), we clip everything against i->pagebbox */
984 i->pagebbox.xmin = 0;
985 i->pagebbox.ymin = 0;
986 i->pagebbox.xmax = width*20;
987 i->pagebbox.ymax = height*20;
989 /* increase SWF's bounding box */
990 swf_ExpandRect2(&i->swf->movieSize, &i->pagebbox);
992 i->lastframeno = i->frameno;
997 void swf_endframe(gfxdevice_t*dev)
999 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1001 if(!i->pagefinished)
1004 if( (i->swf->fileVersion <= 8) && (i->config_insertstoptag) ) {
1006 atag = action_Stop(atag);
1007 atag = action_End(atag);
1008 i->tag = swf_InsertTag(i->tag,ST_DOACTION);
1009 swf_ActionSet(i->tag,atag);
1011 i->tag = swf_InsertTag(i->tag,ST_SHOWFRAME);
1014 for(i->depth;i->depth>i->startdepth;i->depth--) {
1015 i->tag = swf_InsertTag(i->tag,ST_REMOVEOBJECT2);
1016 swf_SetU16(i->tag,i->depth);
1018 i->depth = i->startdepth;
1020 if(i->config_frameresets) {
1021 for(i->currentswfid;i->currentswfid>i->startids;i->currentswfid--) {
1022 i->tag = swf_InsertTag(i->tag,ST_FREECHARACTER);
1023 swf_SetU16(i->tag,i->currentswfid);
1025 i->currentswfid = i->startids;
1029 static void setBackground(gfxdevice_t*dev, int x1, int y1, int x2, int y2)
1031 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1033 rgb.a = rgb.r = rgb.g = rgb.b = 0xff;
1037 int shapeid = getNewID(dev);
1042 i->tag = swf_InsertTag(i->tag, ST_DEFINESHAPE);
1044 fs1 = swf_ShapeAddSolidFillStyle(s, &rgb);
1045 swf_SetU16(i->tag,shapeid);
1046 swf_SetRect(i->tag,&r);
1047 swf_SetShapeHeader(i->tag,s);
1048 swf_ShapeSetAll(i->tag,s,x1,y1,ls1,fs1,0);
1049 swf_ShapeSetLine(i->tag,s,(x2-x1),0);
1050 swf_ShapeSetLine(i->tag,s,0,(y2-y1));
1051 swf_ShapeSetLine(i->tag,s,(x1-x2),0);
1052 swf_ShapeSetLine(i->tag,s,0,(y1-y2));
1053 swf_ShapeSetEnd(i->tag);
1055 i->tag = swf_InsertTag(i->tag, ST_PLACEOBJECT2);
1056 swf_ObjectPlace(i->tag,shapeid,getNewDepth(dev),0,0,0);
1057 i->tag = swf_InsertTag(i->tag, ST_PLACEOBJECT2);
1058 swf_ObjectPlaceClip(i->tag,shapeid,getNewDepth(dev),0,0,0,65535);
1061 /* initialize the swf writer */
1062 void gfxdevice_swf_init(gfxdevice_t* dev)
1064 memset(dev, 0, sizeof(gfxdevice_t));
1068 dev->internal = init_internal_struct(); // set config to default values
1070 dev->startpage = swf_startframe;
1071 dev->endpage = swf_endframe;
1072 dev->finish = swf_finish;
1073 dev->fillbitmap = swf_fillbitmap;
1074 dev->setparameter = swf_setparameter;
1075 dev->stroke = swf_stroke;
1076 dev->startclip = swf_startclip;
1077 dev->endclip = swf_endclip;
1078 dev->fill = swf_fill;
1079 dev->fillbitmap = swf_fillbitmap;
1080 dev->fillgradient = swf_fillgradient;
1081 dev->addfont = swf_addfont;
1082 dev->drawchar = swf_drawchar;
1083 dev->drawlink = swf_drawlink;
1085 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1088 msg("<verbose> initializing swf output\n", i->max_x,i->max_y);
1092 i->swf = (SWF*)rfx_calloc(sizeof(SWF));
1093 i->swf->fileVersion = 0;
1094 i->swf->frameRate = 0x80;
1095 i->swf->movieSize.xmin = 0;
1096 i->swf->movieSize.ymin = 0;
1097 i->swf->movieSize.xmax = 0;
1098 i->swf->movieSize.ymax = 0;
1100 i->swf->firstTag = swf_InsertTag(NULL,ST_SETBACKGROUNDCOLOR);
1101 i->tag = i->swf->firstTag;
1103 rgb.a = rgb.r = rgb.g = rgb.b = 0xff;
1105 swf_SetRGB(i->tag,&rgb);
1107 i->startdepth = i->depth = 0;
1108 i->startids = i->currentswfid = 0;
1111 static void startshape(gfxdevice_t*dev)
1113 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1118 //if(i->chardatapos && i->chardata[i->chardatapos-1].color.a)
1121 i->tag = swf_InsertTag(i->tag,ST_DEFINESHAPE3);
1123 swf_ShapeNew(&i->shape);
1124 i->linestyleid = swf_ShapeAddLineStyle(i->shape,i->linewidth,&i->strokergb);
1125 i->fillstyleid = swf_ShapeAddSolidFillStyle(i->shape,&i->fillrgb);
1127 RGBA markcol = {0,i->mark[0],i->mark[1],i->mark[2]};
1128 swf_ShapeAddSolidFillStyle(i->shape,&markcol);
1131 i->shapeid = getNewID(dev);
1133 msg("<debug> Using shape id %d", i->shapeid);
1135 swf_SetU16(i->tag,i->shapeid); // ID
1137 i->bboxrectpos = i->tag->len;
1139 swf_SetRect(i->tag,&i->pagebbox);
1141 memset(&i->bboxrect, 0, sizeof(i->bboxrect));
1143 swf_SetShapeStyles(i->tag,i->shape);
1144 swf_ShapeCountBits(i->shape,NULL,NULL);
1145 swf_SetShapeBits(i->tag,i->shape);
1147 /* TODO: do we really need this? */
1148 //swf_ShapeSetAll(i->tag,i->shape,/*x*/0,/*y*/0,i->linestyleid,0,0);
1149 //swf_ShapeSetAll(i->tag,i->shape,/*x*/UNDEFINED_COORD,/*y*/UNDEFINED_COORD,i->linestyleid,0,0);
1150 i->swflastx=i->swflasty=UNDEFINED_COORD;
1151 i->lastwasfill = -1;
1152 i->shapeisempty = 1;
1155 static void starttext(gfxdevice_t*dev)
1157 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1161 if(i->config_watermark) {
1162 insert_watermark(dev, 0);
1164 i->textid = getNewID(dev);
1165 i->swflastx=i->swflasty=0;
1169 /* TODO: move to ../lib/rfxswf */
1170 void changeRect(gfxdevice_t*dev, TAG*tag, int pos, SRECT*newrect)
1172 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1173 /* determine length of old rect */
1177 swf_GetRect(tag, &old);
1178 swf_ResetReadBits(tag);
1179 int pos_end = tag->pos;
1181 int len = tag->len - pos_end;
1182 U8*data = (U8*)malloc(len);
1183 memcpy(data, &tag->data[pos_end], len);
1186 swf_SetRect(tag, newrect);
1187 swf_SetBlock(tag, data, len);
1189 tag->pos = tag->readBit = 0;
1192 void cancelshape(gfxdevice_t*dev)
1194 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1195 /* delete old shape tag */
1197 i->tag = i->tag->prev;
1198 swf_DeleteTag(todel);
1199 if(i->shape) {swf_ShapeFree(i->shape);i->shape=0;}
1201 i->bboxrectpos = -1;
1203 // i->currentswfid--; // doesn't work, for some reason
1206 void fixAreas(gfxdevice_t*dev)
1208 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1209 if(!i->shapeisempty && i->fill &&
1210 (i->bboxrect.xmin == i->bboxrect.xmax ||
1211 i->bboxrect.ymin == i->bboxrect.ymax) &&
1212 i->config_minlinewidth >= 0.001
1214 msg("<debug> Shape has size 0: width=%.2f height=%.2f",
1215 (i->bboxrect.xmax-i->bboxrect.xmin)/20.0,
1216 (i->bboxrect.ymax-i->bboxrect.ymin)/20.0
1219 SRECT r = i->bboxrect;
1221 if(r.xmin == r.xmax && r.ymin == r.ymax) {
1222 /* this thing comes down to a single dot- nothing to fix here */
1228 RGBA save_col = i->strokergb;
1229 int save_width = i->linewidth;
1231 i->strokergb = i->fillrgb;
1232 i->linewidth = (int)(i->config_minlinewidth*20);
1233 if(i->linewidth==0) i->linewidth = 1;
1238 moveto(dev, i->tag, r.xmin/20.0,r.ymin/20.0);
1239 lineto(dev, i->tag, r.xmax/20.0,r.ymax/20.0);
1241 i->strokergb = save_col;
1242 i->linewidth = save_width;
1247 static void endshape_noput(gfxdevice_t*dev)
1249 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1252 //changeRect(dev, i->tag, i->bboxrectpos, &i->bboxrect);
1255 swf_ShapeFree(i->shape);
1263 static void endshape(gfxdevice_t*dev)
1265 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1271 if(i->shapeisempty ||
1273 (i->bboxrect.xmin == i->bboxrect.xmax &&
1274 i->bboxrect.ymin == i->bboxrect.ymax))
1276 // delete the shape again, we didn't do anything
1277 msg("<debug> cancelling shape: bbox is (%f,%f,%f,%f)",
1278 i->bboxrect.xmin /20.0,
1279 i->bboxrect.ymin /20.0,
1280 i->bboxrect.xmax /20.0,
1281 i->bboxrect.ymax /20.0
1287 swf_ShapeSetEnd(i->tag);
1289 SRECT r = swf_ClipRect(i->pagebbox, i->bboxrect);
1290 changeRect(dev, i->tag, i->bboxrectpos, &r);
1292 msg("<trace> Placing shape ID %d", i->shapeid);
1294 i->tag = swf_InsertTag(i->tag,ST_PLACEOBJECT2);
1295 MATRIX m = i->page_matrix;
1296 m.tx += i->shapeposx;
1297 m.ty += i->shapeposy;
1298 swf_ObjectPlace(i->tag,i->shapeid,getNewDepth(dev),&m,NULL,NULL);
1300 if(i->config_animate) {
1301 i->tag = swf_InsertTag(i->tag,ST_SHOWFRAME);
1304 swf_ShapeFree(i->shape);
1307 i->bboxrectpos = -1;
1314 void wipeSWF(SWF*swf)
1316 TAG*tag = swf->firstTag;
1318 TAG*next = tag->next;
1319 if(tag->id != ST_SETBACKGROUNDCOLOR &&
1320 tag->id != ST_END &&
1321 tag->id != ST_DOACTION &&
1322 tag->id != ST_SHOWFRAME) {
1329 void swfoutput_finalize(gfxdevice_t*dev)
1331 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1333 if(i->tag && i->tag->id == ST_END)
1334 return; //already done
1336 i->swf->fileVersion = i->config_flashversion;
1337 i->swf->frameRate = i->config_framerate*0x100;
1339 if(i->config_bboxvars) {
1340 TAG* tag = swf_InsertTag(i->swf->firstTag, ST_DOACTION);
1342 a = action_PushString(a, "xmin");
1343 a = action_PushFloat(a, i->swf->movieSize.xmin / 20.0);
1344 a = action_SetVariable(a);
1345 a = action_PushString(a, "ymin");
1346 a = action_PushFloat(a, i->swf->movieSize.ymin / 20.0);
1347 a = action_SetVariable(a);
1348 a = action_PushString(a, "xmax");
1349 a = action_PushFloat(a, i->swf->movieSize.xmax / 20.0);
1350 a = action_SetVariable(a);
1351 a = action_PushString(a, "ymax");
1352 a = action_PushFloat(a, i->swf->movieSize.ymax / 20.0);
1353 a = action_SetVariable(a);
1354 a = action_PushString(a, "width");
1355 a = action_PushFloat(a, (i->swf->movieSize.xmax - i->swf->movieSize.xmin) / 20.0);
1356 a = action_SetVariable(a);
1357 a = action_PushString(a, "height");
1358 a = action_PushFloat(a, (i->swf->movieSize.ymax - i->swf->movieSize.ymin) / 20.0);
1359 a = action_SetVariable(a);
1361 swf_ActionSet(tag, a);
1366 free(i->mark);i->mark = 0;
1370 fontlist_t *iterator = i->fontlist;
1372 TAG*mtag = i->swf->firstTag;
1373 if(iterator->swffont) {
1374 if(!i->config_storeallcharacters) {
1375 msg("<debug> Reducing font %s", iterator->swffont->name);
1376 swf_FontReduce(iterator->swffont);
1378 int used = iterator->swffont->use && iterator->swffont->use->used_glyphs;
1380 mtag = swf_InsertTag(mtag, ST_DEFINEFONT2);
1381 swf_FontSetDefine2(mtag, iterator->swffont);
1385 iterator = iterator->next;
1388 i->tag = swf_InsertTag(i->tag,ST_END);
1389 TAG* tag = i->tag->prev;
1391 /* remove the removeobject2 tags between the last ST_SHOWFRAME
1392 and the ST_END- they confuse the flash player */
1393 while(tag->id == ST_REMOVEOBJECT2) {
1394 TAG* prev = tag->prev;
1402 if(i->config_enablezlib || i->config_flashversion>=6) {
1403 i->swf->compressed = 1;
1406 /* Initialize AVM2 if it is a Flash9 file */
1407 if(i->config_flashversion>=9 && i->config_insertstoptag) {
1408 AVM2_InsertStops(i->swf);
1410 // if(i->config_reordertags)
1411 // swf_Optimize(i->swf);
1414 int swfresult_save(gfxresult_t*gfx, const char*filename)
1416 SWF*swf = (SWF*)gfx->internal;
1419 fi = open(filename, O_BINARY|O_CREAT|O_TRUNC|O_WRONLY, 0777);
1424 msg("<fatal> Could not create \"%s\". ", FIXNULL(filename));
1428 if FAILED(swf_WriteSWF(fi,swf))
1429 msg("<error> WriteSWF() failed.\n");
1435 void* swfresult_get(gfxresult_t*gfx, const char*name)
1437 SWF*swf = (SWF*)gfx->internal;
1438 if(!strcmp(name, "swf")) {
1439 return (void*)swf_CopySWF(swf);
1440 } else if(!strcmp(name, "xmin")) {
1441 return (void*)(swf->movieSize.xmin/20);
1442 } else if(!strcmp(name, "ymin")) {
1443 return (void*)(swf->movieSize.ymin/20);
1444 } else if(!strcmp(name, "xmax")) {
1445 return (void*)(swf->movieSize.xmax/20);
1446 } else if(!strcmp(name, "ymax")) {
1447 return (void*)(swf->movieSize.ymax/20);
1448 } else if(!strcmp(name, "width")) {
1449 return (void*)((swf->movieSize.xmax - swf->movieSize.xmin)/20);
1450 } else if(!strcmp(name, "height")) {
1451 return (void*)((swf->movieSize.ymax - swf->movieSize.ymin)/20);
1455 void swfresult_destroy(gfxresult_t*gfx)
1458 swf_FreeTags((SWF*)gfx->internal);
1459 free(gfx->internal);
1462 memset(gfx, 0, sizeof(gfxresult_t));
1466 static void swfoutput_destroy(gfxdevice_t* dev);
1468 gfxresult_t* swf_finish(gfxdevice_t* dev)
1470 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1473 if(i->config_linktarget) {
1474 free(i->config_linktarget);
1475 i->config_linktarget = 0;
1478 swfoutput_finalize(dev);
1479 SWF* swf = i->swf;i->swf = 0;
1480 swfoutput_destroy(dev);
1482 result = (gfxresult_t*)rfx_calloc(sizeof(gfxresult_t));
1483 result->internal = swf;
1484 result->save = swfresult_save;
1486 result->get = swfresult_get;
1487 result->destroy = swfresult_destroy;
1491 /* Perform cleaning up */
1492 static void swfoutput_destroy(gfxdevice_t* dev)
1494 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1496 /* not initialized yet- nothing to destroy */
1500 fontlist_t *tmp,*iterator = i->fontlist;
1502 if(iterator->swffont) {
1503 swf_FontFree(iterator->swffont);iterator->swffont=0;
1506 iterator = iterator->next;
1509 if(i->swf) {swf_FreeTags(i->swf);free(i->swf);i->swf = 0;}
1512 memset(dev, 0, sizeof(gfxdevice_t));
1515 static void swfoutput_setstrokecolor(gfxdevice_t* dev, U8 r, U8 g, U8 b, U8 a)
1517 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1518 if(i->strokergb.r == r &&
1519 i->strokergb.g == g &&
1520 i->strokergb.b == b &&
1521 i->strokergb.a == a) return;
1531 //#define ROUND_UP 19
1532 //#define ROUND_UP 10
1534 static void swfoutput_setlinewidth(gfxdevice_t*dev, double _linewidth)
1536 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1537 if(i->linewidth == (U16)(_linewidth*20+19.0/20.0))
1541 i->linewidth = (U16)(_linewidth*20+19.0/20.0);
1545 static void drawlink(gfxdevice_t*dev, ActionTAG*,ActionTAG*, gfxline_t*points, char mouseover, const char*url);
1546 static void swfoutput_namedlink(gfxdevice_t*dev, char*name, gfxline_t*points);
1547 static void swfoutput_linktopage(gfxdevice_t*dev, int page, gfxline_t*points);
1548 static void swfoutput_linktourl(gfxdevice_t*dev, const char*url, gfxline_t*points);
1550 /*void swfoutput_drawlink(gfxdevice_t*dev, char*url, gfxline_t*points)
1552 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1553 dev->drawlink(dev, points, url);
1556 void swf_drawlink(gfxdevice_t*dev, gfxline_t*points, const char*url)
1558 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1560 if(!strncmp("http://pdf2swf:", url, 15)) {
1561 char*tmp = strdup(url);
1562 int l = strlen(tmp);
1565 swfoutput_namedlink(dev, tmp+15, points);
1568 } else if(!strncmp("page", url, 4)) {
1571 if(url[t]<'0' || url[t]>'9')
1574 int page = atoi(&url[4]);
1575 if(page<0) page = 0;
1576 swfoutput_linktopage(dev, page, points);
1579 swfoutput_linktourl(dev, url, points);
1582 void swfoutput_linktourl(gfxdevice_t*dev, const char*url, gfxline_t*points)
1584 ActionTAG* actions = 0;
1585 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1591 if(i->config_externallinkfunction) {
1592 actions = action_PushString(actions, url); //parameter
1593 actions = action_PushInt(actions, 1); //number of parameters (1)
1594 actions = action_PushString(actions, i->config_externallinkfunction); //function name
1595 actions = action_CallFunction(actions);
1596 } else if(!i->config_linktarget) {
1597 if(!i->config_opennewwindow)
1598 actions = action_GetUrl(actions, url, "_parent");
1600 actions = action_GetUrl(actions, url, "_this");
1602 actions = action_GetUrl(actions, url, i->config_linktarget);
1604 actions = action_End(actions);
1606 drawlink(dev, actions, 0, points, 0, url);
1608 void swfoutput_linktopage(gfxdevice_t*dev, int page, gfxline_t*points)
1610 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1611 ActionTAG* actions = 0;
1618 if(!i->config_internallinkfunction) {
1619 actions = action_GotoFrame(actions, page-1);
1620 actions = action_End(actions);
1622 actions = action_PushInt(actions, page); //parameter
1623 actions = action_PushInt(actions, 1); //number of parameters (1)
1624 actions = action_PushString(actions, i->config_internallinkfunction); //function name
1625 actions = action_CallFunction(actions);
1626 actions = action_End(actions);
1630 sprintf(name, "page%d", page);
1632 drawlink(dev, actions, 0, points, 0, name);
1635 /* Named Links (a.k.a. Acrobatmenu) are used to implement various gadgets
1636 of the viewer objects, like subtitles, index elements etc.
1638 void swfoutput_namedlink(gfxdevice_t*dev, char*name, gfxline_t*points)
1640 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1641 ActionTAG *actions1,*actions2;
1642 char*tmp = strdup(name);
1650 if(!strncmp(tmp, "call:", 5))
1652 char*x = strchr(&tmp[5], ':');
1654 actions1 = action_PushInt(0, 0); //number of parameters (0)
1655 actions1 = action_PushString(actions1, &tmp[5]); //function name
1656 actions1 = action_CallFunction(actions1);
1657 actions1 = action_End(actions1);
1660 actions1 = action_PushString(0, x+1); //parameter
1661 actions1 = action_PushInt(actions1, 1); //number of parameters (1)
1662 actions1 = action_PushString(actions1, &tmp[5]); //function name
1663 actions1 = action_CallFunction(actions1);
1664 actions1 = action_End(actions1);
1666 actions2 = action_End(0);
1671 actions1 = action_PushString(0, "/:subtitle");
1672 actions1 = action_PushString(actions1, name);
1673 actions1 = action_SetVariable(actions1);
1674 actions1 = action_End(actions1);
1676 actions2 = action_PushString(0, "/:subtitle");
1677 actions2 = action_PushString(actions2, "");
1678 actions2 = action_SetVariable(actions2);
1679 actions2 = action_End(actions2);
1682 drawlink(dev, actions1, actions2, points, mouseover, name);
1684 swf_ActionFree(actions1);
1685 swf_ActionFree(actions2);
1689 static void drawgfxline(gfxdevice_t*dev, gfxline_t*line, int fill)
1691 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1692 gfxcoord_t lastx=0,lasty=0,px=0,py=0;
1694 int lines= 0, splines=0;
1701 /* check whether the next segment is zero */
1702 if(line->type == gfx_moveTo) {
1703 moveto(dev, i->tag, line->x, line->y);
1704 px = lastx = line->x;
1705 py = lasty = line->y;
1707 } if(line->type == gfx_lineTo) {
1708 lineto(dev, i->tag, line->x, line->y);
1713 } else if(line->type == gfx_splineTo) {
1715 s.x = line->sx;p.x = line->x;
1716 s.y = line->sy;p.y = line->y;
1717 splineto(dev, i->tag, s, p);
1725 msg("<trace> drawgfxline, %d lines, %d splines", lines, splines);
1729 static void drawlink(gfxdevice_t*dev, ActionTAG*actions1, ActionTAG*actions2, gfxline_t*points, char mouseover, const char*url)
1731 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1740 int buttonid = getNewID(dev);
1741 gfxbbox_t bbox = gfxline_getbbox(points);
1744 myshapeid = getNewID(dev);
1745 i->tag = swf_InsertTag(i->tag,ST_DEFINESHAPE3);
1746 swf_ShapeNew(&i->shape);
1747 rgb.r = rgb.b = rgb.a = rgb.g = 0;
1748 fsid = swf_ShapeAddSolidFillStyle(i->shape,&rgb);
1749 swf_SetU16(i->tag, myshapeid);
1750 r.xmin = (int)(bbox.xmin*20);
1751 r.ymin = (int)(bbox.ymin*20);
1752 r.xmax = (int)(bbox.xmax*20);
1753 r.ymax = (int)(bbox.ymax*20);
1754 r = swf_ClipRect(i->pagebbox, r);
1755 swf_SetRect(i->tag,&r);
1756 swf_SetShapeStyles(i->tag,i->shape);
1757 swf_ShapeCountBits(i->shape,NULL,NULL);
1758 swf_SetShapeBits(i->tag,i->shape);
1759 swf_ShapeSetAll(i->tag,i->shape,/*x*/0,/*y*/0,0,fsid,0);
1760 i->swflastx = i->swflasty = 0;
1761 drawgfxline(dev, points, 1);
1762 swf_ShapeSetEnd(i->tag);
1765 myshapeid2 = getNewID(dev);
1766 i->tag = swf_InsertTag(i->tag,ST_DEFINESHAPE3);
1767 swf_ShapeNew(&i->shape);
1769 rgb = i->config_linkcolor;
1771 fsid = swf_ShapeAddSolidFillStyle(i->shape,&rgb);
1772 swf_SetU16(i->tag, myshapeid2);
1773 r.xmin = (int)(bbox.xmin*20);
1774 r.ymin = (int)(bbox.ymin*20);
1775 r.xmax = (int)(bbox.xmax*20);
1776 r.ymax = (int)(bbox.ymax*20);
1777 r = swf_ClipRect(i->pagebbox, r);
1778 swf_SetRect(i->tag,&r);
1779 swf_SetShapeStyles(i->tag,i->shape);
1780 swf_ShapeCountBits(i->shape,NULL,NULL);
1781 swf_SetShapeBits(i->tag,i->shape);
1782 swf_ShapeSetAll(i->tag,i->shape,/*x*/0,/*y*/0,0,fsid,0);
1783 i->swflastx = i->swflasty = 0;
1784 drawgfxline(dev, points, 1);
1785 swf_ShapeSetEnd(i->tag);
1789 i->tag = swf_InsertTag(i->tag,ST_DEFINEBUTTON);
1790 swf_SetU16(i->tag,buttonid); //id
1791 swf_ButtonSetFlags(i->tag, 0); //menu=no
1792 swf_ButtonSetRecord(i->tag,0x01,myshapeid,i->depth,0,0);
1793 swf_ButtonSetRecord(i->tag,0x02,myshapeid2,i->depth,0,0);
1794 swf_ButtonSetRecord(i->tag,0x04,myshapeid2,i->depth,0,0);
1795 swf_ButtonSetRecord(i->tag,0x08,myshapeid,i->depth,0,0);
1796 swf_SetU8(i->tag,0);
1797 swf_ActionSet(i->tag,actions1);
1798 swf_SetU8(i->tag,0);
1802 i->tag = swf_InsertTag(i->tag,ST_DEFINEBUTTON2);
1803 swf_SetU16(i->tag,buttonid); //id
1804 swf_ButtonSetFlags(i->tag, 0); //menu=no
1805 swf_ButtonSetRecord(i->tag,0x01,myshapeid,i->depth,0,0);
1806 swf_ButtonSetRecord(i->tag,0x02,myshapeid2,i->depth,0,0);
1807 swf_ButtonSetRecord(i->tag,0x04,myshapeid2,i->depth,0,0);
1808 swf_ButtonSetRecord(i->tag,0x08,myshapeid,i->depth,0,0);
1809 swf_SetU8(i->tag,0); // end of button records
1810 swf_ButtonSetCondition(i->tag, BC_IDLE_OVERUP);
1811 swf_ActionSet(i->tag,actions1);
1813 swf_ButtonSetCondition(i->tag, BC_OVERUP_IDLE);
1814 swf_ActionSet(i->tag,actions2);
1815 swf_SetU8(i->tag,0);
1816 swf_ButtonPostProcess(i->tag, 2);
1818 swf_SetU8(i->tag,0);
1819 swf_ButtonPostProcess(i->tag, 1);
1822 const char* name = 0;
1823 if(i->config_linknameurl) {
1827 msg("<trace> Placing link ID %d", buttonid);
1828 i->tag = swf_InsertTag(i->tag,ST_PLACEOBJECT2);
1830 if(posx!=0 || posy!=0) {
1832 p.x = (int)(posx*20);
1833 p.y = (int)(posy*20);
1834 p = swf_TurnPoint(p, &i->page_matrix);
1839 swf_ObjectPlace(i->tag, buttonid, getNewDepth(dev),&m,0,name);
1841 swf_ObjectPlace(i->tag, buttonid, getNewDepth(dev),&i->page_matrix,0,name);
1848 for(t=0;t<picpos;t++)
1850 if(pic_xids[t] == xid &&
1851 pic_yids[t] == yid) {
1852 width = pic_width[t];
1853 height = pic_height[t];
1857 pic_ids[picpos] = swfoutput_drawimagelosslessN(&output, pic, pal, width, height, x1,y1,x2,y2,x3,y3,x4,y4, numpalette);
1858 pic_xids[picpos] = xid;
1859 pic_yids[picpos] = yid;
1860 pic_width[picpos] = width;
1861 pic_height[picpos] = height;
1864 pic[width*y+x] = buf[0];
1868 xid += pal[1].r*3 + pal[1].g*11 + pal[1].b*17;
1869 yid += pal[1].r*7 + pal[1].g*5 + pal[1].b*23;
1873 xid += x*r+x*b*3+x*g*7+x*a*11;
1874 yid += y*r*3+y*b*17+y*g*19+y*a*11;
1876 for(t=0;t<picpos;t++)
1878 if(pic_xids[t] == xid &&
1879 pic_yids[t] == yid) {
1888 int swf_setparameter(gfxdevice_t*dev, const char*name, const char*value)
1890 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1892 msg("<trace> swfdevice: %s=%s", name, value);
1893 if(!strcmp(name, "jpegsubpixels")) {
1894 i->config_jpegsubpixels = atof(value);
1895 } else if(!strcmp(name, "ppmsubpixels")) {
1896 i->config_ppmsubpixels = atof(value);
1897 } else if(!strcmp(name, "subpixels")) {
1898 i->config_ppmsubpixels = i->config_jpegsubpixels = atof(value);
1899 } else if(!strcmp(name, "drawonlyshapes")) {
1900 i->config_drawonlyshapes = atoi(value);
1901 } else if(!strcmp(name, "ignoredraworder")) {
1902 i->config_ignoredraworder = atoi(value);
1903 } else if(!strcmp(name, "mark")) {
1904 if(!value || !value[0]) {
1905 if(i->mark) free(i->mark);
1909 i->mark = strdup("...");
1910 for(t=0;t<3;t++) if(value[t]) i->mark[t] = value[t];
1912 } else if(!strcmp(name, "filloverlap")) {
1913 i->config_filloverlap = atoi(value);
1914 } else if(!strcmp(name, "linksopennewwindow")) {
1915 i->config_opennewwindow = atoi(value);
1916 } else if(!strcmp(name, "opennewwindow")) {
1917 i->config_opennewwindow = atoi(value);
1918 } else if(!strcmp(name, "storeallcharacters")) {
1919 i->config_storeallcharacters = atoi(value);
1920 } else if(!strcmp(name, "enablezlib")) {
1921 i->config_enablezlib = atoi(value);
1922 } else if(!strcmp(name, "bboxvars")) {
1923 i->config_bboxvars = atoi(value);
1924 } else if(!strcmp(name, "frameresets")) {
1925 i->config_frameresets = atoi(value);
1926 } else if(!strcmp(name, "showclipshapes")) {
1927 i->config_showclipshapes = atoi(value);
1928 } else if(!strcmp(name, "reordertags")) {
1929 i->config_reordertags = atoi(value);
1930 } else if(!strcmp(name, "internallinkfunction")) {
1931 i->config_internallinkfunction = strdup(value);
1932 } else if(!strcmp(name, "externallinkfunction")) {
1933 i->config_externallinkfunction = strdup(value);
1934 } else if(!strcmp(name, "linkfunction")) { //sets both internallinkfunction and externallinkfunction
1935 i->config_internallinkfunction = strdup(value);
1936 i->config_externallinkfunction = strdup(value);
1937 } else if(!strcmp(name, "disable_polygon_conversion")) {
1938 i->config_disable_polygon_conversion = atoi(value);
1939 } else if(!strcmp(name, "normalize_polygon_positions")) {
1940 i->config_normalize_polygon_positions = atoi(value);
1941 } else if(!strcmp(name, "wxwindowparams")) {
1942 i->config_watermark = atoi(value);
1943 } else if(!strcmp(name, "insertstop")) {
1944 i->config_insertstoptag = atoi(value);
1945 } else if(!strcmp(name, "protect")) {
1946 i->config_protect = atoi(value);
1947 if(i->config_protect && i->tag) {
1948 i->tag = swf_InsertTag(i->tag, ST_PROTECT);
1950 } else if(!strcmp(name, "flashversion")) {
1951 i->config_flashversion = atoi(value);
1953 i->swf->fileVersion = i->config_flashversion;
1955 } else if(!strcmp(name, "framerate")) {
1956 i->config_framerate = atoi(value);
1958 i->swf->frameRate = i->config_framerate*0x100;
1960 } else if(!strcmp(name, "minlinewidth")) {
1961 i->config_minlinewidth = atof(value);
1962 } else if(!strcmp(name, "caplinewidth")) {
1963 i->config_caplinewidth = atof(value);
1964 } else if(!strcmp(name, "linktarget")) {
1965 i->config_linktarget = strdup(value);
1966 } else if(!strcmp(name, "dumpfonts")) {
1967 i->config_dumpfonts = atoi(value);
1968 } else if(!strcmp(name, "animate")) {
1969 i->config_animate = atoi(value);
1970 } else if(!strcmp(name, "simpleviewer")) {
1971 i->config_simpleviewer = atoi(value);
1972 } else if(!strcmp(name, "next_bitmap_is_jpeg")) {
1974 } else if(!strcmp(name, "jpegquality")) {
1975 int val = atoi(value);
1977 if(val>101) val=101;
1978 i->config_jpegquality = val;
1979 } else if(!strcmp(name, "splinequality")) {
1980 int v = atoi(value);
1981 v = 500-(v*5); // 100% = 0.25 pixel, 0% = 25 pixel
1983 i->config_splinemaxerror = v;
1984 } else if(!strcmp(name, "fontquality")) {
1985 int v = atoi(value);
1986 v = 500-(v*5); // 100% = 0.25 pixel, 0% = 25 pixel
1988 i->config_fontsplinemaxerror = v;
1989 } else if(!strcmp(name, "linkcolor")) {
1990 if(strlen(value)!=8) {
1991 fprintf(stderr, "Unknown format for option 'linkcolor'. (%s <-> RRGGBBAA)\n", value);
1994 # define NIBBLE(s) (((s)>='0' && (s)<='9')?((s)-'0'):((s)&0x0f)+9)
1995 i->config_linkcolor.r = NIBBLE(value[0])<<4 | NIBBLE(value[1]);
1996 i->config_linkcolor.g = NIBBLE(value[2])<<4 | NIBBLE(value[3]);
1997 i->config_linkcolor.b = NIBBLE(value[4])<<4 | NIBBLE(value[5]);
1998 i->config_linkcolor.a = NIBBLE(value[6])<<4 | NIBBLE(value[7]);
1999 } else if(!strcmp(name, "help")) {
2000 printf("\nSWF layer options:\n");
2001 printf("jpegsubpixels=<pixels> resolution adjustment for jpeg images (same as jpegdpi, but in pixels)\n");
2002 printf("ppmsubpixels=<pixels resolution adjustment for lossless images (same as ppmdpi, but in pixels)\n");
2003 printf("subpixels=<pixels> shortcut for setting both jpegsubpixels and ppmsubpixels\n");
2004 printf("drawonlyshapes convert everything to shapes (currently broken)\n");
2005 printf("ignoredraworder allow to perform a few optimizations for creating smaller SWFs\n");
2006 printf("linksopennewwindow make links open a new browser window\n");
2007 printf("linktarget target window name of new links\n");
2008 printf("linkcolor=<color) color of links (format: RRGGBBAA)\n");
2009 printf("linknameurl Link buttons will be named like the URL they refer to (handy for iterating through links with actionscript)\n");
2010 printf("storeallcharacters don't reduce the fonts to used characters in the output file\n");
2011 printf("enablezlib switch on zlib compression (also done if flashversion>=7)\n");
2012 printf("bboxvars store the bounding box of the SWF file in actionscript variables\n");
2013 printf("reordertags=0/1 (default: 1) perform some tag optimizations\n");
2014 printf("internallinkfunction=<name> when the user clicks a internal link (to a different page) in the converted file, this actionscript function is called\n");
2015 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");
2016 printf("disable_polygon_conversion never convert strokes to polygons (will remove capstyles and joint styles)\n");
2017 printf("caplinewidth=<width> the minimum thichness a line needs to have so that capstyles become visible (and are converted)\n");
2018 printf("insertstop put an ActionScript \"STOP\" tag in every frame\n");
2019 printf("protect add a \"protect\" tag to the file, to prevent loading in the Flash editor\n");
2020 printf("flashversion=<version> the SWF fileversion (6)\n");
2021 printf("framerate=<fps> SWF framerate\n");
2022 printf("minlinewidth=<width> convert horizontal/vertical boxes smaller than this width to lines (0.05) \n");
2023 printf("simpleviewer Add next/previous buttons to the SWF\n");
2024 printf("animate insert a showframe tag after each placeobject (animate draw order of PDF files)\n");
2025 printf("jpegquality=<quality> set compression quality of jpeg images\n");
2026 printf("splinequality=<value> Set the quality of spline convertion to value (0-100, default: 100).\n");
2033 // --------------------------------------------------------------------
2035 static CXFORM gfxcxform_to_cxform(gfxcxform_t* c)
2038 swf_GetCXForm(0, &cx, 1);
2041 if(c->rg!=0 || c->rb!=0 || c->ra!=0 ||
2042 c->gr!=0 || c->gb!=0 || c->ga!=0 ||
2043 c->br!=0 || c->bg!=0 || c->ba!=0 ||
2044 c->ar!=0 || c->ag!=0 || c->ab!=0)
2045 msg("<warning> CXForm not SWF-compatible");
2047 cx.a0 = (S16)(c->aa*256);
2048 cx.r0 = (S16)(c->rr*256);
2049 cx.g0 = (S16)(c->gg*256);
2050 cx.b0 = (S16)(c->bb*256);
2059 static int imageInCache(gfxdevice_t*dev, void*data, int width, int height)
2063 static void addImageToCache(gfxdevice_t*dev, void*data, int width, int height)
2067 static int add_image(swfoutput_internal*i, gfximage_t*img, int targetwidth, int targetheight, int* newwidth, int* newheight)
2069 gfxdevice_t*dev = i->dev;
2071 RGBA*mem = (RGBA*)img->data;
2073 int sizex = img->width;
2074 int sizey = img->height;
2075 int is_jpeg = i->jpeg;
2078 int newsizex=sizex, newsizey=sizey;
2081 if(is_jpeg && i->config_jpegsubpixels) {
2082 newsizex = (int)(targetwidth*i->config_jpegsubpixels + 0.5);
2083 newsizey = (int)(targetheight*i->config_jpegsubpixels + 0.5);
2084 } else if(!is_jpeg && i->config_ppmsubpixels) {
2085 newsizex = (int)(targetwidth*i->config_ppmsubpixels + 0.5);
2086 newsizey = (int)(targetheight*i->config_ppmsubpixels + 0.5);
2090 if(sizex<=0 || sizey<=0)
2097 /* TODO: cache images */
2099 if(newsizex<sizex || newsizey<sizey) {
2100 msg("<verbose> Scaling %dx%d image to %dx%d", sizex, sizey, newsizex, newsizey);
2101 newpic = swf_ImageScale(mem, sizex, sizey, newsizex, newsizey);
2102 *newwidth = sizex = newsizex;
2103 *newheight = sizey = newsizey;
2106 *newwidth = newsizex = sizex;
2107 *newheight = newsizey = sizey;
2110 int num_colors = swf_ImageGetNumberOfPaletteEntries(mem,sizex,sizey,0);
2111 int has_alpha = swf_ImageHasAlpha(mem,sizex,sizey);
2113 msg("<verbose> Drawing %dx%d %s%simage (id %d) at size %dx%d (%dx%d), %s%d colors",
2115 has_alpha?(has_alpha==2?"semi-transparent ":"transparent "):"",
2116 is_jpeg?"jpeg-":"", i->currentswfid+1,
2118 targetwidth, targetheight,
2119 /*newsizex, newsizey,*/
2120 num_colors>256?">":"", num_colors>256?256:num_colors);
2122 /*RGBA* pal = (RGBA*)rfx_alloc(sizeof(RGBA)*num_colors);
2123 swf_ImageGetNumberOfPaletteEntries(mem,sizex,sizey,pal);
2125 for(t=0;t<num_colors;t++) {
2126 printf("%02x%02x%02x%02x ",
2127 pal[t].r, pal[t].g, pal[t].b, pal[t].a);
2134 int cacheid = imageInCache(dev, mem, sizex, sizey);
2137 bitid = getNewID(dev);
2139 i->tag = swf_AddImage(i->tag, bitid, mem, sizex, sizey, i->config_jpegquality);
2140 addImageToCache(dev, mem, sizex, sizey);
2150 static SRECT gfxline_getSWFbbox(gfxline_t*line)
2152 gfxbbox_t bbox = gfxline_getbbox(line);
2154 r.xmin = (int)(bbox.xmin*20);
2155 r.ymin = (int)(bbox.ymin*20);
2156 r.xmax = (int)(bbox.xmax*20);
2157 r.ymax = (int)(bbox.ymax*20);
2161 int line_is_empty(gfxline_t*line)
2164 if(line->type != gfx_moveTo)
2171 static void swf_fillbitmap(gfxdevice_t*dev, gfxline_t*line, gfximage_t*img, gfxmatrix_t*matrix, gfxcxform_t*cxform)
2173 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
2175 if(line_is_empty(line))
2181 int targetx = (int)(sqrt(matrix->m00*matrix->m00 + matrix->m01*matrix->m01)*img->width);
2182 int targety = (int)(sqrt(matrix->m10*matrix->m10 + matrix->m11*matrix->m11)*img->height);
2184 int newwidth=0,newheight=0;
2185 int bitid = add_image(i, img, targetx, targety, &newwidth, &newheight);
2188 double fx = (double)img->width / (double)newwidth;
2189 double fy = (double)img->height / (double)newheight;
2192 m.sx = (int)(65536*20*matrix->m00*fx); m.r1 = (int)(65536*20*matrix->m10*fy);
2193 m.r0 = (int)(65536*20*matrix->m01*fx); m.sy = (int)(65536*20*matrix->m11*fy);
2194 m.tx = (int)(matrix->tx*20);
2195 m.ty = (int)(matrix->ty*20);
2198 int myshapeid = getNewID(dev);
2199 i->tag = swf_InsertTag(i->tag,ST_DEFINESHAPE);
2201 swf_ShapeNew(&shape);
2202 int fsid = swf_ShapeAddBitmapFillStyle(shape,&m,bitid,1);
2203 swf_SetU16(i->tag, myshapeid);
2204 SRECT r = gfxline_getSWFbbox(line);
2205 r = swf_ClipRect(i->pagebbox, r);
2206 swf_SetRect(i->tag,&r);
2207 swf_SetShapeStyles(i->tag,shape);
2208 swf_ShapeCountBits(shape,NULL,NULL);
2209 swf_SetShapeBits(i->tag,shape);
2210 swf_ShapeSetAll(i->tag,shape,UNDEFINED_COORD,UNDEFINED_COORD,0,fsid,0);
2211 i->swflastx = i->swflasty = UNDEFINED_COORD;
2212 drawgfxline(dev, line, 1);
2213 swf_ShapeSetEnd(i->tag);
2214 swf_ShapeFree(shape);
2216 msg("<trace> Placing image, shape ID %d, bitmap ID %d", myshapeid, bitid);
2217 i->tag = swf_InsertTag(i->tag,ST_PLACEOBJECT2);
2218 CXFORM cxform2 = gfxcxform_to_cxform(cxform);
2219 swf_ObjectPlace(i->tag,myshapeid,getNewDepth(dev),&i->page_matrix,&cxform2,NULL);
2222 static RGBA col_black = {255,0,0,0};
2224 static void drawoutline(gfxdevice_t*dev, gfxline_t*line)
2226 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
2228 int myshapeid = getNewID(dev);
2229 i->tag = swf_InsertTag(i->tag,ST_DEFINESHAPE3);
2232 swf_ShapeNew(&shape);
2233 int lsid = swf_ShapeAddLineStyle(shape,1,&col_black);
2235 swf_SetU16(i->tag,myshapeid);
2236 SRECT r = gfxline_getSWFbbox(line);
2237 r = swf_ClipRect(i->pagebbox, r);
2238 swf_SetRect(i->tag,&r);
2239 swf_SetShapeStyles(i->tag,shape);
2240 swf_ShapeCountBits(shape,NULL,NULL);
2241 swf_SetShapeBits(i->tag,shape);
2242 swf_ShapeSetAll(i->tag,shape,UNDEFINED_COORD,UNDEFINED_COORD,lsid,0,0);
2243 drawgfxline(dev, line, 1);
2244 swf_ShapeSetEnd(i->tag);
2245 swf_ShapeFree(shape);
2247 i->tag = swf_InsertTag(i->tag,ST_PLACEOBJECT2);
2248 swf_ObjectPlace(i->tag, myshapeid, getNewDepth(dev), 0,0,0);
2251 static void swf_startclip(gfxdevice_t*dev, gfxline_t*line)
2253 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
2258 if(i->clippos >= 127)
2260 msg("<warning> Too many clip levels.");
2264 if(i->config_showclipshapes)
2265 drawoutline(dev, line);
2267 int myshapeid = getNewID(dev);
2268 i->tag = swf_InsertTag(i->tag,ST_DEFINESHAPE3);
2270 memset(&col, 0, sizeof(RGBA));
2273 swf_ShapeNew(&shape);
2274 int fsid = swf_ShapeAddSolidFillStyle(shape,&col);
2276 RGBA markcol = {0,i->mark[0],i->mark[1],i->mark[2]};
2277 swf_ShapeAddSolidFillStyle(shape,&markcol);
2279 swf_SetU16(i->tag,myshapeid);
2280 SRECT r = gfxline_getSWFbbox(line);
2281 r = swf_ClipRect(i->pagebbox, r);
2282 swf_SetRect(i->tag,&r);
2283 swf_SetShapeStyles(i->tag,shape);
2284 swf_ShapeCountBits(shape,NULL,NULL);
2285 swf_SetShapeBits(i->tag,shape);
2286 swf_ShapeSetAll(i->tag,shape,UNDEFINED_COORD,UNDEFINED_COORD,0,fsid,0);
2287 i->swflastx = i->swflasty = UNDEFINED_COORD;
2288 i->shapeisempty = 1;
2289 drawgfxline(dev, line, 1);
2290 if(i->shapeisempty) {
2291 /* an empty clip shape is equivalent to a shape with no area */
2292 int x = line?line->x:0;
2293 int y = line?line->y:0;
2294 moveto(dev, i->tag, x,y);
2295 lineto(dev, i->tag, x,y);
2296 lineto(dev, i->tag, x,y);
2298 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)) {
2299 if(i->config_watermark) {
2300 gfxbbox_t r; r.xmin = r.ymin = 0;r.xmax = i->max_x;r.ymax = i->max_y;
2301 draw_watermark(dev, r, 1);
2304 swf_ShapeSetEnd(i->tag);
2305 swf_ShapeFree(shape);
2307 /* TODO: remember the bbox, and check all shapes against it */
2309 msg("<trace> Placing clip ID %d", myshapeid);
2310 i->tag = swf_InsertTag(i->tag,ST_PLACEOBJECT2);
2311 i->cliptags[i->clippos] = i->tag;
2312 i->clipshapes[i->clippos] = myshapeid;
2313 i->clipdepths[i->clippos] = getNewDepth(dev);
2317 static void swf_endclip(gfxdevice_t*dev)
2319 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
2326 msg("<error> Invalid end of clipping region");
2330 /*swf_ObjectPlaceClip(i->cliptags[i->clippos],i->clipshapes[i->clippos],i->clipdepths[i->clippos],&i->page_matrix,NULL,NULL,
2331 / * clip to depth: * / i->depth <= i->clipdepths[i->clippos]? i->depth : i->depth - 1);
2333 swf_ObjectPlaceClip(i->cliptags[i->clippos],i->clipshapes[i->clippos],i->clipdepths[i->clippos],&i->page_matrix,NULL,NULL,i->depth);
2335 static int gfxline_type(gfxline_t*line)
2341 int haszerosegments=0;
2344 if(line->type == gfx_moveTo) {
2347 } else if(line->type == gfx_lineTo) {
2351 } else if(line->type == gfx_splineTo) {
2353 if(tmpsplines>lines)
2361 if(lines==0 && splines==0) return 0;
2362 else if(lines==1 && splines==0) return 1;
2363 else if(lines==0 && splines==1) return 2;
2364 else if(splines==0) return 3;
2368 static int gfxline_has_dots(gfxline_t*line)
2376 if(line->type == gfx_moveTo) {
2377 /* test the length of the preceding line, and assume it is a dot if
2378 it's length is less than 1.0. But *only* if there's a noticable
2379 gap between the previous line and the next moveTo. (I've come
2380 across a PDF where thousands of "dots" were stringed together,
2382 int last_short_gap = short_gap;
2383 if((fabs(line->x - x) + fabs(line->y - y)) < 1.0) {
2388 if(isline && dist < 1 && !short_gap && !last_short_gap) {
2393 } else if(line->type == gfx_lineTo) {
2394 dist += fabs(line->x - x) + fabs(line->y - y);
2396 } else if(line->type == gfx_splineTo) {
2397 dist += fabs(line->sx - x) + fabs(line->sy - y) +
2398 fabs(line->x - line->sx) + fabs(line->y - line->sy);
2405 if(isline && dist < 1 && !short_gap) {
2411 static int gfxline_fix_short_edges(gfxline_t*line)
2415 if(line->type == gfx_lineTo) {
2416 if(fabs(line->x - x) + fabs(line->y - y) < 0.01) {
2419 } else if(line->type == gfx_splineTo) {
2420 if(fabs(line->sx - x) + fabs(line->sy - y) +
2421 fabs(line->x - line->sx) + fabs(line->y - line->sy) < 0.01) {
2432 static char is_inside_page(gfxdevice_t*dev, gfxcoord_t x, gfxcoord_t y)
2434 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
2435 if(x<i->min_x || x>i->max_x) return 0;
2436 if(y<i->min_y || y>i->max_y) return 0;
2440 gfxline_t* gfxline_move(gfxline_t*line, double x, double y)
2442 gfxline_t*l = line = gfxline_clone(line);
2454 //#define NORMALIZE_POLYGON_POSITIONS
2456 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)
2458 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
2459 if(line_is_empty(line))
2461 int type = gfxline_type(line);
2462 int has_dots = gfxline_has_dots(line);
2463 gfxbbox_t r = gfxline_getbbox(line);
2464 int is_outside_page = !is_inside_page(dev, r.xmin, r.ymin) || !is_inside_page(dev, r.xmax, r.ymax);
2466 /* TODO: * split line into segments, and perform this check for all segments */
2468 if(i->config_disable_polygon_conversion || type>=5 ||
2470 (width <= i->config_caplinewidth
2471 || (cap_style == gfx_capRound && joint_style == gfx_joinRound)
2472 || (cap_style == gfx_capRound && type<=2)))) {} else
2474 /* convert line to polygon */
2475 msg("<trace> draw as polygon, type=%d dots=%d", type, has_dots);
2477 gfxline_fix_short_edges(line);
2478 /* we need to convert the line into a polygon */
2479 gfxpoly_t* poly = gfxpoly_strokeToPoly(line, width, cap_style, joint_style, miterLimit);
2480 gfxline_t*gfxline = gfxpoly_to_gfxline(poly);
2481 dev->fill(dev, gfxline, color);
2482 gfxline_free(gfxline);
2487 msg("<trace> draw as stroke, type=%d dots=%d", type, has_dots);
2490 if(i->config_normalize_polygon_positions) {
2492 double startx = 0, starty = 0;
2493 if(line && line->type == gfx_moveTo) {
2497 line = gfxline_move(line, -startx, -starty);
2498 i->shapeposx = (int)(startx*20);
2499 i->shapeposy = (int)(starty*20);
2502 swfoutput_setstrokecolor(dev, color->r, color->g, color->b, color->a);
2503 swfoutput_setlinewidth(dev, width);
2506 drawgfxline(dev, line, 0);
2508 if(i->config_normalize_polygon_positions) {
2509 free(line); //account for _move
2514 static void swf_fill(gfxdevice_t*dev, gfxline_t*line, gfxcolor_t*color)
2516 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
2517 if(line_is_empty(line))
2521 gfxbbox_t r = gfxline_getbbox(line);
2522 int is_outside_page = !is_inside_page(dev, r.xmin, r.ymin) || !is_inside_page(dev, r.xmax, r.ymax);
2524 //if(i->chardatapos && i->chardata[i->chardatapos-1].color.a) {
2527 if(!i->config_ignoredraworder)
2530 if(i->config_normalize_polygon_positions) {
2532 double startx = 0, starty = 0;
2533 if(line && line->type == gfx_moveTo) {
2537 line = gfxline_move(line, -startx, -starty);
2538 i->shapeposx = (int)(startx*20);
2539 i->shapeposy = (int)(starty*20);
2542 swfoutput_setfillcolor(dev, color->r, color->g, color->b, color->a);
2545 drawgfxline(dev, line, 1);
2547 if(i->currentswfid==2 && r.xmin==0 && r.ymin==0 && r.xmax==i->max_x && r.ymax==i->max_y) {
2548 if(i->config_watermark) {
2549 draw_watermark(dev, r, 1);
2553 msg("<trace> end of swf_fill (shapeid=%d)", i->shapeid);
2555 if(i->config_normalize_polygon_positions) {
2556 free(line); //account for _move
2560 static GRADIENT* gfxgradient_to_GRADIENT(gfxgradient_t*gradient)
2563 gfxgradient_t*g = gradient;
2568 GRADIENT* swfgradient = malloc(sizeof(GRADIENT));
2569 swfgradient->num = num;
2570 swfgradient->rgba = malloc(sizeof(swfgradient->rgba[0])*num);
2571 swfgradient->ratios = malloc(sizeof(swfgradient->ratios[0])*num);
2576 swfgradient->ratios[num] = g->pos*255;
2577 swfgradient->rgba[num] = *(RGBA*)&g->color;
2584 static void swf_fillgradient(gfxdevice_t*dev, gfxline_t*line, gfxgradient_t*gradient, gfxgradienttype_t type, gfxmatrix_t*matrix)
2586 if(line_is_empty(line))
2588 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
2590 if(line_is_empty(line))
2593 GRADIENT* swfgradient = gfxgradient_to_GRADIENT(gradient);
2600 double f = type==gfxgradient_radial?4:4;
2602 m.sx = (int)(matrix->m00*20*f); m.r1 = (int)(matrix->m10*20*f);
2603 m.r0 = (int)(matrix->m01*20*f); m.sy = (int)(matrix->m11*20*f);
2604 m.tx = (int)(matrix->tx*20);
2605 m.ty = (int)(matrix->ty*20);
2608 int myshapeid = getNewID(dev);
2609 i->tag = swf_InsertTag(i->tag, ST_DEFINESHAPE2);
2611 swf_ShapeNew(&shape);
2612 int fsid = swf_ShapeAddGradientFillStyle(shape,&m,swfgradient,type==gfxgradient_radial);
2613 swf_SetU16(i->tag, myshapeid);
2614 SRECT r = gfxline_getSWFbbox(line);
2615 r = swf_ClipRect(i->pagebbox, r);
2616 swf_SetRect(i->tag,&r);
2617 swf_SetShapeStyles(i->tag,shape);
2618 swf_ShapeCountBits(shape,NULL,NULL);
2619 swf_SetShapeBits(i->tag,shape);
2620 swf_ShapeSetAll(i->tag,shape,UNDEFINED_COORD,UNDEFINED_COORD,0,fsid,0);
2621 i->swflastx = i->swflasty = UNDEFINED_COORD;
2622 drawgfxline(dev, line, 1);
2623 swf_ShapeSetEnd(i->tag);
2624 swf_ShapeFree(shape);
2626 int depth = getNewDepth(dev);
2627 msg("<trace> Placing gradient, shape ID %d, depth %d", myshapeid, depth);
2628 i->tag = swf_InsertTag(i->tag,ST_PLACEOBJECT2);
2629 swf_ObjectPlace(i->tag,myshapeid,depth,&i->page_matrix,NULL,NULL);
2631 swf_FreeGradient(swfgradient);free(swfgradient);
2634 static SWFFONT* gfxfont_to_swffont(gfxfont_t*font, const char* id)
2636 SWFFONT*swffont = (SWFFONT*)rfx_calloc(sizeof(SWFFONT));
2638 SRECT bounds = {0,0,0,0};
2640 swffont->version = 2;
2641 swffont->name = (U8*)strdup(id);
2642 swffont->layout = (SWFLAYOUT*)rfx_calloc(sizeof(SWFLAYOUT));
2643 swffont->layout->ascent = 0;
2644 swffont->layout->descent = 0;
2645 swffont->layout->leading = 0;
2646 swffont->layout->bounds = (SRECT*)rfx_calloc(sizeof(SRECT)*font->num_glyphs);
2647 swffont->encoding = FONT_ENCODING_UNICODE;
2648 swffont->numchars = font->num_glyphs;
2649 swffont->maxascii = font->max_unicode;
2650 swffont->ascii2glyph = (int*)rfx_calloc(sizeof(int)*swffont->maxascii);
2651 swffont->glyph2ascii = (U16*)rfx_calloc(sizeof(U16)*swffont->numchars);
2652 swffont->glyph = (SWFGLYPH*)rfx_calloc(sizeof(SWFGLYPH)*swffont->numchars);
2653 swffont->glyphnames = (char**)rfx_calloc(sizeof(char*)*swffont->numchars);
2654 for(t=0;t<font->max_unicode;t++) {
2655 swffont->ascii2glyph[t] = font->unicode2glyph[t];
2657 for(t=0;t<font->num_glyphs;t++) {
2661 swffont->glyph2ascii[t] = font->glyphs[t].unicode;
2662 if(swffont->glyph2ascii[t] == 0xffff || swffont->glyph2ascii[t] == 0x0000) {
2663 /* flash 8 flashtype requires unique unicode IDs for each character.
2664 We use the Unicode private user area to assign characters, hoping that
2665 the font doesn't contain more than 2048 glyphs */
2666 swffont->glyph2ascii[t] = 0xe000 + (t&0x1fff);
2669 if(font->glyphs[t].name) {
2670 swffont->glyphnames[t] = strdup(font->glyphs[t].name);
2672 swffont->glyphnames[t] = 0;
2674 advance = (int)(font->glyphs[t].advance);
2676 swf_Shape01DrawerInit(&draw, 0);
2677 line = font->glyphs[t].line;
2680 c.x = line->sx * GLYPH_SCALE; c.y = line->sy * GLYPH_SCALE;
2681 to.x = line->x * GLYPH_SCALE; to.y = line->y * GLYPH_SCALE;
2682 if(line->type == gfx_moveTo) {
2683 draw.moveTo(&draw, &to);
2684 } else if(line->type == gfx_lineTo) {
2685 draw.lineTo(&draw, &to);
2686 } else if(line->type == gfx_splineTo) {
2687 draw.splineTo(&draw, &c, &to);
2692 swffont->glyph[t].shape = swf_ShapeDrawerToShape(&draw);
2693 swffont->layout->bounds[t] = swf_ShapeDrawerGetBBox(&draw);
2695 int xmax = swffont->layout->bounds[t].xmax / 20;
2696 if(xmax>0 && xmax*2 < advance) {
2697 printf("fix bad advance value: bbox=%d, advance=%d (%f)\n", xmax, advance, font->glyphs[t].advance);
2701 if(advance<32768/20) {
2702 swffont->glyph[t].advance = advance*20;
2704 swffont->glyph[t].advance = 32767;
2707 draw.dealloc(&draw);
2709 swf_ExpandRect2(&bounds, &swffont->layout->bounds[t]);
2713 /* Flash player will use the advance value from the char, and the ascent/descent values
2714 from the layout for text selection.
2715 ascent will extend the char into negative y direction, from the baseline, while descent
2716 will extend in positive y direction, also from the baseline.
2717 The baseline is defined as the y-position zero
2720 swffont->layout->ascent = -bounds.ymin;
2721 if(swffont->layout->ascent < 0)
2722 swffont->layout->ascent = 0;
2723 swffont->layout->descent = bounds.ymax;
2724 if(swffont->layout->descent < 0)
2725 swffont->layout->descent = 0;
2726 swffont->layout->leading = bounds.ymax - bounds.ymin;
2731 static void swf_addfont(gfxdevice_t*dev, gfxfont_t*font)
2733 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
2735 if(i->swffont && i->swffont->name && !strcmp((char*)i->swffont->name,font->id))
2736 return; // the requested font is the current font
2738 fontlist_t*last=0,*l = i->fontlist;
2741 if(!strcmp((char*)l->swffont->name, font->id)) {
2742 return; // we already know this font
2746 l = (fontlist_t*)rfx_calloc(sizeof(fontlist_t));
2747 l->swffont = gfxfont_to_swffont(font, font->id);
2754 swf_FontSetID(l->swffont, getNewID(i->dev));
2756 if(getScreenLogLevel() >= LOGLEVEL_DEBUG) {
2758 // print font information
2759 msg("<debug> Font %s",font->id);
2760 msg("<debug> | ID: %d", l->swffont->id);
2761 msg("<debug> | Version: %d", l->swffont->version);
2762 msg("<debug> | Name: %s", l->swffont->name);
2763 msg("<debug> | Numchars: %d", l->swffont->numchars);
2764 msg("<debug> | Maxascii: %d", l->swffont->maxascii);
2765 msg("<debug> | Style: %d", l->swffont->style);
2766 msg("<debug> | Encoding: %d", l->swffont->encoding);
2767 for(iii=0; iii<l->swffont->numchars;iii++) {
2768 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,
2769 l->swffont->layout->bounds[iii].xmin/20.0,
2770 l->swffont->layout->bounds[iii].ymin/20.0,
2771 l->swffont->layout->bounds[iii].xmax/20.0,
2772 l->swffont->layout->bounds[iii].ymax/20.0
2775 for(t=0;t<l->swffont->maxascii;t++) {
2776 if(l->swffont->ascii2glyph[t] == iii)
2777 msg("<debug> | - maps to %d",t);
2783 static void swf_switchfont(gfxdevice_t*dev, const char*fontid)
2785 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
2787 if(i->swffont && i->swffont->name && !strcmp((char*)i->swffont->name,fontid))
2788 return; // the requested font is the current font
2790 fontlist_t*l = i->fontlist;
2792 if(!strcmp((char*)l->swffont->name, fontid)) {
2793 i->swffont = l->swffont;
2798 msg("<error> Unknown font id: %s", fontid);
2802 static void swf_drawchar(gfxdevice_t*dev, gfxfont_t*font, int glyph, gfxcolor_t*color, gfxmatrix_t*matrix)
2804 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
2806 msg("<error> swf_drawchar called (glyph %d) without font", glyph);
2809 if(!i->swffont || !i->swffont->name || strcmp((char*)i->swffont->name,font->id)) // not equal to current font
2811 /* TODO: remove the need for this (enhance getcharacterbbox so that it can cope
2812 with multiple fonts */
2814 swf_switchfont(dev, font->id); // set the current font
2817 msg("<warning> swf_drawchar: Font is NULL");
2820 if(glyph<0 || glyph>=i->swffont->numchars) {
2821 msg("<warning> No character %d in font %s (%d chars)", glyph, FIXNULL((char*)i->swffont->name), i->swffont->numchars);
2825 setfontscale(dev, matrix->m00, matrix->m01, matrix->m10, matrix->m11, matrix->tx, matrix->ty, 0);
2827 double det = i->fontmatrix.sx/65536.0 * i->fontmatrix.sy/65536.0 -
2828 i->fontmatrix.r0/65536.0 * i->fontmatrix.r1/65536.0;
2829 if(fabs(det) < 0.0005) {
2830 /* x direction equals y direction- the text is invisible */
2831 msg("<verbose> Not drawing invisible character character %d (det=%f, m=[%f %f;%f %f]\n", glyph,
2833 i->fontmatrix.sx/65536.0, i->fontmatrix.r1/65536.0,
2834 i->fontmatrix.r0/65536.0, i->fontmatrix.sy/65536.0);
2838 /*if(i->swffont->glyph[glyph].shape->bitlen <= 16) {
2839 msg("<warning> Glyph %d in current charset (%s, %d characters) is empty",
2840 glyph, FIXNULL((char*)i->swffont->name), i->swffont->numchars);
2844 /* calculate character position with respect to the current font matrix */
2845 double s = 20 * GLYPH_SCALE / det;
2846 double px = matrix->tx - i->fontmatrix.tx/20.0;
2847 double py = matrix->ty - i->fontmatrix.ty/20.0;
2848 int x = (SCOORD)(( px * i->fontmatrix.sy/65536.0 - py * i->fontmatrix.r1/65536.0)*s);
2849 int y = (SCOORD)((- px * i->fontmatrix.r0/65536.0 + py * i->fontmatrix.sx/65536.0)*s);
2850 if(x>32767 || x<-32768 || y>32767 || y<-32768) {
2851 msg("<verbose> Moving character origin to %f %f\n", matrix->tx, matrix->ty);
2853 setfontscale(dev, matrix->m00, matrix->m01, matrix->m10, matrix->m11, matrix->tx, matrix->ty, 1);
2854 /* since we just moved the char origin to the current char's position,
2855 it now has the relative position (0,0) */
2864 msg("<trace> Drawing char %d in font %d at %d,%d in color %02x%02x%02x%02x",
2865 glyph, i->swffont->id, x, y, color->r, color->g, color->b, color->a);
2867 putcharacter(dev, i->swffont->id, glyph, x, y, i->current_font_size, *(RGBA*)color);
2868 swf_FontUseGlyph(i->swffont, glyph);