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"
44 #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;
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=0;
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(0, 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) {
1323 swf_DeleteTag(swf, tag);
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;
1395 swf_DeleteTag(i->swf, tag);
1402 if(i->config_enablezlib || i->config_flashversion>=6) {
1403 i->swf->compressed = 1;
1406 /* Add AVM2 actionscript */
1407 if(i->config_flashversion>=9 &&
1408 (i->config_insertstoptag || i->hasbuttons)) {
1409 swf_AddButtonLinks(i->swf, i->config_insertstoptag,
1410 i->config_internallinkfunction||i->config_externallinkfunction);
1412 // if(i->config_reordertags)
1413 // swf_Optimize(i->swf);
1416 int swfresult_save(gfxresult_t*gfx, const char*filename)
1418 SWF*swf = (SWF*)gfx->internal;
1421 fi = open(filename, O_BINARY|O_CREAT|O_TRUNC|O_WRONLY, 0777);
1426 msg("<fatal> Could not create \"%s\". ", FIXNULL(filename));
1430 if FAILED(swf_WriteSWF(fi,swf))
1431 msg("<error> WriteSWF() failed.\n");
1437 void* swfresult_get(gfxresult_t*gfx, const char*name)
1439 SWF*swf = (SWF*)gfx->internal;
1440 if(!strcmp(name, "swf")) {
1441 return (void*)swf_CopySWF(swf);
1442 } else if(!strcmp(name, "xmin")) {
1443 return (void*)(swf->movieSize.xmin/20);
1444 } else if(!strcmp(name, "ymin")) {
1445 return (void*)(swf->movieSize.ymin/20);
1446 } else if(!strcmp(name, "xmax")) {
1447 return (void*)(swf->movieSize.xmax/20);
1448 } else if(!strcmp(name, "ymax")) {
1449 return (void*)(swf->movieSize.ymax/20);
1450 } else if(!strcmp(name, "width")) {
1451 return (void*)((swf->movieSize.xmax - swf->movieSize.xmin)/20);
1452 } else if(!strcmp(name, "height")) {
1453 return (void*)((swf->movieSize.ymax - swf->movieSize.ymin)/20);
1457 void swfresult_destroy(gfxresult_t*gfx)
1460 swf_FreeTags((SWF*)gfx->internal);
1461 free(gfx->internal);
1464 memset(gfx, 0, sizeof(gfxresult_t));
1468 static void swfoutput_destroy(gfxdevice_t* dev);
1470 gfxresult_t* swf_finish(gfxdevice_t* dev)
1472 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1475 if(i->config_linktarget) {
1476 free(i->config_linktarget);
1477 i->config_linktarget = 0;
1480 swfoutput_finalize(dev);
1481 SWF* swf = i->swf;i->swf = 0;
1482 swfoutput_destroy(dev);
1484 result = (gfxresult_t*)rfx_calloc(sizeof(gfxresult_t));
1485 result->internal = swf;
1486 result->save = swfresult_save;
1488 result->get = swfresult_get;
1489 result->destroy = swfresult_destroy;
1493 /* Perform cleaning up */
1494 static void swfoutput_destroy(gfxdevice_t* dev)
1496 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1498 /* not initialized yet- nothing to destroy */
1502 fontlist_t *tmp,*iterator = i->fontlist;
1504 if(iterator->swffont) {
1505 swf_FontFree(iterator->swffont);iterator->swffont=0;
1508 iterator = iterator->next;
1511 if(i->swf) {swf_FreeTags(i->swf);free(i->swf);i->swf = 0;}
1514 memset(dev, 0, sizeof(gfxdevice_t));
1517 static void swfoutput_setstrokecolor(gfxdevice_t* dev, U8 r, U8 g, U8 b, U8 a)
1519 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1520 if(i->strokergb.r == r &&
1521 i->strokergb.g == g &&
1522 i->strokergb.b == b &&
1523 i->strokergb.a == a) return;
1533 //#define ROUND_UP 19
1534 //#define ROUND_UP 10
1536 static void swfoutput_setlinewidth(gfxdevice_t*dev, double _linewidth)
1538 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1539 if(i->linewidth == (U16)(_linewidth*20+19.0/20.0))
1543 i->linewidth = (U16)(_linewidth*20+19.0/20.0);
1547 static void drawlink(gfxdevice_t*dev, ActionTAG*,ActionTAG*, gfxline_t*points, char mouseover, const char*url);
1548 static void swfoutput_namedlink(gfxdevice_t*dev, char*name, gfxline_t*points);
1549 static void swfoutput_linktopage(gfxdevice_t*dev, int page, gfxline_t*points);
1550 static void swfoutput_linktourl(gfxdevice_t*dev, const char*url, gfxline_t*points);
1552 /*void swfoutput_drawlink(gfxdevice_t*dev, char*url, gfxline_t*points)
1554 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1555 dev->drawlink(dev, points, url);
1558 void swf_drawlink(gfxdevice_t*dev, gfxline_t*points, const char*url)
1560 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1562 if(!strncmp("http://pdf2swf:", url, 15)) {
1563 char*tmp = strdup(url);
1564 int l = strlen(tmp);
1567 swfoutput_namedlink(dev, tmp+15, points);
1570 } else if(!strncmp("page", url, 4)) {
1573 if(url[t]<'0' || url[t]>'9')
1576 int page = atoi(&url[4]);
1577 if(page<0) page = 0;
1578 swfoutput_linktopage(dev, page, points);
1581 swfoutput_linktourl(dev, url, points);
1584 void swfoutput_linktourl(gfxdevice_t*dev, const char*url, gfxline_t*points)
1586 ActionTAG* actions = 0;
1587 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1593 /* TODO: escape special characters in url */
1595 if(i->config_externallinkfunction && i->config_flashversion<=8) {
1596 actions = action_PushString(actions, url); //parameter
1597 actions = action_PushInt(actions, 1); //number of parameters (1)
1598 actions = action_PushString(actions, i->config_externallinkfunction); //function name
1599 actions = action_CallFunction(actions);
1600 } else if(!i->config_linktarget) {
1601 if(!i->config_opennewwindow)
1602 actions = action_GetUrl(actions, url, "_parent");
1604 actions = action_GetUrl(actions, url, "_this");
1606 actions = action_GetUrl(actions, url, i->config_linktarget);
1608 actions = action_End(actions);
1610 drawlink(dev, actions, 0, points, 0, url);
1612 void swfoutput_linktopage(gfxdevice_t*dev, int page, gfxline_t*points)
1614 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1615 ActionTAG* actions = 0;
1622 if(!i->config_internallinkfunction || i->config_flashversion>=9) {
1623 actions = action_GotoFrame(actions, page-1);
1624 actions = action_End(actions);
1626 actions = action_PushInt(actions, page); //parameter
1627 actions = action_PushInt(actions, 1); //number of parameters (1)
1628 actions = action_PushString(actions, i->config_internallinkfunction); //function name
1629 actions = action_CallFunction(actions);
1630 actions = action_End(actions);
1634 sprintf(name, "page%d", page);
1636 drawlink(dev, actions, 0, points, 0, name);
1639 /* Named Links (a.k.a. Acrobatmenu) are used to implement various gadgets
1640 of the viewer objects, like subtitles, index elements etc.
1642 void swfoutput_namedlink(gfxdevice_t*dev, char*name, gfxline_t*points)
1644 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1645 ActionTAG *actions1,*actions2;
1646 char*tmp = strdup(name);
1654 if(!strncmp(tmp, "call:", 5))
1656 char*x = strchr(&tmp[5], ':');
1658 actions1 = action_PushInt(0, 0); //number of parameters (0)
1659 actions1 = action_PushString(actions1, &tmp[5]); //function name
1660 actions1 = action_CallFunction(actions1);
1661 actions1 = action_End(actions1);
1664 actions1 = action_PushString(0, x+1); //parameter
1665 actions1 = action_PushInt(actions1, 1); //number of parameters (1)
1666 actions1 = action_PushString(actions1, &tmp[5]); //function name
1667 actions1 = action_CallFunction(actions1);
1668 actions1 = action_End(actions1);
1670 actions2 = action_End(0);
1675 actions1 = action_PushString(0, "/:subtitle");
1676 actions1 = action_PushString(actions1, name);
1677 actions1 = action_SetVariable(actions1);
1678 actions1 = action_End(actions1);
1680 actions2 = action_PushString(0, "/:subtitle");
1681 actions2 = action_PushString(actions2, "");
1682 actions2 = action_SetVariable(actions2);
1683 actions2 = action_End(actions2);
1686 drawlink(dev, actions1, actions2, points, mouseover, name);
1688 swf_ActionFree(actions1);
1689 swf_ActionFree(actions2);
1693 static void drawgfxline(gfxdevice_t*dev, gfxline_t*line, int fill)
1695 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1696 gfxcoord_t lastx=0,lasty=0,px=0,py=0;
1698 int lines= 0, splines=0;
1705 /* check whether the next segment is zero */
1706 if(line->type == gfx_moveTo) {
1707 moveto(dev, i->tag, line->x, line->y);
1708 px = lastx = line->x;
1709 py = lasty = line->y;
1711 } if(line->type == gfx_lineTo) {
1712 lineto(dev, i->tag, line->x, line->y);
1717 } else if(line->type == gfx_splineTo) {
1719 s.x = line->sx;p.x = line->x;
1720 s.y = line->sy;p.y = line->y;
1721 splineto(dev, i->tag, s, p);
1729 msg("<trace> drawgfxline, %d lines, %d splines", lines, splines);
1733 static void drawlink(gfxdevice_t*dev, ActionTAG*actions1, ActionTAG*actions2, gfxline_t*points, char mouseover, const char*url)
1735 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1744 int buttonid = getNewID(dev);
1745 gfxbbox_t bbox = gfxline_getbbox(points);
1750 myshapeid = getNewID(dev);
1751 i->tag = swf_InsertTag(i->tag,ST_DEFINESHAPE3);
1752 swf_ShapeNew(&i->shape);
1753 rgb.r = rgb.b = rgb.a = rgb.g = 0;
1754 fsid = swf_ShapeAddSolidFillStyle(i->shape,&rgb);
1755 swf_SetU16(i->tag, myshapeid);
1756 r.xmin = (int)(bbox.xmin*20);
1757 r.ymin = (int)(bbox.ymin*20);
1758 r.xmax = (int)(bbox.xmax*20);
1759 r.ymax = (int)(bbox.ymax*20);
1760 r = swf_ClipRect(i->pagebbox, r);
1761 swf_SetRect(i->tag,&r);
1762 swf_SetShapeStyles(i->tag,i->shape);
1763 swf_ShapeCountBits(i->shape,NULL,NULL);
1764 swf_SetShapeBits(i->tag,i->shape);
1765 swf_ShapeSetAll(i->tag,i->shape,/*x*/0,/*y*/0,0,fsid,0);
1766 i->swflastx = i->swflasty = 0;
1767 drawgfxline(dev, points, 1);
1768 swf_ShapeSetEnd(i->tag);
1771 myshapeid2 = getNewID(dev);
1772 i->tag = swf_InsertTag(i->tag,ST_DEFINESHAPE3);
1773 swf_ShapeNew(&i->shape);
1775 rgb = i->config_linkcolor;
1777 fsid = swf_ShapeAddSolidFillStyle(i->shape,&rgb);
1778 swf_SetU16(i->tag, myshapeid2);
1779 r.xmin = (int)(bbox.xmin*20);
1780 r.ymin = (int)(bbox.ymin*20);
1781 r.xmax = (int)(bbox.xmax*20);
1782 r.ymax = (int)(bbox.ymax*20);
1783 r = swf_ClipRect(i->pagebbox, r);
1784 swf_SetRect(i->tag,&r);
1785 swf_SetShapeStyles(i->tag,i->shape);
1786 swf_ShapeCountBits(i->shape,NULL,NULL);
1787 swf_SetShapeBits(i->tag,i->shape);
1788 swf_ShapeSetAll(i->tag,i->shape,/*x*/0,/*y*/0,0,fsid,0);
1789 i->swflastx = i->swflasty = 0;
1790 drawgfxline(dev, points, 1);
1791 swf_ShapeSetEnd(i->tag);
1795 i->tag = swf_InsertTag(i->tag,ST_DEFINEBUTTON);
1796 swf_SetU16(i->tag,buttonid); //id
1797 swf_ButtonSetFlags(i->tag, 0); //menu=no
1798 swf_ButtonSetRecord(i->tag,0x01,myshapeid,i->depth,0,0);
1799 swf_ButtonSetRecord(i->tag,0x02,myshapeid2,i->depth,0,0);
1800 swf_ButtonSetRecord(i->tag,0x04,myshapeid2,i->depth,0,0);
1801 swf_ButtonSetRecord(i->tag,0x08,myshapeid,i->depth,0,0);
1802 swf_SetU8(i->tag,0);
1803 swf_ActionSet(i->tag,actions1);
1804 swf_SetU8(i->tag,0);
1808 i->tag = swf_InsertTag(i->tag,ST_DEFINEBUTTON2);
1809 swf_SetU16(i->tag,buttonid); //id
1810 swf_ButtonSetFlags(i->tag, 0); //menu=no
1811 swf_ButtonSetRecord(i->tag,0x01,myshapeid,i->depth,0,0);
1812 swf_ButtonSetRecord(i->tag,0x02,myshapeid2,i->depth,0,0);
1813 swf_ButtonSetRecord(i->tag,0x04,myshapeid2,i->depth,0,0);
1814 swf_ButtonSetRecord(i->tag,0x08,myshapeid,i->depth,0,0);
1815 swf_SetU8(i->tag,0); // end of button records
1816 swf_ButtonSetCondition(i->tag, BC_IDLE_OVERUP);
1817 swf_ActionSet(i->tag,actions1);
1819 swf_ButtonSetCondition(i->tag, BC_OVERUP_IDLE);
1820 swf_ActionSet(i->tag,actions2);
1821 swf_SetU8(i->tag,0);
1822 swf_ButtonPostProcess(i->tag, 2);
1824 swf_SetU8(i->tag,0);
1825 swf_ButtonPostProcess(i->tag, 1);
1829 const char* name = 0;
1830 if(i->config_linknameurl) {
1834 sprintf(buf, "button%d", buttonid);
1837 msg("<trace> Placing link ID %d", buttonid);
1838 i->tag = swf_InsertTag(i->tag,ST_PLACEOBJECT2);
1840 if(posx!=0 || posy!=0) {
1842 p.x = (int)(posx*20);
1843 p.y = (int)(posy*20);
1844 p = swf_TurnPoint(p, &i->page_matrix);
1849 swf_ObjectPlace(i->tag, buttonid, getNewDepth(dev),&m,0,name);
1851 swf_ObjectPlace(i->tag, buttonid, getNewDepth(dev),&i->page_matrix,0,name);
1858 for(t=0;t<picpos;t++)
1860 if(pic_xids[t] == xid &&
1861 pic_yids[t] == yid) {
1862 width = pic_width[t];
1863 height = pic_height[t];
1867 pic_ids[picpos] = swfoutput_drawimagelosslessN(&output, pic, pal, width, height, x1,y1,x2,y2,x3,y3,x4,y4, numpalette);
1868 pic_xids[picpos] = xid;
1869 pic_yids[picpos] = yid;
1870 pic_width[picpos] = width;
1871 pic_height[picpos] = height;
1874 pic[width*y+x] = buf[0];
1878 xid += pal[1].r*3 + pal[1].g*11 + pal[1].b*17;
1879 yid += pal[1].r*7 + pal[1].g*5 + pal[1].b*23;
1883 xid += x*r+x*b*3+x*g*7+x*a*11;
1884 yid += y*r*3+y*b*17+y*g*19+y*a*11;
1886 for(t=0;t<picpos;t++)
1888 if(pic_xids[t] == xid &&
1889 pic_yids[t] == yid) {
1898 int swf_setparameter(gfxdevice_t*dev, const char*name, const char*value)
1900 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1902 msg("<trace> swfdevice: %s=%s", name, value);
1903 if(!strcmp(name, "jpegsubpixels")) {
1904 i->config_jpegsubpixels = atof(value);
1905 } else if(!strcmp(name, "ppmsubpixels")) {
1906 i->config_ppmsubpixels = atof(value);
1907 } else if(!strcmp(name, "subpixels")) {
1908 i->config_ppmsubpixels = i->config_jpegsubpixels = atof(value);
1909 } else if(!strcmp(name, "drawonlyshapes")) {
1910 i->config_drawonlyshapes = atoi(value);
1911 } else if(!strcmp(name, "ignoredraworder")) {
1912 i->config_ignoredraworder = atoi(value);
1913 } else if(!strcmp(name, "mark")) {
1914 if(!value || !value[0]) {
1915 if(i->mark) free(i->mark);
1919 i->mark = strdup("...");
1920 for(t=0;t<3;t++) if(value[t]) i->mark[t] = value[t];
1922 } else if(!strcmp(name, "filloverlap")) {
1923 i->config_filloverlap = atoi(value);
1924 } else if(!strcmp(name, "linksopennewwindow")) {
1925 i->config_opennewwindow = atoi(value);
1926 } else if(!strcmp(name, "opennewwindow")) {
1927 i->config_opennewwindow = atoi(value);
1928 } else if(!strcmp(name, "storeallcharacters")) {
1929 i->config_storeallcharacters = atoi(value);
1930 } else if(!strcmp(name, "enablezlib")) {
1931 i->config_enablezlib = atoi(value);
1932 } else if(!strcmp(name, "bboxvars")) {
1933 i->config_bboxvars = atoi(value);
1934 } else if(!strcmp(name, "frameresets")) {
1935 i->config_frameresets = atoi(value);
1936 } else if(!strcmp(name, "showclipshapes")) {
1937 i->config_showclipshapes = atoi(value);
1938 } else if(!strcmp(name, "reordertags")) {
1939 i->config_reordertags = atoi(value);
1940 } else if(!strcmp(name, "internallinkfunction")) {
1941 i->config_internallinkfunction = strdup(value);
1942 } else if(!strcmp(name, "externallinkfunction")) {
1943 i->config_externallinkfunction = strdup(value);
1944 } else if(!strcmp(name, "linkfunction")) { //sets both internallinkfunction and externallinkfunction
1945 i->config_internallinkfunction = strdup(value);
1946 i->config_externallinkfunction = strdup(value);
1947 } else if(!strcmp(name, "disable_polygon_conversion")) {
1948 i->config_disable_polygon_conversion = atoi(value);
1949 } else if(!strcmp(name, "normalize_polygon_positions")) {
1950 i->config_normalize_polygon_positions = atoi(value);
1951 } else if(!strcmp(name, "wxwindowparams")) {
1952 i->config_watermark = atoi(value);
1953 } else if(!strcmp(name, "insertstop")) {
1954 i->config_insertstoptag = atoi(value);
1955 } else if(!strcmp(name, "protect")) {
1956 i->config_protect = atoi(value);
1957 if(i->config_protect && i->tag) {
1958 i->tag = swf_InsertTag(i->tag, ST_PROTECT);
1960 } else if(!strcmp(name, "flashversion")) {
1961 i->config_flashversion = atoi(value);
1963 i->swf->fileVersion = i->config_flashversion;
1965 } else if(!strcmp(name, "framerate")) {
1966 i->config_framerate = atof(value);
1968 i->swf->frameRate = i->config_framerate*0x100;
1970 } else if(!strcmp(name, "minlinewidth")) {
1971 i->config_minlinewidth = atof(value);
1972 } else if(!strcmp(name, "caplinewidth")) {
1973 i->config_caplinewidth = atof(value);
1974 } else if(!strcmp(name, "linktarget")) {
1975 i->config_linktarget = strdup(value);
1976 } else if(!strcmp(name, "dumpfonts")) {
1977 i->config_dumpfonts = atoi(value);
1978 } else if(!strcmp(name, "animate")) {
1979 i->config_animate = atoi(value);
1980 } else if(!strcmp(name, "simpleviewer")) {
1981 i->config_simpleviewer = atoi(value);
1982 } else if(!strcmp(name, "next_bitmap_is_jpeg")) {
1984 } else if(!strcmp(name, "jpegquality")) {
1985 int val = atoi(value);
1987 if(val>101) val=101;
1988 i->config_jpegquality = val;
1989 } else if(!strcmp(name, "splinequality")) {
1990 int v = atoi(value);
1991 v = 500-(v*5); // 100% = 0.25 pixel, 0% = 25 pixel
1993 i->config_splinemaxerror = v;
1994 } else if(!strcmp(name, "fontquality")) {
1995 int v = atoi(value);
1996 v = 500-(v*5); // 100% = 0.25 pixel, 0% = 25 pixel
1998 i->config_fontsplinemaxerror = v;
1999 } else if(!strcmp(name, "linkcolor")) {
2000 if(strlen(value)!=8) {
2001 fprintf(stderr, "Unknown format for option 'linkcolor'. (%s <-> RRGGBBAA)\n", value);
2004 # define NIBBLE(s) (((s)>='0' && (s)<='9')?((s)-'0'):((s)&0x0f)+9)
2005 i->config_linkcolor.r = NIBBLE(value[0])<<4 | NIBBLE(value[1]);
2006 i->config_linkcolor.g = NIBBLE(value[2])<<4 | NIBBLE(value[3]);
2007 i->config_linkcolor.b = NIBBLE(value[4])<<4 | NIBBLE(value[5]);
2008 i->config_linkcolor.a = NIBBLE(value[6])<<4 | NIBBLE(value[7]);
2009 } else if(!strcmp(name, "help")) {
2010 printf("\nSWF layer options:\n");
2011 printf("jpegsubpixels=<pixels> resolution adjustment for jpeg images (same as jpegdpi, but in pixels)\n");
2012 printf("ppmsubpixels=<pixels resolution adjustment for lossless images (same as ppmdpi, but in pixels)\n");
2013 printf("subpixels=<pixels> shortcut for setting both jpegsubpixels and ppmsubpixels\n");
2014 printf("drawonlyshapes convert everything to shapes (currently broken)\n");
2015 printf("ignoredraworder allow to perform a few optimizations for creating smaller SWFs\n");
2016 printf("linksopennewwindow make links open a new browser window\n");
2017 printf("linktarget target window name of new links\n");
2018 printf("linkcolor=<color) color of links (format: RRGGBBAA)\n");
2019 printf("linknameurl Link buttons will be named like the URL they refer to (handy for iterating through links with actionscript)\n");
2020 printf("storeallcharacters don't reduce the fonts to used characters in the output file\n");
2021 printf("enablezlib switch on zlib compression (also done if flashversion>=7)\n");
2022 printf("bboxvars store the bounding box of the SWF file in actionscript variables\n");
2023 printf("reordertags=0/1 (default: 1) perform some tag optimizations\n");
2024 printf("internallinkfunction=<name> when the user clicks a internal link (to a different page) in the converted file, this actionscript function is called\n");
2025 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");
2026 printf("disable_polygon_conversion never convert strokes to polygons (will remove capstyles and joint styles)\n");
2027 printf("caplinewidth=<width> the minimum thichness a line needs to have so that capstyles become visible (and are converted)\n");
2028 printf("insertstop put an ActionScript \"STOP\" tag in every frame\n");
2029 printf("protect add a \"protect\" tag to the file, to prevent loading in the Flash editor\n");
2030 printf("flashversion=<version> the SWF fileversion (6)\n");
2031 printf("framerate=<fps> SWF framerate\n");
2032 printf("minlinewidth=<width> convert horizontal/vertical boxes smaller than this width to lines (0.05) \n");
2033 printf("simpleviewer Add next/previous buttons to the SWF\n");
2034 printf("animate insert a showframe tag after each placeobject (animate draw order of PDF files)\n");
2035 printf("jpegquality=<quality> set compression quality of jpeg images\n");
2036 printf("splinequality=<value> Set the quality of spline convertion to value (0-100, default: 100).\n");
2043 // --------------------------------------------------------------------
2045 static CXFORM gfxcxform_to_cxform(gfxcxform_t* c)
2048 swf_GetCXForm(0, &cx, 1);
2051 if(c->rg!=0 || c->rb!=0 || c->ra!=0 ||
2052 c->gr!=0 || c->gb!=0 || c->ga!=0 ||
2053 c->br!=0 || c->bg!=0 || c->ba!=0 ||
2054 c->ar!=0 || c->ag!=0 || c->ab!=0)
2055 msg("<warning> CXForm not SWF-compatible");
2057 cx.a0 = (S16)(c->aa*256);
2058 cx.r0 = (S16)(c->rr*256);
2059 cx.g0 = (S16)(c->gg*256);
2060 cx.b0 = (S16)(c->bb*256);
2069 static int imageInCache(gfxdevice_t*dev, void*data, int width, int height)
2073 static void addImageToCache(gfxdevice_t*dev, void*data, int width, int height)
2077 static int add_image(swfoutput_internal*i, gfximage_t*img, int targetwidth, int targetheight, int* newwidth, int* newheight)
2079 gfxdevice_t*dev = i->dev;
2081 RGBA*mem = (RGBA*)img->data;
2083 int sizex = img->width;
2084 int sizey = img->height;
2085 int is_jpeg = i->jpeg;
2088 int newsizex=sizex, newsizey=sizey;
2091 if(is_jpeg && i->config_jpegsubpixels) {
2092 newsizex = (int)(targetwidth*i->config_jpegsubpixels + 0.5);
2093 newsizey = (int)(targetheight*i->config_jpegsubpixels + 0.5);
2094 } else if(!is_jpeg && i->config_ppmsubpixels) {
2095 newsizex = (int)(targetwidth*i->config_ppmsubpixels + 0.5);
2096 newsizey = (int)(targetheight*i->config_ppmsubpixels + 0.5);
2100 if(sizex<=0 || sizey<=0)
2107 /* TODO: cache images */
2109 if(newsizex<sizex || newsizey<sizey) {
2110 msg("<verbose> Scaling %dx%d image to %dx%d", sizex, sizey, newsizex, newsizey);
2111 newpic = swf_ImageScale(mem, sizex, sizey, newsizex, newsizey);
2112 *newwidth = sizex = newsizex;
2113 *newheight = sizey = newsizey;
2116 *newwidth = newsizex = sizex;
2117 *newheight = newsizey = sizey;
2120 int num_colors = swf_ImageGetNumberOfPaletteEntries(mem,sizex,sizey,0);
2121 int has_alpha = swf_ImageHasAlpha(mem,sizex,sizey);
2123 msg("<verbose> Drawing %dx%d %s%simage (id %d) at size %dx%d (%dx%d), %s%d colors",
2125 has_alpha?(has_alpha==2?"semi-transparent ":"transparent "):"",
2126 is_jpeg?"jpeg-":"", i->currentswfid+1,
2128 targetwidth, targetheight,
2129 /*newsizex, newsizey,*/
2130 num_colors>256?">":"", num_colors>256?256:num_colors);
2132 /*RGBA* pal = (RGBA*)rfx_alloc(sizeof(RGBA)*num_colors);
2133 swf_ImageGetNumberOfPaletteEntries(mem,sizex,sizey,pal);
2135 for(t=0;t<num_colors;t++) {
2136 printf("%02x%02x%02x%02x ",
2137 pal[t].r, pal[t].g, pal[t].b, pal[t].a);
2144 int cacheid = imageInCache(dev, mem, sizex, sizey);
2147 bitid = getNewID(dev);
2149 i->tag = swf_AddImage(i->tag, bitid, mem, sizex, sizey, i->config_jpegquality);
2150 addImageToCache(dev, mem, sizex, sizey);
2160 static SRECT gfxline_getSWFbbox(gfxline_t*line)
2162 gfxbbox_t bbox = gfxline_getbbox(line);
2164 r.xmin = (int)(bbox.xmin*20);
2165 r.ymin = (int)(bbox.ymin*20);
2166 r.xmax = (int)(bbox.xmax*20);
2167 r.ymax = (int)(bbox.ymax*20);
2171 int line_is_empty(gfxline_t*line)
2174 if(line->type != gfx_moveTo)
2181 static void swf_fillbitmap(gfxdevice_t*dev, gfxline_t*line, gfximage_t*img, gfxmatrix_t*matrix, gfxcxform_t*cxform)
2183 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
2185 if(line_is_empty(line))
2191 int targetx = (int)(sqrt(matrix->m00*matrix->m00 + matrix->m01*matrix->m01)*img->width);
2192 int targety = (int)(sqrt(matrix->m10*matrix->m10 + matrix->m11*matrix->m11)*img->height);
2194 int newwidth=0,newheight=0;
2195 int bitid = add_image(i, img, targetx, targety, &newwidth, &newheight);
2198 double fx = (double)img->width / (double)newwidth;
2199 double fy = (double)img->height / (double)newheight;
2202 m.sx = (int)(65536*20*matrix->m00*fx); m.r1 = (int)(65536*20*matrix->m10*fy);
2203 m.r0 = (int)(65536*20*matrix->m01*fx); m.sy = (int)(65536*20*matrix->m11*fy);
2204 m.tx = (int)(matrix->tx*20);
2205 m.ty = (int)(matrix->ty*20);
2208 int myshapeid = getNewID(dev);
2209 i->tag = swf_InsertTag(i->tag,ST_DEFINESHAPE);
2211 swf_ShapeNew(&shape);
2212 int fsid = swf_ShapeAddBitmapFillStyle(shape,&m,bitid,1);
2213 swf_SetU16(i->tag, myshapeid);
2214 SRECT r = gfxline_getSWFbbox(line);
2215 r = swf_ClipRect(i->pagebbox, r);
2216 swf_SetRect(i->tag,&r);
2217 swf_SetShapeStyles(i->tag,shape);
2218 swf_ShapeCountBits(shape,NULL,NULL);
2219 swf_SetShapeBits(i->tag,shape);
2220 swf_ShapeSetAll(i->tag,shape,UNDEFINED_COORD,UNDEFINED_COORD,0,fsid,0);
2221 i->swflastx = i->swflasty = UNDEFINED_COORD;
2222 drawgfxline(dev, line, 1);
2223 swf_ShapeSetEnd(i->tag);
2224 swf_ShapeFree(shape);
2226 msg("<trace> Placing image, shape ID %d, bitmap ID %d", myshapeid, bitid);
2227 i->tag = swf_InsertTag(i->tag,ST_PLACEOBJECT2);
2228 CXFORM cxform2 = gfxcxform_to_cxform(cxform);
2229 swf_ObjectPlace(i->tag,myshapeid,getNewDepth(dev),&i->page_matrix,&cxform2,NULL);
2232 static RGBA col_black = {255,0,0,0};
2234 static void drawoutline(gfxdevice_t*dev, gfxline_t*line)
2236 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
2238 int myshapeid = getNewID(dev);
2239 i->tag = swf_InsertTag(i->tag,ST_DEFINESHAPE3);
2242 swf_ShapeNew(&shape);
2243 int lsid = swf_ShapeAddLineStyle(shape,1,&col_black);
2245 swf_SetU16(i->tag,myshapeid);
2246 SRECT r = gfxline_getSWFbbox(line);
2247 r = swf_ClipRect(i->pagebbox, r);
2248 swf_SetRect(i->tag,&r);
2249 swf_SetShapeStyles(i->tag,shape);
2250 swf_ShapeCountBits(shape,NULL,NULL);
2251 swf_SetShapeBits(i->tag,shape);
2252 swf_ShapeSetAll(i->tag,shape,UNDEFINED_COORD,UNDEFINED_COORD,lsid,0,0);
2253 drawgfxline(dev, line, 1);
2254 swf_ShapeSetEnd(i->tag);
2255 swf_ShapeFree(shape);
2257 i->tag = swf_InsertTag(i->tag,ST_PLACEOBJECT2);
2258 swf_ObjectPlace(i->tag, myshapeid, getNewDepth(dev), 0,0,0);
2261 static void swf_startclip(gfxdevice_t*dev, gfxline_t*line)
2263 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
2268 if(i->clippos >= 127)
2270 msg("<warning> Too many clip levels.");
2274 if(i->config_showclipshapes)
2275 drawoutline(dev, line);
2277 int myshapeid = getNewID(dev);
2278 i->tag = swf_InsertTag(i->tag,ST_DEFINESHAPE3);
2280 memset(&col, 0, sizeof(RGBA));
2283 swf_ShapeNew(&shape);
2284 int fsid = swf_ShapeAddSolidFillStyle(shape,&col);
2286 RGBA markcol = {0,i->mark[0],i->mark[1],i->mark[2]};
2287 swf_ShapeAddSolidFillStyle(shape,&markcol);
2289 swf_SetU16(i->tag,myshapeid);
2290 SRECT r = gfxline_getSWFbbox(line);
2291 r = swf_ClipRect(i->pagebbox, r);
2292 swf_SetRect(i->tag,&r);
2293 swf_SetShapeStyles(i->tag,shape);
2294 swf_ShapeCountBits(shape,NULL,NULL);
2295 swf_SetShapeBits(i->tag,shape);
2296 swf_ShapeSetAll(i->tag,shape,UNDEFINED_COORD,UNDEFINED_COORD,0,fsid,0);
2297 i->swflastx = i->swflasty = UNDEFINED_COORD;
2298 i->shapeisempty = 1;
2299 drawgfxline(dev, line, 1);
2300 if(i->shapeisempty) {
2301 /* an empty clip shape is equivalent to a shape with no area */
2302 int x = line?line->x:0;
2303 int y = line?line->y:0;
2304 moveto(dev, i->tag, x,y);
2305 lineto(dev, i->tag, x,y);
2306 lineto(dev, i->tag, x,y);
2308 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)) {
2309 if(i->config_watermark) {
2310 gfxbbox_t r; r.xmin = r.ymin = 0;r.xmax = i->max_x;r.ymax = i->max_y;
2311 draw_watermark(dev, r, 1);
2314 swf_ShapeSetEnd(i->tag);
2315 swf_ShapeFree(shape);
2317 /* TODO: remember the bbox, and check all shapes against it */
2319 msg("<trace> Placing clip ID %d", myshapeid);
2320 i->tag = swf_InsertTag(i->tag,ST_PLACEOBJECT2);
2321 i->cliptags[i->clippos] = i->tag;
2322 i->clipshapes[i->clippos] = myshapeid;
2323 i->clipdepths[i->clippos] = getNewDepth(dev);
2327 static void swf_endclip(gfxdevice_t*dev)
2329 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
2336 msg("<error> Invalid end of clipping region");
2340 /*swf_ObjectPlaceClip(i->cliptags[i->clippos],i->clipshapes[i->clippos],i->clipdepths[i->clippos],&i->page_matrix,NULL,NULL,
2341 / * clip to depth: * / i->depth <= i->clipdepths[i->clippos]? i->depth : i->depth - 1);
2343 swf_ObjectPlaceClip(i->cliptags[i->clippos],i->clipshapes[i->clippos],i->clipdepths[i->clippos],&i->page_matrix,NULL,NULL,i->depth);
2345 static int gfxline_type(gfxline_t*line)
2351 int haszerosegments=0;
2354 if(line->type == gfx_moveTo) {
2357 } else if(line->type == gfx_lineTo) {
2361 } else if(line->type == gfx_splineTo) {
2363 if(tmpsplines>lines)
2371 if(lines==0 && splines==0) return 0;
2372 else if(lines==1 && splines==0) return 1;
2373 else if(lines==0 && splines==1) return 2;
2374 else if(splines==0) return 3;
2378 static int gfxline_has_dots(gfxline_t*line)
2386 if(line->type == gfx_moveTo) {
2387 /* test the length of the preceding line, and assume it is a dot if
2388 it's length is less than 1.0. But *only* if there's a noticable
2389 gap between the previous line and the next moveTo. (I've come
2390 across a PDF where thousands of "dots" were stringed together,
2392 int last_short_gap = short_gap;
2393 if((fabs(line->x - x) + fabs(line->y - y)) < 1.0) {
2398 if(isline && dist < 1 && !short_gap && !last_short_gap) {
2403 } else if(line->type == gfx_lineTo) {
2404 dist += fabs(line->x - x) + fabs(line->y - y);
2406 } else if(line->type == gfx_splineTo) {
2407 dist += fabs(line->sx - x) + fabs(line->sy - y) +
2408 fabs(line->x - line->sx) + fabs(line->y - line->sy);
2415 if(isline && dist < 1 && !short_gap) {
2421 static int gfxline_fix_short_edges(gfxline_t*line)
2425 if(line->type == gfx_lineTo) {
2426 if(fabs(line->x - x) + fabs(line->y - y) < 0.01) {
2429 } else if(line->type == gfx_splineTo) {
2430 if(fabs(line->sx - x) + fabs(line->sy - y) +
2431 fabs(line->x - line->sx) + fabs(line->y - line->sy) < 0.01) {
2442 static char is_inside_page(gfxdevice_t*dev, gfxcoord_t x, gfxcoord_t y)
2444 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
2445 if(x<i->min_x || x>i->max_x) return 0;
2446 if(y<i->min_y || y>i->max_y) return 0;
2450 gfxline_t* gfxline_move(gfxline_t*line, double x, double y)
2452 gfxline_t*l = line = gfxline_clone(line);
2464 //#define NORMALIZE_POLYGON_POSITIONS
2466 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)
2468 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
2469 if(line_is_empty(line))
2471 int type = gfxline_type(line);
2472 int has_dots = gfxline_has_dots(line);
2473 gfxbbox_t r = gfxline_getbbox(line);
2474 int is_outside_page = !is_inside_page(dev, r.xmin, r.ymin) || !is_inside_page(dev, r.xmax, r.ymax);
2476 /* TODO: * split line into segments, and perform this check for all segments */
2478 if(i->config_disable_polygon_conversion || type>=5 ||
2480 (width <= i->config_caplinewidth
2481 || (cap_style == gfx_capRound && joint_style == gfx_joinRound)
2482 || (cap_style == gfx_capRound && type<=2)))) {} else
2484 /* convert line to polygon */
2485 msg("<trace> draw as polygon, type=%d dots=%d", type, has_dots);
2487 gfxline_fix_short_edges(line);
2488 /* we need to convert the line into a polygon */
2489 gfxpoly_t* poly = gfxpoly_strokeToPoly(line, width, cap_style, joint_style, miterLimit);
2490 gfxline_t*gfxline = gfxpoly_to_gfxline(poly);
2491 dev->fill(dev, gfxline, color);
2492 gfxline_free(gfxline);
2497 msg("<trace> draw as stroke, type=%d dots=%d", type, has_dots);
2500 if(i->config_normalize_polygon_positions) {
2502 double startx = 0, starty = 0;
2503 if(line && line->type == gfx_moveTo) {
2507 line = gfxline_move(line, -startx, -starty);
2508 i->shapeposx = (int)(startx*20);
2509 i->shapeposy = (int)(starty*20);
2512 swfoutput_setstrokecolor(dev, color->r, color->g, color->b, color->a);
2513 swfoutput_setlinewidth(dev, width);
2516 drawgfxline(dev, line, 0);
2518 if(i->config_normalize_polygon_positions) {
2519 free(line); //account for _move
2524 static void swf_fill(gfxdevice_t*dev, gfxline_t*line, gfxcolor_t*color)
2526 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
2527 if(line_is_empty(line))
2531 gfxbbox_t r = gfxline_getbbox(line);
2532 int is_outside_page = !is_inside_page(dev, r.xmin, r.ymin) || !is_inside_page(dev, r.xmax, r.ymax);
2534 //if(i->chardatapos && i->chardata[i->chardatapos-1].color.a) {
2537 if(!i->config_ignoredraworder)
2540 if(i->config_normalize_polygon_positions) {
2542 double startx = 0, starty = 0;
2543 if(line && line->type == gfx_moveTo) {
2547 line = gfxline_move(line, -startx, -starty);
2548 i->shapeposx = (int)(startx*20);
2549 i->shapeposy = (int)(starty*20);
2552 swfoutput_setfillcolor(dev, color->r, color->g, color->b, color->a);
2555 drawgfxline(dev, line, 1);
2557 if(i->currentswfid==2 && r.xmin==0 && r.ymin==0 && r.xmax==i->max_x && r.ymax==i->max_y) {
2558 if(i->config_watermark) {
2559 draw_watermark(dev, r, 1);
2563 msg("<trace> end of swf_fill (shapeid=%d)", i->shapeid);
2565 if(i->config_normalize_polygon_positions) {
2566 free(line); //account for _move
2570 static GRADIENT* gfxgradient_to_GRADIENT(gfxgradient_t*gradient)
2573 gfxgradient_t*g = gradient;
2578 GRADIENT* swfgradient = malloc(sizeof(GRADIENT));
2579 swfgradient->num = num;
2580 swfgradient->rgba = malloc(sizeof(swfgradient->rgba[0])*num);
2581 swfgradient->ratios = malloc(sizeof(swfgradient->ratios[0])*num);
2586 swfgradient->ratios[num] = g->pos*255;
2587 swfgradient->rgba[num] = *(RGBA*)&g->color;
2594 static void swf_fillgradient(gfxdevice_t*dev, gfxline_t*line, gfxgradient_t*gradient, gfxgradienttype_t type, gfxmatrix_t*matrix)
2596 if(line_is_empty(line))
2598 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
2600 if(line_is_empty(line))
2603 GRADIENT* swfgradient = gfxgradient_to_GRADIENT(gradient);
2610 double f = type==gfxgradient_radial?4:4;
2612 m.sx = (int)(matrix->m00*20*f); m.r1 = (int)(matrix->m10*20*f);
2613 m.r0 = (int)(matrix->m01*20*f); m.sy = (int)(matrix->m11*20*f);
2614 m.tx = (int)(matrix->tx*20);
2615 m.ty = (int)(matrix->ty*20);
2618 int myshapeid = getNewID(dev);
2619 i->tag = swf_InsertTag(i->tag, ST_DEFINESHAPE2);
2621 swf_ShapeNew(&shape);
2622 int fsid = swf_ShapeAddGradientFillStyle(shape,&m,swfgradient,type==gfxgradient_radial);
2623 swf_SetU16(i->tag, myshapeid);
2624 SRECT r = gfxline_getSWFbbox(line);
2625 r = swf_ClipRect(i->pagebbox, r);
2626 swf_SetRect(i->tag,&r);
2627 swf_SetShapeStyles(i->tag,shape);
2628 swf_ShapeCountBits(shape,NULL,NULL);
2629 swf_SetShapeBits(i->tag,shape);
2630 swf_ShapeSetAll(i->tag,shape,UNDEFINED_COORD,UNDEFINED_COORD,0,fsid,0);
2631 i->swflastx = i->swflasty = UNDEFINED_COORD;
2632 drawgfxline(dev, line, 1);
2633 swf_ShapeSetEnd(i->tag);
2634 swf_ShapeFree(shape);
2636 int depth = getNewDepth(dev);
2637 msg("<trace> Placing gradient, shape ID %d, depth %d", myshapeid, depth);
2638 i->tag = swf_InsertTag(i->tag,ST_PLACEOBJECT2);
2639 swf_ObjectPlace(i->tag,myshapeid,depth,&i->page_matrix,NULL,NULL);
2641 swf_FreeGradient(swfgradient);free(swfgradient);
2644 static SWFFONT* gfxfont_to_swffont(gfxfont_t*font, const char* id)
2646 SWFFONT*swffont = (SWFFONT*)rfx_calloc(sizeof(SWFFONT));
2648 SRECT bounds = {0,0,0,0};
2650 swffont->version = 2;
2651 swffont->name = (U8*)strdup(id);
2652 swffont->layout = (SWFLAYOUT*)rfx_calloc(sizeof(SWFLAYOUT));
2653 swffont->layout->ascent = 0;
2654 swffont->layout->descent = 0;
2655 swffont->layout->leading = 0;
2656 swffont->layout->bounds = (SRECT*)rfx_calloc(sizeof(SRECT)*font->num_glyphs);
2657 swffont->encoding = FONT_ENCODING_UNICODE;
2658 swffont->numchars = font->num_glyphs;
2659 swffont->maxascii = font->max_unicode;
2660 swffont->ascii2glyph = (int*)rfx_calloc(sizeof(int)*swffont->maxascii);
2661 swffont->glyph2ascii = (U16*)rfx_calloc(sizeof(U16)*swffont->numchars);
2662 swffont->glyph = (SWFGLYPH*)rfx_calloc(sizeof(SWFGLYPH)*swffont->numchars);
2663 swffont->glyphnames = (char**)rfx_calloc(sizeof(char*)*swffont->numchars);
2664 for(t=0;t<font->max_unicode;t++) {
2665 swffont->ascii2glyph[t] = font->unicode2glyph[t];
2667 SRECT max = {0,0,0,0};
2668 for(t=0;t<font->num_glyphs;t++) {
2672 swffont->glyph2ascii[t] = font->glyphs[t].unicode;
2673 if(swffont->glyph2ascii[t] == 0xffff || swffont->glyph2ascii[t] == 0x0000) {
2674 /* flash 8 flashtype requires unique unicode IDs for each character.
2675 We use the Unicode private user area to assign characters, hoping that
2676 the font doesn't contain more than 2048 glyphs */
2677 swffont->glyph2ascii[t] = 0xe000 + (t&0x1fff);
2680 if(font->glyphs[t].name) {
2681 swffont->glyphnames[t] = strdup(font->glyphs[t].name);
2683 swffont->glyphnames[t] = 0;
2685 advance = font->glyphs[t].advance;
2687 swf_Shape01DrawerInit(&draw, 0);
2688 line = font->glyphs[t].line;
2691 c.x = line->sx * GLYPH_SCALE; c.y = line->sy * GLYPH_SCALE;
2692 to.x = line->x * GLYPH_SCALE; to.y = line->y * GLYPH_SCALE;
2693 if(line->type == gfx_moveTo) {
2694 draw.moveTo(&draw, &to);
2695 } else if(line->type == gfx_lineTo) {
2696 draw.lineTo(&draw, &to);
2697 } else if(line->type == gfx_splineTo) {
2698 draw.splineTo(&draw, &c, &to);
2703 swffont->glyph[t].shape = swf_ShapeDrawerToShape(&draw);
2705 SRECT bbox = swf_ShapeDrawerGetBBox(&draw);
2706 swf_ExpandRect2(&max, &bbox);
2708 swffont->layout->bounds[t] = bbox;
2710 if(advance<32768.0/20) {
2711 swffont->glyph[t].advance = (int)(advance*20);
2713 //msg("<warning> Advance value overflow in glyph %d", t);
2714 swffont->glyph[t].advance = 32767;
2717 draw.dealloc(&draw);
2719 swf_ExpandRect2(&bounds, &swffont->layout->bounds[t]);
2721 for(t=0;t<font->num_glyphs;t++) {
2722 SRECT bbox = swffont->layout->bounds[t];
2724 /* if the glyph doesn't have a bounding box, use the
2725 combined bounding box (necessary e.g. for space characters) */
2726 if(!(bbox.xmin|bbox.ymin|bbox.xmax|bbox.ymax)) {
2727 swffont->layout->bounds[t] = bbox = max;
2730 /* check that the advance value is reasonable, by comparing it
2731 with the bounding box */
2732 if(bbox.xmax>0 && (bbox.xmax*10 < swffont->glyph[t].advance || !swffont->glyph[t].advance)) {
2733 if(swffont->glyph[t].advance)
2734 msg("<warning> fix bad advance value for char %d: bbox=%.2f, advance=%.2f\n", t, bbox.xmax/20.0, swffont->glyph[t].advance/20.0);
2735 swffont->glyph[t].advance = bbox.xmax;
2737 //swffont->glyph[t].advance = bbox.xmax - bbox.xmin;
2741 /* Flash player will use the advance value from the char, and the ascent/descent values
2742 from the layout for text selection.
2743 ascent will extend the char into negative y direction, from the baseline, while descent
2744 will extend in positive y direction, also from the baseline.
2745 The baseline is defined as the y-position zero
2748 swffont->layout->ascent = -bounds.ymin;
2749 if(swffont->layout->ascent < 0)
2750 swffont->layout->ascent = 0;
2751 swffont->layout->descent = bounds.ymax;
2752 if(swffont->layout->descent < 0)
2753 swffont->layout->descent = 0;
2754 swffont->layout->leading = bounds.ymax - bounds.ymin;
2756 /* if the font has proper ascent/descent values (>0) and those define
2757 greater line spacing that what we estimated from the bounding boxes,
2758 use the font's parameters */
2759 if(font->ascent*20 > swffont->layout->ascent)
2760 swffont->layout->ascent = font->ascent*20;
2761 if(font->descent*20 > swffont->layout->descent)
2762 swffont->layout->descent = font->descent*20;
2767 static void swf_addfont(gfxdevice_t*dev, gfxfont_t*font)
2769 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
2771 if(i->swffont && i->swffont->name && !strcmp((char*)i->swffont->name,font->id))
2772 return; // the requested font is the current font
2774 fontlist_t*last=0,*l = i->fontlist;
2777 if(!strcmp((char*)l->swffont->name, font->id)) {
2778 return; // we already know this font
2782 l = (fontlist_t*)rfx_calloc(sizeof(fontlist_t));
2783 l->swffont = gfxfont_to_swffont(font, font->id);
2790 swf_FontSetID(l->swffont, getNewID(i->dev));
2792 if(getScreenLogLevel() >= LOGLEVEL_DEBUG) {
2794 // print font information
2795 msg("<debug> Font %s",font->id);
2796 msg("<debug> | ID: %d", l->swffont->id);
2797 msg("<debug> | Version: %d", l->swffont->version);
2798 msg("<debug> | Name: %s", l->swffont->name);
2799 msg("<debug> | Numchars: %d", l->swffont->numchars);
2800 msg("<debug> | Maxascii: %d", l->swffont->maxascii);
2801 msg("<debug> | Style: %d", l->swffont->style);
2802 msg("<debug> | Encoding: %d", l->swffont->encoding);
2803 for(iii=0; iii<l->swffont->numchars;iii++) {
2804 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,
2805 l->swffont->layout->bounds[iii].xmin/20.0,
2806 l->swffont->layout->bounds[iii].ymin/20.0,
2807 l->swffont->layout->bounds[iii].xmax/20.0,
2808 l->swffont->layout->bounds[iii].ymax/20.0
2811 for(t=0;t<l->swffont->maxascii;t++) {
2812 if(l->swffont->ascii2glyph[t] == iii)
2813 msg("<debug> | - maps to %d",t);
2819 static void swf_switchfont(gfxdevice_t*dev, const char*fontid)
2821 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
2823 if(i->swffont && i->swffont->name && !strcmp((char*)i->swffont->name,fontid))
2824 return; // the requested font is the current font
2826 fontlist_t*l = i->fontlist;
2828 if(!strcmp((char*)l->swffont->name, fontid)) {
2829 i->swffont = l->swffont;
2834 msg("<error> Unknown font id: %s", fontid);
2838 static void swf_drawchar(gfxdevice_t*dev, gfxfont_t*font, int glyph, gfxcolor_t*color, gfxmatrix_t*matrix)
2840 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
2842 msg("<error> swf_drawchar called (glyph %d) without font", glyph);
2846 if(i->config_drawonlyshapes) {
2847 gfxglyph_t*g = &font->glyphs[glyph];
2848 gfxline_t*line2 = gfxline_clone(g->line);
2849 gfxline_transform(line2, matrix);
2850 dev->fill(dev, line2, color);
2851 gfxline_free(line2);
2855 if(!i->swffont || !i->swffont->name || strcmp((char*)i->swffont->name,font->id)) // not equal to current font
2857 /* TODO: remove the need for this (enhance getcharacterbbox so that it can cope
2858 with multiple fonts */
2860 swf_switchfont(dev, font->id); // set the current font
2863 msg("<warning> swf_drawchar: Font is NULL");
2866 if(glyph<0 || glyph>=i->swffont->numchars) {
2867 msg("<warning> No character %d in font %s (%d chars)", glyph, FIXNULL((char*)i->swffont->name), i->swffont->numchars);
2871 setfontscale(dev, matrix->m00, matrix->m01, matrix->m10, matrix->m11, matrix->tx, matrix->ty, 0);
2873 double det = i->fontmatrix.sx/65536.0 * i->fontmatrix.sy/65536.0 -
2874 i->fontmatrix.r0/65536.0 * i->fontmatrix.r1/65536.0;
2875 if(fabs(det) < 0.0005) {
2876 /* x direction equals y direction- the text is invisible */
2877 msg("<verbose> Not drawing invisible character character %d (det=%f, m=[%f %f;%f %f]\n", glyph,
2879 i->fontmatrix.sx/65536.0, i->fontmatrix.r1/65536.0,
2880 i->fontmatrix.r0/65536.0, i->fontmatrix.sy/65536.0);
2884 /*if(i->swffont->glyph[glyph].shape->bitlen <= 16) {
2885 msg("<warning> Glyph %d in current charset (%s, %d characters) is empty",
2886 glyph, FIXNULL((char*)i->swffont->name), i->swffont->numchars);
2890 /* calculate character position with respect to the current font matrix */
2891 double s = 20 * GLYPH_SCALE / det;
2892 double px = matrix->tx - i->fontmatrix.tx/20.0;
2893 double py = matrix->ty - i->fontmatrix.ty/20.0;
2894 int x = (SCOORD)(( px * i->fontmatrix.sy/65536.0 - py * i->fontmatrix.r1/65536.0)*s);
2895 int y = (SCOORD)((- px * i->fontmatrix.r0/65536.0 + py * i->fontmatrix.sx/65536.0)*s);
2896 if(x>32767 || x<-32768 || y>32767 || y<-32768) {
2897 msg("<verbose> Moving character origin to %f %f\n", matrix->tx, matrix->ty);
2899 setfontscale(dev, matrix->m00, matrix->m01, matrix->m10, matrix->m11, matrix->tx, matrix->ty, 1);
2900 /* since we just moved the char origin to the current char's position,
2901 it now has the relative position (0,0) */
2910 msg("<trace> Drawing char %d in font %d at %d,%d in color %02x%02x%02x%02x",
2911 glyph, i->swffont->id, x, y, color->r, color->g, color->b, color->a);
2913 putcharacter(dev, i->swffont->id, glyph, x, y, i->current_font_size, *(RGBA*)color);
2914 swf_FontUseGlyph(i->swffont, glyph);