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;
79 int config_simpleviewer;
80 int config_opennewwindow;
81 int config_ignoredraworder;
82 int config_drawonlyshapes;
83 int config_frameresets;
84 int config_linknameurl;
85 int config_jpegquality;
86 int config_storeallcharacters;
87 int config_enablezlib;
88 int config_insertstoptag;
90 int config_flashversion;
91 int config_reordertags;
92 int config_showclipshapes;
93 int config_splinemaxerror;
94 int config_fontsplinemaxerror;
95 int config_filloverlap;
98 int config_disable_polygon_conversion;
99 int config_normalize_polygon_positions;
100 RGBA config_linkcolor;
101 float config_minlinewidth;
102 double config_caplinewidth;
103 char* config_linktarget;
104 char*config_internallinkfunction;
105 char*config_externallinkfunction;
107 double config_framerate;
111 fontlist_t* fontlist;
150 int pic_height[1024];
157 char fillstylechanged;
159 int jpeg; //next image type
166 chardata_t chardata[CHARDATAMAX];
173 int current_font_size;
175 double lastfontm11,lastfontm12,lastfontm21,lastfontm22;
186 } swfoutput_internal;
188 static void swf_fillbitmap(gfxdevice_t*driver, gfxline_t*line, gfximage_t*img, gfxmatrix_t*move, gfxcxform_t*cxform);
189 static int swf_setparameter(gfxdevice_t*driver, const char*key, const char*value);
190 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);
191 static void swf_startclip(gfxdevice_t*dev, gfxline_t*line);
192 static void swf_endclip(gfxdevice_t*dev);
193 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);
194 static void swf_fill(gfxdevice_t*dev, gfxline_t*line, gfxcolor_t*color);
195 static void swf_fillbitmap(gfxdevice_t*dev, gfxline_t*line, gfximage_t*img, gfxmatrix_t*matrix, gfxcxform_t*cxform);
196 static void swf_fillgradient(gfxdevice_t*dev, gfxline_t*line, gfxgradient_t*gradient, gfxgradienttype_t type, gfxmatrix_t*matrix);
197 static void swf_drawchar(gfxdevice_t*dev, gfxfont_t*font, int glyph, gfxcolor_t*color, gfxmatrix_t*matrix);
198 static void swf_addfont(gfxdevice_t*dev, gfxfont_t*font);
199 static void swf_drawlink(gfxdevice_t*dev, gfxline_t*line, const char*action);
200 static void swf_startframe(gfxdevice_t*dev, int width, int height);
201 static void swf_endframe(gfxdevice_t*dev);
202 static gfxresult_t* swf_finish(gfxdevice_t*driver);
204 static swfoutput_internal* init_internal_struct()
206 swfoutput_internal*i = (swfoutput_internal*)malloc(sizeof(swfoutput_internal));
207 memset(i, 0, sizeof(swfoutput_internal));
231 i->fillstylechanged = 0;
238 i->config_dumpfonts=0;
239 i->config_ppmsubpixels=0;
240 i->config_jpegsubpixels=0;
241 i->config_opennewwindow=1;
242 i->config_ignoredraworder=0;
243 i->config_drawonlyshapes=0;
244 i->config_jpegquality=85;
245 i->config_storeallcharacters=0;
246 i->config_enablezlib=0;
247 i->config_insertstoptag=0;
248 i->config_flashversion=6;
249 i->config_framerate=0.25;
250 i->config_splinemaxerror=1;
251 i->config_fontsplinemaxerror=1;
252 i->config_filloverlap=0;
254 i->config_bboxvars=0;
255 i->config_showclipshapes=0;
256 i->config_minlinewidth=0.05;
257 i->config_caplinewidth=1;
258 i->config_linktarget=0;
259 i->config_internallinkfunction=0;
260 i->config_externallinkfunction=0;
261 i->config_reordertags=1;
262 i->config_linknameurl=1;
264 i->config_linkcolor.r = i->config_linkcolor.g = i->config_linkcolor.b = 255;
265 i->config_linkcolor.a = 0x40;
270 static int id_error = 0;
272 static U16 getNewID(gfxdevice_t* dev)
274 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
275 if(i->currentswfid == 65535) {
277 msg("<error> ID Table overflow");
278 msg("<error> This file is too complex to render- SWF only supports 65536 shapes at once");
284 return ++i->currentswfid;
286 static U16 getNewDepth(gfxdevice_t* dev)
288 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
289 if(i->depth == 65520) {
291 msg("<error> Depth Table overflow");
292 msg("<error> This file is too complex to render- SWF only supports 65536 shapes at once");
301 static void startshape(gfxdevice_t* dev);
302 static void starttext(gfxdevice_t* dev);
303 static void endshape(gfxdevice_t* dev);
304 static void endtext(gfxdevice_t* dev);
306 typedef struct _plotxy
311 // write a move-to command into the swf
312 static int movetoxy(gfxdevice_t*dev, TAG*tag, plotxy_t p0)
314 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
315 int rx = (int)(p0.x*20);
316 int ry = (int)(p0.y*20);
317 if(rx!=i->swflastx || ry!=i->swflasty || i->fillstylechanged) {
318 swf_ShapeSetMove (tag, i->shape, rx,ry);
319 i->fillstylechanged = 0;
326 static int moveto(gfxdevice_t*dev, TAG*tag, double x, double y)
328 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
332 return movetoxy(dev, tag, p);
334 static void addPointToBBox(gfxdevice_t*dev, int px, int py)
336 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
342 swf_ExpandRect(&i->bboxrect, p);
344 swf_ExpandRect3(&i->bboxrect, p, i->linewidth*3/2);
348 /*static void plot(gfxdevice_t*dev, int x, int y, TAG*tag)
350 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
351 int width = i->linewidth/4;
355 //swf_ShapeSetLine(tag, i->shape,-width,-width);
356 //swf_ShapeSetLine(tag, i->shape,width*2,0);
357 //swf_ShapeSetLine(tag, i->shape,0,width*2);
358 //swf_ShapeSetLine(tag, i->shape,-width*2,0);
359 //swf_ShapeSetLine(tag, i->shape,0,-width*2);
360 //swf_ShapeSetLine(tag, i->shape,width,width);
363 swf_ShapeSetLine(tag, i->shape,-width,0);
364 swf_ShapeSetLine(tag, i->shape,width,-width);
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,0);
370 addPointToBBox(dev, x-width ,y-width);
371 addPointToBBox(dev, x+width ,y+width);
374 // write a line-to command into the swf
375 static void linetoxy(gfxdevice_t*dev, TAG*tag, plotxy_t p0)
377 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
378 int px = (int)(p0.x*20);
379 int py = (int)(p0.y*20);
380 int rx = (px-i->swflastx);
381 int ry = (py-i->swflasty);
383 swf_ShapeSetLine (tag, i->shape, rx,ry);
384 addPointToBBox(dev, i->swflastx,i->swflasty);
385 addPointToBBox(dev, px,py);
386 }/* else if(!i->fill) {
387 // treat lines of length 0 as plots, making them
388 // at least 1 twip wide so Flash will display them
389 plot(dev, i->swflastx, i->swflasty, tag);
396 static void lineto(gfxdevice_t*dev, TAG*tag, double x, double y)
401 linetoxy(dev,tag, p);
404 // write a spline-to command into the swf
405 static void splineto(gfxdevice_t*dev, TAG*tag, plotxy_t control,plotxy_t end)
407 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
408 int lastlastx = i->swflastx;
409 int lastlasty = i->swflasty;
411 int cx = ((int)(control.x*20)-i->swflastx);
412 int cy = ((int)(control.y*20)-i->swflasty);
415 int ex = ((int)(end.x*20)-i->swflastx);
416 int ey = ((int)(end.y*20)-i->swflasty);
420 if((cx || cy) && (ex || ey)) {
421 swf_ShapeSetCurve(tag, i->shape, cx,cy,ex,ey);
422 addPointToBBox(dev, lastlastx ,lastlasty );
423 addPointToBBox(dev, lastlastx+cx,lastlasty+cy);
424 addPointToBBox(dev, lastlastx+cx+ex,lastlasty+cy+ey);
425 } else if(cx || cy || ex || ey) {
426 swf_ShapeSetLine(tag, i->shape, cx+ex,cy+ey);
427 addPointToBBox(dev, lastlastx ,lastlasty );
428 addPointToBBox(dev, lastlastx+cx,lastlasty+cy);
429 addPointToBBox(dev, lastlastx+cx+ex,lastlasty+cy+ey);
435 /* write a line, given two points and the transformation
437 /*static void line(gfxdevice_t*dev, TAG*tag, plotxy_t p0, plotxy_t p1)
439 moveto(dev, tag, p0);
440 lineto(dev, tag, p1);
443 void resetdrawer(gfxdevice_t*dev)
445 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
450 static void stopFill(gfxdevice_t*dev)
452 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
453 if(i->lastwasfill!=0)
455 swf_ShapeSetStyle(i->tag,i->shape,i->linestyleid,0x8000,0);
456 i->fillstylechanged = 1;
460 static void startFill(gfxdevice_t*dev)
462 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
463 if(i->lastwasfill!=1)
465 swf_ShapeSetStyle(i->tag,i->shape,0x8000,i->fillstyleid,0);
466 i->fillstylechanged = 1;
471 static inline int colorcompare(gfxdevice_t*dev, RGBA*a,RGBA*b)
483 static SRECT getcharacterbbox(gfxdevice_t*dev, SWFFONT*font, MATRIX* m)
485 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
488 memset(&r, 0, sizeof(r));
491 if(debug) printf("\n");
492 for(t=0;t<i->chardatapos;t++)
494 if(i->chardata[t].fontid != font->id) {
495 msg("<error> Internal error: fontid %d != fontid %d", i->chardata[t].fontid, font->id);
498 SRECT b = font->layout->bounds[i->chardata[t].charid];
499 b.xmin *= i->chardata[t].size;
500 b.ymin *= i->chardata[t].size;
501 b.xmax *= i->chardata[t].size;
502 b.ymax *= i->chardata[t].size;
504 /* divide by 1024, rounding xmax/ymax up */
505 b.xmax += 1023; b.ymax += 1023; b.xmin /= 1024; b.ymin /= 1024; b.xmax /= 1024; b.ymax /= 1024;
507 b.xmin += i->chardata[t].x;
508 b.ymin += i->chardata[t].y;
509 b.xmax += i->chardata[t].x;
510 b.ymax += i->chardata[t].y;
512 /* until we solve the INTERNAL_SCALING problem (see below)
513 make sure the bounding box is big enough */
519 b = swf_TurnRect(b, m);
521 if(debug) printf("(%f,%f,%f,%f) -> (%f,%f,%f,%f) [font %d/%d, char %d]\n",
522 font->layout->bounds[i->chardata[t].charid].xmin/20.0,
523 font->layout->bounds[i->chardata[t].charid].ymin/20.0,
524 font->layout->bounds[i->chardata[t].charid].xmax/20.0,
525 font->layout->bounds[i->chardata[t].charid].ymax/20.0,
530 i->chardata[t].fontid,
532 i->chardata[t].charid
534 swf_ExpandRect2(&r, &b);
536 if(debug) printf("-----> (%f,%f,%f,%f)\n",
544 static void putcharacters(gfxdevice_t*dev, TAG*tag)
546 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
550 color.r = i->chardata[0].color.r^255;
559 int charadvance[128];
562 int glyphbits=1; //TODO: can this be zero?
565 if(tag->id != ST_DEFINETEXT &&
566 tag->id != ST_DEFINETEXT2) {
567 msg("<error> internal error: putcharacters needs an text tag, not %d\n",tag->id);
570 if(!i->chardatapos) {
571 msg("<warning> putcharacters called with zero characters");
574 for(pass = 0; pass < 2; pass++)
584 advancebits++; // add sign bit
585 swf_SetU8(tag, glyphbits);
586 swf_SetU8(tag, advancebits);
589 for(t=0;t<=i->chardatapos;t++)
591 if(lastfontid != i->chardata[t].fontid ||
592 lastx!=i->chardata[t].x ||
593 lasty!=i->chardata[t].y ||
594 !colorcompare(dev,&color, &i->chardata[t].color) ||
596 lastsize != i->chardata[t].size ||
599 if(charstorepos && pass==0)
602 for(s=0;s<charstorepos;s++)
604 while(charids[s]>=(1<<glyphbits))
606 while(charadvance[s]>=(1<<advancebits))
610 if(charstorepos && pass==1)
612 tag->writeBit = 0; // Q&D
613 swf_SetBits(tag, 0, 1); // GLYPH Record
614 swf_SetBits(tag, charstorepos, 7); // number of glyphs
616 for(s=0;s<charstorepos;s++)
618 swf_SetBits(tag, charids[s], glyphbits);
619 swf_SetBits(tag, charadvance[s], advancebits);
624 if(pass == 1 && t<i->chardatapos)
630 if(lastx != i->chardata[t].x ||
631 lasty != i->chardata[t].y)
633 newx = i->chardata[t].x;
634 newy = i->chardata[t].y;
640 if(!colorcompare(dev,&color, &i->chardata[t].color))
642 color = i->chardata[t].color;
645 font.id = i->chardata[t].fontid;
646 if(lastfontid != i->chardata[t].fontid || lastsize != i->chardata[t].size)
649 tag->writeBit = 0; // Q&D
650 swf_TextSetInfoRecord(tag, newfont, i->chardata[t].size, newcolor, newx,newy);
653 lastfontid = i->chardata[t].fontid;
654 lastx = i->chardata[t].x;
655 lasty = i->chardata[t].y;
656 lastsize = i->chardata[t].size;
659 if(t==i->chardatapos)
663 int nextt = t==i->chardatapos-1?t:t+1;
664 int rel = i->chardata[nextt].x-i->chardata[t].x;
665 if(rel>=0 && (rel<(1<<(advancebits-1)) || pass==0)) {
667 lastx=i->chardata[nextt].x;
671 lastx=i->chardata[t].x;
673 charids[charstorepos] = i->chardata[t].charid;
674 charadvance[charstorepos] = advance;
681 static void putcharacter(gfxdevice_t*dev, int fontid, int charid, int x,int y, int size, RGBA color)
683 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
684 if(i->chardatapos == CHARDATAMAX)
686 msg("<warning> Character buffer too small. SWF will be slightly bigger");
690 i->chardata[i->chardatapos].fontid = fontid;
691 i->chardata[i->chardatapos].charid = charid;
692 i->chardata[i->chardatapos].x = x;
693 i->chardata[i->chardatapos].y = y;
694 i->chardata[i->chardatapos].color = color;
695 i->chardata[i->chardatapos].size = size;
699 /* Notice: we can only put chars in the range -1639,1638 (-32768/20,32768/20).
700 So if we set this value to high, the char coordinates will overflow.
701 If we set it to low, however, the char positions will be inaccurate */
702 #define GLYPH_SCALE 1
704 static void endtext(gfxdevice_t*dev)
706 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
710 i->tag = swf_InsertTag(i->tag,ST_DEFINETEXT2);
711 swf_SetU16(i->tag, i->textid);
714 r = getcharacterbbox(dev, i->swffont, &i->fontmatrix);
715 r = swf_ClipRect(i->pagebbox, r);
716 swf_SetRect(i->tag,&r);
718 swf_SetMatrix(i->tag,&i->fontmatrix);
720 msg("<trace> Placing text (%d characters) as ID %d", i->chardatapos, i->textid);
722 putcharacters(dev, i->tag);
725 if(i->swf->fileVersion >= 8) {
726 i->tag = swf_InsertTag(i->tag, ST_CSMTEXTSETTINGS);
727 swf_SetU16(i->tag, i->textid);
729 //swf_SetU8(i->tag, /*subpixel grid*/(2<<3)|/*flashtype*/0x40);
730 swf_SetU8(i->tag, /*grid*/(1<<3)|/*flashtype*/0x40);
731 //swf_SetU8(i->tag, /*no grid*/(0<<3)|/*flashtype*/0x40);
733 swf_SetU32(i->tag, 0);//thickness
734 swf_SetU32(i->tag, 0);//sharpness
735 swf_SetU8(i->tag, 0);//reserved
737 i->tag = swf_InsertTag(i->tag,ST_PLACEOBJECT2);
739 swf_ObjectPlace(i->tag,i->textid,getNewDepth(dev),&i->page_matrix,NULL,NULL);
743 /* set's the matrix which is to be applied to characters drawn by swfoutput_drawchar() */
744 static void setfontscale(gfxdevice_t*dev,double m11,double m12, double m21,double m22,double x, double y, char force)
750 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
751 if(i->lastfontm11 == m11 &&
752 i->lastfontm12 == m12 &&
753 i->lastfontm21 == m21 &&
754 i->lastfontm22 == m22 && !force)
759 i->lastfontm11 = m11;
760 i->lastfontm12 = m12;
761 i->lastfontm21 = m21;
762 i->lastfontm22 = m22;
764 double xsize = sqrt(m11*m11 + m12*m12);
765 double ysize = sqrt(m21*m21 + m22*m22);
766 i->current_font_size = (xsize>ysize?xsize:ysize)*1;
767 if(i->current_font_size < 1)
768 i->current_font_size = 1;
769 double ifs = 1.0 / (i->current_font_size*GLYPH_SCALE);
772 m.sx = (S32)((m11*ifs)*65536); m.r1 = (S32)((m21*ifs)*65536);
773 m.r0 = (S32)((m12*ifs)*65536); m.sy = (S32)((m22*ifs)*65536);
774 /* this is the position of the first char to set a new fontmatrix-
775 we hope that it's close enough to all other characters using the
776 font, so we use its position as origin for the matrix */
782 static int watermark2_width=47;
783 static int watermark2_height=11;
784 static int watermark2[47] = {95,1989,71,0,2015,337,1678,0,2015,5,1921,320,1938,25,2006,1024,
785 1042,21,13,960,1039,976,8,2000,1359,1088,31,1989,321,1728,0,1152,
786 1344,832,0,1984,0,896,1088,1088,896,0,1984,128,256,512,1984};
788 static void draw_watermark(gfxdevice_t*dev, gfxbbox_t r, char drawall)
790 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
791 double wx = r.xmax / 5.0;
792 double tx = r.xmax*4.0 / 5.0;
793 double ty = r.ymax-wx*watermark2_height/watermark2_width;
794 double sx = (r.xmax - tx) / watermark2_width;
795 double sy = (r.ymax - ty) / watermark2_height;
798 if(ty > 0 && px > 1.0 && py > 1.0) {
800 for(y=0;y<watermark2_height;y++)
801 for(x=0;x<watermark2_width;x++) {
802 if(((watermark2[x]>>y)&1)) {
803 if(!drawall && rand()%5)
805 unsigned int b = rand();
806 moveto(dev, i->tag, x*sx+tx+((b>>1)&1)/20.0, y*sy+ty+((b>>3)&1)/20.0);
807 lineto(dev, i->tag, x*sx+px+tx+((b>>2)&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+py+ty+((b>>4)&1)/20.0);
809 lineto(dev, i->tag, x*sx+tx+((b>>1)&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+ty+((b>>3)&1)/20.0);
816 static void swfoutput_setfillcolor(gfxdevice_t* dev, U8 r, U8 g, U8 b, U8 a)
818 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
819 if(i->fillrgb.r == r &&
822 i->fillrgb.a == a) return;
831 static void insert_watermark(gfxdevice_t*dev, char drawall)
833 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
834 if(!drawall && i->watermarks>20)
840 swfoutput_setfillcolor(dev, 0,0,255,192);
842 swfoutput_setfillcolor(dev, rand(),rand(),rand(),(rand()&127)+128);
847 gfxbbox_t r; r.xmin = r.ymin = 0;
850 draw_watermark(dev, r, drawall);
856 static void endpage(gfxdevice_t*dev)
858 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
869 if(i->config_watermark) {
870 insert_watermark(dev, 1);
876 static void addViewer(gfxdevice_t* dev)
878 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
881 RGBA button_colors[3]= {{0xbf,0x00,0x00,0x80},{0xbf,0x20,0x20,0xc0}, {0xbf,0xc0,0xc0,0xff}};
883 int button_sizex = 20;
884 int button_sizey = 20;
886 RGBA black = {255,0,0,0};
888 i->tag = swf_InsertTag(i->tag,ST_DEFINESHAPE3);
890 int ls1 = swf_ShapeAddLineStyle(s,40,&black);
891 int fs1 = swf_ShapeAddSolidFillStyle(s,&button_colors[t/2]);
892 int shapeid = ids[t] = getNewID(dev);
893 swf_SetU16(i->tag,shapeid);
895 r.xmin = -20*button_sizex;
896 r.xmax = 20*button_sizex;
898 r.ymax = 40*button_sizey;
899 swf_SetRect(i->tag,&r); // set shape bounds
900 swf_SetShapeHeader(i->tag,s); // write all styles to tag
901 swf_ShapeSetAll(i->tag,s,0*button_sizex,0,ls1,fs1,0);
902 swf_ShapeSetLine(i->tag,s,(1-(t&1)*2)*20*button_sizex,20*button_sizey);
903 swf_ShapeSetLine(i->tag,s,-(1-(t&1)*2)*20*button_sizex,20*button_sizey);
904 swf_ShapeSetLine(i->tag,s,0,-40*button_sizey);
905 swf_ShapeSetEnd(i->tag); // finish drawing
906 swf_ShapeFree(s); // clean shape structure (which isn't needed anymore after writing the tag)
908 ActionTAG*a1=0,*a2=0,*a3=0;
909 a1 = action_NextFrame(a1);
910 a1 = action_Stop(a1);
913 a2 = action_PreviousFrame(a2);
914 a2 = action_Stop(a2);
917 a3 = action_Stop(a3);
920 i->tag = swf_InsertTag(i->tag, ST_DOACTION);
921 swf_ActionSet(i->tag,a3);
923 i->tag = swf_InsertTag(i->tag,ST_DEFINEBUTTON);
924 int buttonid1 = getNewID(dev);
925 swf_SetU16(i->tag, buttonid1);
926 swf_ButtonSetRecord(i->tag,BS_UP|BS_HIT,ids[0],1,NULL,NULL);
927 swf_ButtonSetRecord(i->tag,BS_OVER,ids[2],1,NULL,NULL);
928 swf_ButtonSetRecord(i->tag,BS_DOWN,ids[4],1,NULL,NULL);
929 swf_SetU8(i->tag,0); // end of button records
930 swf_ActionSet(i->tag,a1);
932 i->tag = swf_InsertTag(i->tag,ST_DEFINEBUTTON);
933 int buttonid2 = getNewID(dev);
934 swf_SetU16(i->tag, buttonid2);
935 swf_ButtonSetRecord(i->tag,BS_UP|BS_HIT,ids[1],1,NULL,NULL);
936 swf_ButtonSetRecord(i->tag,BS_OVER,ids[3],1,NULL,NULL);
937 swf_ButtonSetRecord(i->tag,BS_DOWN,ids[5],1,NULL,NULL);
938 swf_SetU8(i->tag,0); // end of button records
939 swf_ActionSet(i->tag,a2);
941 i->tag = swf_InsertTag(i->tag,ST_PLACEOBJECT2);
943 swf_GetMatrix(0, &m);
944 m.tx = button_sizex*20+200;
945 swf_ObjectPlace(i->tag, buttonid2, 65534,&m,0,0);
946 i->tag = swf_InsertTag(i->tag,ST_PLACEOBJECT2);
947 m.tx = button_sizex*20+200+200;
948 swf_ObjectPlace(i->tag, buttonid1, 65535,&m,0,0);
952 void swf_startframe(gfxdevice_t*dev, int width, int height)
954 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
956 if(i->config_protect) {
957 i->tag = swf_InsertTag(i->tag, ST_PROTECT);
958 i->config_protect = 0;
960 if(i->config_simpleviewer) {
965 if(!i->firstpage && !i->pagefinished)
968 msg("<verbose> Starting new SWF page of size %dx%d", width, height);
970 swf_GetMatrix(0, &i->page_matrix);
971 i->page_matrix.tx = 0;
972 i->page_matrix.ty = 0;
979 /* create a bbox structure with the page size. This is used
980 for clipping shape and text bounding boxes. As we don't want to
981 generate bounding boxes which extend beyond the movie size (in
982 order to not confuse Flash), we clip everything against i->pagebbox */
983 i->pagebbox.xmin = 0;
984 i->pagebbox.ymin = 0;
985 i->pagebbox.xmax = width*20;
986 i->pagebbox.ymax = height*20;
988 /* increase SWF's bounding box */
989 swf_ExpandRect2(&i->swf->movieSize, &i->pagebbox);
991 i->lastframeno = i->frameno;
996 void swf_endframe(gfxdevice_t*dev)
998 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1000 if(!i->pagefinished)
1003 if( (i->swf->fileVersion <= 8) && (i->config_insertstoptag) ) {
1005 atag = action_Stop(atag);
1006 atag = action_End(atag);
1007 i->tag = swf_InsertTag(i->tag,ST_DOACTION);
1008 swf_ActionSet(i->tag,atag);
1010 i->tag = swf_InsertTag(i->tag,ST_SHOWFRAME);
1013 for(i->depth;i->depth>i->startdepth;i->depth--) {
1014 i->tag = swf_InsertTag(i->tag,ST_REMOVEOBJECT2);
1015 swf_SetU16(i->tag,i->depth);
1017 i->depth = i->startdepth;
1019 if(i->config_frameresets) {
1020 for(i->currentswfid;i->currentswfid>i->startids;i->currentswfid--) {
1021 i->tag = swf_InsertTag(i->tag,ST_FREECHARACTER);
1022 swf_SetU16(i->tag,i->currentswfid);
1024 i->currentswfid = i->startids;
1028 static void setBackground(gfxdevice_t*dev, int x1, int y1, int x2, int y2)
1030 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1032 rgb.a = rgb.r = rgb.g = rgb.b = 0xff;
1036 int shapeid = getNewID(dev);
1041 i->tag = swf_InsertTag(i->tag, ST_DEFINESHAPE);
1043 fs1 = swf_ShapeAddSolidFillStyle(s, &rgb);
1044 swf_SetU16(i->tag,shapeid);
1045 swf_SetRect(i->tag,&r);
1046 swf_SetShapeHeader(i->tag,s);
1047 swf_ShapeSetAll(i->tag,s,x1,y1,ls1,fs1,0);
1048 swf_ShapeSetLine(i->tag,s,(x2-x1),0);
1049 swf_ShapeSetLine(i->tag,s,0,(y2-y1));
1050 swf_ShapeSetLine(i->tag,s,(x1-x2),0);
1051 swf_ShapeSetLine(i->tag,s,0,(y1-y2));
1052 swf_ShapeSetEnd(i->tag);
1054 i->tag = swf_InsertTag(i->tag, ST_PLACEOBJECT2);
1055 swf_ObjectPlace(i->tag,shapeid,getNewDepth(dev),0,0,0);
1056 i->tag = swf_InsertTag(i->tag, ST_PLACEOBJECT2);
1057 swf_ObjectPlaceClip(i->tag,shapeid,getNewDepth(dev),0,0,0,65535);
1060 /* initialize the swf writer */
1061 void gfxdevice_swf_init(gfxdevice_t* dev)
1063 memset(dev, 0, sizeof(gfxdevice_t));
1067 dev->internal = init_internal_struct(); // set config to default values
1069 dev->startpage = swf_startframe;
1070 dev->endpage = swf_endframe;
1071 dev->finish = swf_finish;
1072 dev->fillbitmap = swf_fillbitmap;
1073 dev->setparameter = swf_setparameter;
1074 dev->stroke = swf_stroke;
1075 dev->startclip = swf_startclip;
1076 dev->endclip = swf_endclip;
1077 dev->fill = swf_fill;
1078 dev->fillbitmap = swf_fillbitmap;
1079 dev->fillgradient = swf_fillgradient;
1080 dev->addfont = swf_addfont;
1081 dev->drawchar = swf_drawchar;
1082 dev->drawlink = swf_drawlink;
1084 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1087 msg("<verbose> initializing swf output\n", i->max_x,i->max_y);
1091 i->swf = (SWF*)rfx_calloc(sizeof(SWF));
1092 i->swf->fileVersion = 0;
1093 i->swf->frameRate = 0x80;
1094 i->swf->movieSize.xmin = 0;
1095 i->swf->movieSize.ymin = 0;
1096 i->swf->movieSize.xmax = 0;
1097 i->swf->movieSize.ymax = 0;
1099 i->swf->firstTag = swf_InsertTag(NULL,ST_SETBACKGROUNDCOLOR);
1100 i->tag = i->swf->firstTag;
1102 rgb.a = rgb.r = rgb.g = rgb.b = 0xff;
1104 swf_SetRGB(i->tag,&rgb);
1106 i->startdepth = i->depth = 0;
1107 i->startids = i->currentswfid = 0;
1110 static void startshape(gfxdevice_t*dev)
1112 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1117 //if(i->chardatapos && i->chardata[i->chardatapos-1].color.a)
1120 i->tag = swf_InsertTag(i->tag,ST_DEFINESHAPE3);
1122 swf_ShapeNew(&i->shape);
1123 i->linestyleid = swf_ShapeAddLineStyle(i->shape,i->linewidth,&i->strokergb);
1124 i->fillstyleid = swf_ShapeAddSolidFillStyle(i->shape,&i->fillrgb);
1126 RGBA markcol = {0,i->mark[0],i->mark[1],i->mark[2]};
1127 swf_ShapeAddSolidFillStyle(i->shape,&markcol);
1130 i->shapeid = getNewID(dev);
1132 msg("<debug> Using shape id %d", i->shapeid);
1134 swf_SetU16(i->tag,i->shapeid); // ID
1136 i->bboxrectpos = i->tag->len;
1138 swf_SetRect(i->tag,&i->pagebbox);
1140 memset(&i->bboxrect, 0, sizeof(i->bboxrect));
1142 swf_SetShapeStyles(i->tag,i->shape);
1143 swf_ShapeCountBits(i->shape,NULL,NULL);
1144 swf_SetShapeBits(i->tag,i->shape);
1146 /* TODO: do we really need this? */
1147 //swf_ShapeSetAll(i->tag,i->shape,/*x*/0,/*y*/0,i->linestyleid,0,0);
1148 //swf_ShapeSetAll(i->tag,i->shape,/*x*/UNDEFINED_COORD,/*y*/UNDEFINED_COORD,i->linestyleid,0,0);
1149 i->swflastx=i->swflasty=UNDEFINED_COORD;
1150 i->lastwasfill = -1;
1151 i->shapeisempty = 1;
1154 static void starttext(gfxdevice_t*dev)
1156 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1160 if(i->config_watermark) {
1161 insert_watermark(dev, 0);
1163 i->textid = getNewID(dev);
1164 i->swflastx=i->swflasty=0;
1168 /* TODO: move to ../lib/rfxswf */
1169 void changeRect(gfxdevice_t*dev, TAG*tag, int pos, SRECT*newrect)
1171 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1172 /* determine length of old rect */
1176 swf_GetRect(tag, &old);
1177 swf_ResetReadBits(tag);
1178 int pos_end = tag->pos;
1180 int len = tag->len - pos_end;
1181 U8*data = (U8*)malloc(len);
1182 memcpy(data, &tag->data[pos_end], len);
1185 swf_SetRect(tag, newrect);
1186 swf_SetBlock(tag, data, len);
1188 tag->pos = tag->readBit = 0;
1191 void cancelshape(gfxdevice_t*dev)
1193 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1194 /* delete old shape tag */
1196 i->tag = i->tag->prev;
1197 swf_DeleteTag(0, todel);
1198 if(i->shape) {swf_ShapeFree(i->shape);i->shape=0;}
1200 i->bboxrectpos = -1;
1202 // i->currentswfid--; // doesn't work, for some reason
1205 void fixAreas(gfxdevice_t*dev)
1207 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1208 if(!i->shapeisempty && i->fill &&
1209 (i->bboxrect.xmin == i->bboxrect.xmax ||
1210 i->bboxrect.ymin == i->bboxrect.ymax) &&
1211 i->config_minlinewidth >= 0.001
1213 msg("<debug> Shape has size 0: width=%.2f height=%.2f",
1214 (i->bboxrect.xmax-i->bboxrect.xmin)/20.0,
1215 (i->bboxrect.ymax-i->bboxrect.ymin)/20.0
1218 SRECT r = i->bboxrect;
1220 if(r.xmin == r.xmax && r.ymin == r.ymax) {
1221 /* this thing comes down to a single dot- nothing to fix here */
1227 RGBA save_col = i->strokergb;
1228 int save_width = i->linewidth;
1230 i->strokergb = i->fillrgb;
1231 i->linewidth = (int)(i->config_minlinewidth*20);
1232 if(i->linewidth==0) i->linewidth = 1;
1237 moveto(dev, i->tag, r.xmin/20.0,r.ymin/20.0);
1238 lineto(dev, i->tag, r.xmax/20.0,r.ymax/20.0);
1240 i->strokergb = save_col;
1241 i->linewidth = save_width;
1246 static void endshape_noput(gfxdevice_t*dev)
1248 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1251 //changeRect(dev, i->tag, i->bboxrectpos, &i->bboxrect);
1254 swf_ShapeFree(i->shape);
1262 static void endshape(gfxdevice_t*dev)
1264 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1270 if(i->shapeisempty ||
1272 (i->bboxrect.xmin == i->bboxrect.xmax &&
1273 i->bboxrect.ymin == i->bboxrect.ymax))
1275 // delete the shape again, we didn't do anything
1276 msg("<debug> cancelling shape: bbox is (%f,%f,%f,%f)",
1277 i->bboxrect.xmin /20.0,
1278 i->bboxrect.ymin /20.0,
1279 i->bboxrect.xmax /20.0,
1280 i->bboxrect.ymax /20.0
1286 swf_ShapeSetEnd(i->tag);
1288 SRECT r = swf_ClipRect(i->pagebbox, i->bboxrect);
1289 changeRect(dev, i->tag, i->bboxrectpos, &r);
1291 msg("<trace> Placing shape ID %d", i->shapeid);
1293 i->tag = swf_InsertTag(i->tag,ST_PLACEOBJECT2);
1294 MATRIX m = i->page_matrix;
1295 m.tx += i->shapeposx;
1296 m.ty += i->shapeposy;
1297 swf_ObjectPlace(i->tag,i->shapeid,getNewDepth(dev),&m,NULL,NULL);
1299 if(i->config_animate) {
1300 i->tag = swf_InsertTag(i->tag,ST_SHOWFRAME);
1303 swf_ShapeFree(i->shape);
1306 i->bboxrectpos = -1;
1313 void wipeSWF(SWF*swf)
1315 TAG*tag = swf->firstTag;
1317 TAG*next = tag->next;
1318 if(tag->id != ST_SETBACKGROUNDCOLOR &&
1319 tag->id != ST_END &&
1320 tag->id != ST_DOACTION &&
1321 tag->id != ST_SHOWFRAME) {
1322 swf_DeleteTag(swf, tag);
1328 void swfoutput_finalize(gfxdevice_t*dev)
1330 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1332 if(i->tag && i->tag->id == ST_END)
1333 return; //already done
1335 i->swf->fileVersion = i->config_flashversion;
1336 i->swf->frameRate = i->config_framerate*0x100;
1338 if(i->config_bboxvars) {
1339 TAG* tag = swf_InsertTag(i->swf->firstTag, ST_DOACTION);
1341 a = action_PushString(a, "xmin");
1342 a = action_PushFloat(a, i->swf->movieSize.xmin / 20.0);
1343 a = action_SetVariable(a);
1344 a = action_PushString(a, "ymin");
1345 a = action_PushFloat(a, i->swf->movieSize.ymin / 20.0);
1346 a = action_SetVariable(a);
1347 a = action_PushString(a, "xmax");
1348 a = action_PushFloat(a, i->swf->movieSize.xmax / 20.0);
1349 a = action_SetVariable(a);
1350 a = action_PushString(a, "ymax");
1351 a = action_PushFloat(a, i->swf->movieSize.ymax / 20.0);
1352 a = action_SetVariable(a);
1353 a = action_PushString(a, "width");
1354 a = action_PushFloat(a, (i->swf->movieSize.xmax - i->swf->movieSize.xmin) / 20.0);
1355 a = action_SetVariable(a);
1356 a = action_PushString(a, "height");
1357 a = action_PushFloat(a, (i->swf->movieSize.ymax - i->swf->movieSize.ymin) / 20.0);
1358 a = action_SetVariable(a);
1360 swf_ActionSet(tag, a);
1365 free(i->mark);i->mark = 0;
1369 fontlist_t *iterator = i->fontlist;
1371 TAG*mtag = i->swf->firstTag;
1372 if(iterator->swffont) {
1373 if(!i->config_storeallcharacters) {
1374 msg("<debug> Reducing font %s", iterator->swffont->name);
1375 swf_FontReduce(iterator->swffont);
1377 int used = iterator->swffont->use && iterator->swffont->use->used_glyphs;
1379 mtag = swf_InsertTag(mtag, ST_DEFINEFONT2);
1380 swf_FontSetDefine2(mtag, iterator->swffont);
1384 iterator = iterator->next;
1387 i->tag = swf_InsertTag(i->tag,ST_END);
1388 TAG* tag = i->tag->prev;
1390 /* remove the removeobject2 tags between the last ST_SHOWFRAME
1391 and the ST_END- they confuse the flash player */
1392 while(tag->id == ST_REMOVEOBJECT2) {
1393 TAG* prev = tag->prev;
1394 swf_DeleteTag(i->swf, tag);
1401 if(i->config_enablezlib || i->config_flashversion>=6) {
1402 i->swf->compressed = 1;
1405 /* Initialize AVM2 if it is a Flash9 file */
1406 if(i->config_flashversion>=9 && i->config_insertstoptag) {
1407 AVM2_InsertStops(i->swf);
1409 // if(i->config_reordertags)
1410 // swf_Optimize(i->swf);
1413 int swfresult_save(gfxresult_t*gfx, const char*filename)
1415 SWF*swf = (SWF*)gfx->internal;
1418 fi = open(filename, O_BINARY|O_CREAT|O_TRUNC|O_WRONLY, 0777);
1423 msg("<fatal> Could not create \"%s\". ", FIXNULL(filename));
1427 if FAILED(swf_WriteSWF(fi,swf))
1428 msg("<error> WriteSWF() failed.\n");
1434 void* swfresult_get(gfxresult_t*gfx, const char*name)
1436 SWF*swf = (SWF*)gfx->internal;
1437 if(!strcmp(name, "swf")) {
1438 return (void*)swf_CopySWF(swf);
1439 } else if(!strcmp(name, "xmin")) {
1440 return (void*)(swf->movieSize.xmin/20);
1441 } else if(!strcmp(name, "ymin")) {
1442 return (void*)(swf->movieSize.ymin/20);
1443 } else if(!strcmp(name, "xmax")) {
1444 return (void*)(swf->movieSize.xmax/20);
1445 } else if(!strcmp(name, "ymax")) {
1446 return (void*)(swf->movieSize.ymax/20);
1447 } else if(!strcmp(name, "width")) {
1448 return (void*)((swf->movieSize.xmax - swf->movieSize.xmin)/20);
1449 } else if(!strcmp(name, "height")) {
1450 return (void*)((swf->movieSize.ymax - swf->movieSize.ymin)/20);
1454 void swfresult_destroy(gfxresult_t*gfx)
1457 swf_FreeTags((SWF*)gfx->internal);
1458 free(gfx->internal);
1461 memset(gfx, 0, sizeof(gfxresult_t));
1465 static void swfoutput_destroy(gfxdevice_t* dev);
1467 gfxresult_t* swf_finish(gfxdevice_t* dev)
1469 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1472 if(i->config_linktarget) {
1473 free(i->config_linktarget);
1474 i->config_linktarget = 0;
1477 swfoutput_finalize(dev);
1478 SWF* swf = i->swf;i->swf = 0;
1479 swfoutput_destroy(dev);
1481 result = (gfxresult_t*)rfx_calloc(sizeof(gfxresult_t));
1482 result->internal = swf;
1483 result->save = swfresult_save;
1485 result->get = swfresult_get;
1486 result->destroy = swfresult_destroy;
1490 /* Perform cleaning up */
1491 static void swfoutput_destroy(gfxdevice_t* dev)
1493 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1495 /* not initialized yet- nothing to destroy */
1499 fontlist_t *tmp,*iterator = i->fontlist;
1501 if(iterator->swffont) {
1502 swf_FontFree(iterator->swffont);iterator->swffont=0;
1505 iterator = iterator->next;
1508 if(i->swf) {swf_FreeTags(i->swf);free(i->swf);i->swf = 0;}
1511 memset(dev, 0, sizeof(gfxdevice_t));
1514 static void swfoutput_setstrokecolor(gfxdevice_t* dev, U8 r, U8 g, U8 b, U8 a)
1516 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1517 if(i->strokergb.r == r &&
1518 i->strokergb.g == g &&
1519 i->strokergb.b == b &&
1520 i->strokergb.a == a) return;
1530 //#define ROUND_UP 19
1531 //#define ROUND_UP 10
1533 static void swfoutput_setlinewidth(gfxdevice_t*dev, double _linewidth)
1535 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1536 if(i->linewidth == (U16)(_linewidth*20+19.0/20.0))
1540 i->linewidth = (U16)(_linewidth*20+19.0/20.0);
1544 static void drawlink(gfxdevice_t*dev, ActionTAG*,ActionTAG*, gfxline_t*points, char mouseover, const char*url);
1545 static void swfoutput_namedlink(gfxdevice_t*dev, char*name, gfxline_t*points);
1546 static void swfoutput_linktopage(gfxdevice_t*dev, int page, gfxline_t*points);
1547 static void swfoutput_linktourl(gfxdevice_t*dev, const char*url, gfxline_t*points);
1549 /*void swfoutput_drawlink(gfxdevice_t*dev, char*url, gfxline_t*points)
1551 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1552 dev->drawlink(dev, points, url);
1555 void swf_drawlink(gfxdevice_t*dev, gfxline_t*points, const char*url)
1557 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1559 if(!strncmp("http://pdf2swf:", url, 15)) {
1560 char*tmp = strdup(url);
1561 int l = strlen(tmp);
1564 swfoutput_namedlink(dev, tmp+15, points);
1567 } else if(!strncmp("page", url, 4)) {
1570 if(url[t]<'0' || url[t]>'9')
1573 int page = atoi(&url[4]);
1574 if(page<0) page = 0;
1575 swfoutput_linktopage(dev, page, points);
1578 swfoutput_linktourl(dev, url, points);
1581 void swfoutput_linktourl(gfxdevice_t*dev, const char*url, gfxline_t*points)
1583 ActionTAG* actions = 0;
1584 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1590 if(i->config_externallinkfunction) {
1591 actions = action_PushString(actions, url); //parameter
1592 actions = action_PushInt(actions, 1); //number of parameters (1)
1593 actions = action_PushString(actions, i->config_externallinkfunction); //function name
1594 actions = action_CallFunction(actions);
1595 } else if(!i->config_linktarget) {
1596 if(!i->config_opennewwindow)
1597 actions = action_GetUrl(actions, url, "_parent");
1599 actions = action_GetUrl(actions, url, "_this");
1601 actions = action_GetUrl(actions, url, i->config_linktarget);
1603 actions = action_End(actions);
1605 drawlink(dev, actions, 0, points, 0, url);
1607 void swfoutput_linktopage(gfxdevice_t*dev, int page, gfxline_t*points)
1609 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1610 ActionTAG* actions = 0;
1617 if(!i->config_internallinkfunction) {
1618 actions = action_GotoFrame(actions, page-1);
1619 actions = action_End(actions);
1621 actions = action_PushInt(actions, page); //parameter
1622 actions = action_PushInt(actions, 1); //number of parameters (1)
1623 actions = action_PushString(actions, i->config_internallinkfunction); //function name
1624 actions = action_CallFunction(actions);
1625 actions = action_End(actions);
1629 sprintf(name, "page%d", page);
1631 drawlink(dev, actions, 0, points, 0, name);
1634 /* Named Links (a.k.a. Acrobatmenu) are used to implement various gadgets
1635 of the viewer objects, like subtitles, index elements etc.
1637 void swfoutput_namedlink(gfxdevice_t*dev, char*name, gfxline_t*points)
1639 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1640 ActionTAG *actions1,*actions2;
1641 char*tmp = strdup(name);
1649 if(!strncmp(tmp, "call:", 5))
1651 char*x = strchr(&tmp[5], ':');
1653 actions1 = action_PushInt(0, 0); //number of parameters (0)
1654 actions1 = action_PushString(actions1, &tmp[5]); //function name
1655 actions1 = action_CallFunction(actions1);
1656 actions1 = action_End(actions1);
1659 actions1 = action_PushString(0, x+1); //parameter
1660 actions1 = action_PushInt(actions1, 1); //number of parameters (1)
1661 actions1 = action_PushString(actions1, &tmp[5]); //function name
1662 actions1 = action_CallFunction(actions1);
1663 actions1 = action_End(actions1);
1665 actions2 = action_End(0);
1670 actions1 = action_PushString(0, "/:subtitle");
1671 actions1 = action_PushString(actions1, name);
1672 actions1 = action_SetVariable(actions1);
1673 actions1 = action_End(actions1);
1675 actions2 = action_PushString(0, "/:subtitle");
1676 actions2 = action_PushString(actions2, "");
1677 actions2 = action_SetVariable(actions2);
1678 actions2 = action_End(actions2);
1681 drawlink(dev, actions1, actions2, points, mouseover, name);
1683 swf_ActionFree(actions1);
1684 swf_ActionFree(actions2);
1688 static void drawgfxline(gfxdevice_t*dev, gfxline_t*line, int fill)
1690 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1691 gfxcoord_t lastx=0,lasty=0,px=0,py=0;
1693 int lines= 0, splines=0;
1700 /* check whether the next segment is zero */
1701 if(line->type == gfx_moveTo) {
1702 moveto(dev, i->tag, line->x, line->y);
1703 px = lastx = line->x;
1704 py = lasty = line->y;
1706 } if(line->type == gfx_lineTo) {
1707 lineto(dev, i->tag, line->x, line->y);
1712 } else if(line->type == gfx_splineTo) {
1714 s.x = line->sx;p.x = line->x;
1715 s.y = line->sy;p.y = line->y;
1716 splineto(dev, i->tag, s, p);
1724 msg("<trace> drawgfxline, %d lines, %d splines", lines, splines);
1728 static void drawlink(gfxdevice_t*dev, ActionTAG*actions1, ActionTAG*actions2, gfxline_t*points, char mouseover, const char*url)
1730 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1739 int buttonid = getNewID(dev);
1740 gfxbbox_t bbox = gfxline_getbbox(points);
1743 myshapeid = getNewID(dev);
1744 i->tag = swf_InsertTag(i->tag,ST_DEFINESHAPE3);
1745 swf_ShapeNew(&i->shape);
1746 rgb.r = rgb.b = rgb.a = rgb.g = 0;
1747 fsid = swf_ShapeAddSolidFillStyle(i->shape,&rgb);
1748 swf_SetU16(i->tag, myshapeid);
1749 r.xmin = (int)(bbox.xmin*20);
1750 r.ymin = (int)(bbox.ymin*20);
1751 r.xmax = (int)(bbox.xmax*20);
1752 r.ymax = (int)(bbox.ymax*20);
1753 r = swf_ClipRect(i->pagebbox, r);
1754 swf_SetRect(i->tag,&r);
1755 swf_SetShapeStyles(i->tag,i->shape);
1756 swf_ShapeCountBits(i->shape,NULL,NULL);
1757 swf_SetShapeBits(i->tag,i->shape);
1758 swf_ShapeSetAll(i->tag,i->shape,/*x*/0,/*y*/0,0,fsid,0);
1759 i->swflastx = i->swflasty = 0;
1760 drawgfxline(dev, points, 1);
1761 swf_ShapeSetEnd(i->tag);
1764 myshapeid2 = getNewID(dev);
1765 i->tag = swf_InsertTag(i->tag,ST_DEFINESHAPE3);
1766 swf_ShapeNew(&i->shape);
1768 rgb = i->config_linkcolor;
1770 fsid = swf_ShapeAddSolidFillStyle(i->shape,&rgb);
1771 swf_SetU16(i->tag, myshapeid2);
1772 r.xmin = (int)(bbox.xmin*20);
1773 r.ymin = (int)(bbox.ymin*20);
1774 r.xmax = (int)(bbox.xmax*20);
1775 r.ymax = (int)(bbox.ymax*20);
1776 r = swf_ClipRect(i->pagebbox, r);
1777 swf_SetRect(i->tag,&r);
1778 swf_SetShapeStyles(i->tag,i->shape);
1779 swf_ShapeCountBits(i->shape,NULL,NULL);
1780 swf_SetShapeBits(i->tag,i->shape);
1781 swf_ShapeSetAll(i->tag,i->shape,/*x*/0,/*y*/0,0,fsid,0);
1782 i->swflastx = i->swflasty = 0;
1783 drawgfxline(dev, points, 1);
1784 swf_ShapeSetEnd(i->tag);
1788 i->tag = swf_InsertTag(i->tag,ST_DEFINEBUTTON);
1789 swf_SetU16(i->tag,buttonid); //id
1790 swf_ButtonSetFlags(i->tag, 0); //menu=no
1791 swf_ButtonSetRecord(i->tag,0x01,myshapeid,i->depth,0,0);
1792 swf_ButtonSetRecord(i->tag,0x02,myshapeid2,i->depth,0,0);
1793 swf_ButtonSetRecord(i->tag,0x04,myshapeid2,i->depth,0,0);
1794 swf_ButtonSetRecord(i->tag,0x08,myshapeid,i->depth,0,0);
1795 swf_SetU8(i->tag,0);
1796 swf_ActionSet(i->tag,actions1);
1797 swf_SetU8(i->tag,0);
1801 i->tag = swf_InsertTag(i->tag,ST_DEFINEBUTTON2);
1802 swf_SetU16(i->tag,buttonid); //id
1803 swf_ButtonSetFlags(i->tag, 0); //menu=no
1804 swf_ButtonSetRecord(i->tag,0x01,myshapeid,i->depth,0,0);
1805 swf_ButtonSetRecord(i->tag,0x02,myshapeid2,i->depth,0,0);
1806 swf_ButtonSetRecord(i->tag,0x04,myshapeid2,i->depth,0,0);
1807 swf_ButtonSetRecord(i->tag,0x08,myshapeid,i->depth,0,0);
1808 swf_SetU8(i->tag,0); // end of button records
1809 swf_ButtonSetCondition(i->tag, BC_IDLE_OVERUP);
1810 swf_ActionSet(i->tag,actions1);
1812 swf_ButtonSetCondition(i->tag, BC_OVERUP_IDLE);
1813 swf_ActionSet(i->tag,actions2);
1814 swf_SetU8(i->tag,0);
1815 swf_ButtonPostProcess(i->tag, 2);
1817 swf_SetU8(i->tag,0);
1818 swf_ButtonPostProcess(i->tag, 1);
1821 const char* name = 0;
1822 if(i->config_linknameurl) {
1826 msg("<trace> Placing link ID %d", buttonid);
1827 i->tag = swf_InsertTag(i->tag,ST_PLACEOBJECT2);
1829 if(posx!=0 || posy!=0) {
1831 p.x = (int)(posx*20);
1832 p.y = (int)(posy*20);
1833 p = swf_TurnPoint(p, &i->page_matrix);
1838 swf_ObjectPlace(i->tag, buttonid, getNewDepth(dev),&m,0,name);
1840 swf_ObjectPlace(i->tag, buttonid, getNewDepth(dev),&i->page_matrix,0,name);
1847 for(t=0;t<picpos;t++)
1849 if(pic_xids[t] == xid &&
1850 pic_yids[t] == yid) {
1851 width = pic_width[t];
1852 height = pic_height[t];
1856 pic_ids[picpos] = swfoutput_drawimagelosslessN(&output, pic, pal, width, height, x1,y1,x2,y2,x3,y3,x4,y4, numpalette);
1857 pic_xids[picpos] = xid;
1858 pic_yids[picpos] = yid;
1859 pic_width[picpos] = width;
1860 pic_height[picpos] = height;
1863 pic[width*y+x] = buf[0];
1867 xid += pal[1].r*3 + pal[1].g*11 + pal[1].b*17;
1868 yid += pal[1].r*7 + pal[1].g*5 + pal[1].b*23;
1872 xid += x*r+x*b*3+x*g*7+x*a*11;
1873 yid += y*r*3+y*b*17+y*g*19+y*a*11;
1875 for(t=0;t<picpos;t++)
1877 if(pic_xids[t] == xid &&
1878 pic_yids[t] == yid) {
1887 int swf_setparameter(gfxdevice_t*dev, const char*name, const char*value)
1889 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1891 msg("<trace> swfdevice: %s=%s", name, value);
1892 if(!strcmp(name, "jpegsubpixels")) {
1893 i->config_jpegsubpixels = atof(value);
1894 } else if(!strcmp(name, "ppmsubpixels")) {
1895 i->config_ppmsubpixels = atof(value);
1896 } else if(!strcmp(name, "subpixels")) {
1897 i->config_ppmsubpixels = i->config_jpegsubpixels = atof(value);
1898 } else if(!strcmp(name, "drawonlyshapes")) {
1899 i->config_drawonlyshapes = atoi(value);
1900 } else if(!strcmp(name, "ignoredraworder")) {
1901 i->config_ignoredraworder = atoi(value);
1902 } else if(!strcmp(name, "mark")) {
1903 if(!value || !value[0]) {
1904 if(i->mark) free(i->mark);
1908 i->mark = strdup("...");
1909 for(t=0;t<3;t++) if(value[t]) i->mark[t] = value[t];
1911 } else if(!strcmp(name, "filloverlap")) {
1912 i->config_filloverlap = atoi(value);
1913 } else if(!strcmp(name, "linksopennewwindow")) {
1914 i->config_opennewwindow = atoi(value);
1915 } else if(!strcmp(name, "opennewwindow")) {
1916 i->config_opennewwindow = atoi(value);
1917 } else if(!strcmp(name, "storeallcharacters")) {
1918 i->config_storeallcharacters = atoi(value);
1919 } else if(!strcmp(name, "enablezlib")) {
1920 i->config_enablezlib = atoi(value);
1921 } else if(!strcmp(name, "bboxvars")) {
1922 i->config_bboxvars = atoi(value);
1923 } else if(!strcmp(name, "frameresets")) {
1924 i->config_frameresets = atoi(value);
1925 } else if(!strcmp(name, "showclipshapes")) {
1926 i->config_showclipshapes = atoi(value);
1927 } else if(!strcmp(name, "reordertags")) {
1928 i->config_reordertags = atoi(value);
1929 } else if(!strcmp(name, "internallinkfunction")) {
1930 i->config_internallinkfunction = strdup(value);
1931 } else if(!strcmp(name, "externallinkfunction")) {
1932 i->config_externallinkfunction = strdup(value);
1933 } else if(!strcmp(name, "linkfunction")) { //sets both internallinkfunction and externallinkfunction
1934 i->config_internallinkfunction = strdup(value);
1935 i->config_externallinkfunction = strdup(value);
1936 } else if(!strcmp(name, "disable_polygon_conversion")) {
1937 i->config_disable_polygon_conversion = atoi(value);
1938 } else if(!strcmp(name, "normalize_polygon_positions")) {
1939 i->config_normalize_polygon_positions = atoi(value);
1940 } else if(!strcmp(name, "wxwindowparams")) {
1941 i->config_watermark = atoi(value);
1942 } else if(!strcmp(name, "insertstop")) {
1943 i->config_insertstoptag = atoi(value);
1944 } else if(!strcmp(name, "protect")) {
1945 i->config_protect = atoi(value);
1946 if(i->config_protect && i->tag) {
1947 i->tag = swf_InsertTag(i->tag, ST_PROTECT);
1949 } else if(!strcmp(name, "flashversion")) {
1950 i->config_flashversion = atoi(value);
1952 i->swf->fileVersion = i->config_flashversion;
1954 } else if(!strcmp(name, "framerate")) {
1955 i->config_framerate = atof(value);
1957 i->swf->frameRate = i->config_framerate*0x100;
1959 } else if(!strcmp(name, "minlinewidth")) {
1960 i->config_minlinewidth = atof(value);
1961 } else if(!strcmp(name, "caplinewidth")) {
1962 i->config_caplinewidth = atof(value);
1963 } else if(!strcmp(name, "linktarget")) {
1964 i->config_linktarget = strdup(value);
1965 } else if(!strcmp(name, "dumpfonts")) {
1966 i->config_dumpfonts = atoi(value);
1967 } else if(!strcmp(name, "animate")) {
1968 i->config_animate = atoi(value);
1969 } else if(!strcmp(name, "simpleviewer")) {
1970 i->config_simpleviewer = atoi(value);
1971 } else if(!strcmp(name, "next_bitmap_is_jpeg")) {
1973 } else if(!strcmp(name, "jpegquality")) {
1974 int val = atoi(value);
1976 if(val>101) val=101;
1977 i->config_jpegquality = val;
1978 } else if(!strcmp(name, "splinequality")) {
1979 int v = atoi(value);
1980 v = 500-(v*5); // 100% = 0.25 pixel, 0% = 25 pixel
1982 i->config_splinemaxerror = v;
1983 } else if(!strcmp(name, "fontquality")) {
1984 int v = atoi(value);
1985 v = 500-(v*5); // 100% = 0.25 pixel, 0% = 25 pixel
1987 i->config_fontsplinemaxerror = v;
1988 } else if(!strcmp(name, "linkcolor")) {
1989 if(strlen(value)!=8) {
1990 fprintf(stderr, "Unknown format for option 'linkcolor'. (%s <-> RRGGBBAA)\n", value);
1993 # define NIBBLE(s) (((s)>='0' && (s)<='9')?((s)-'0'):((s)&0x0f)+9)
1994 i->config_linkcolor.r = NIBBLE(value[0])<<4 | NIBBLE(value[1]);
1995 i->config_linkcolor.g = NIBBLE(value[2])<<4 | NIBBLE(value[3]);
1996 i->config_linkcolor.b = NIBBLE(value[4])<<4 | NIBBLE(value[5]);
1997 i->config_linkcolor.a = NIBBLE(value[6])<<4 | NIBBLE(value[7]);
1998 } else if(!strcmp(name, "help")) {
1999 printf("\nSWF layer options:\n");
2000 printf("jpegsubpixels=<pixels> resolution adjustment for jpeg images (same as jpegdpi, but in pixels)\n");
2001 printf("ppmsubpixels=<pixels resolution adjustment for lossless images (same as ppmdpi, but in pixels)\n");
2002 printf("subpixels=<pixels> shortcut for setting both jpegsubpixels and ppmsubpixels\n");
2003 printf("drawonlyshapes convert everything to shapes (currently broken)\n");
2004 printf("ignoredraworder allow to perform a few optimizations for creating smaller SWFs\n");
2005 printf("linksopennewwindow make links open a new browser window\n");
2006 printf("linktarget target window name of new links\n");
2007 printf("linkcolor=<color) color of links (format: RRGGBBAA)\n");
2008 printf("linknameurl Link buttons will be named like the URL they refer to (handy for iterating through links with actionscript)\n");
2009 printf("storeallcharacters don't reduce the fonts to used characters in the output file\n");
2010 printf("enablezlib switch on zlib compression (also done if flashversion>=7)\n");
2011 printf("bboxvars store the bounding box of the SWF file in actionscript variables\n");
2012 printf("reordertags=0/1 (default: 1) perform some tag optimizations\n");
2013 printf("internallinkfunction=<name> when the user clicks a internal link (to a different page) in the converted file, this actionscript function is called\n");
2014 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");
2015 printf("disable_polygon_conversion never convert strokes to polygons (will remove capstyles and joint styles)\n");
2016 printf("caplinewidth=<width> the minimum thichness a line needs to have so that capstyles become visible (and are converted)\n");
2017 printf("insertstop put an ActionScript \"STOP\" tag in every frame\n");
2018 printf("protect add a \"protect\" tag to the file, to prevent loading in the Flash editor\n");
2019 printf("flashversion=<version> the SWF fileversion (6)\n");
2020 printf("framerate=<fps> SWF framerate\n");
2021 printf("minlinewidth=<width> convert horizontal/vertical boxes smaller than this width to lines (0.05) \n");
2022 printf("simpleviewer Add next/previous buttons to the SWF\n");
2023 printf("animate insert a showframe tag after each placeobject (animate draw order of PDF files)\n");
2024 printf("jpegquality=<quality> set compression quality of jpeg images\n");
2025 printf("splinequality=<value> Set the quality of spline convertion to value (0-100, default: 100).\n");
2032 // --------------------------------------------------------------------
2034 static CXFORM gfxcxform_to_cxform(gfxcxform_t* c)
2037 swf_GetCXForm(0, &cx, 1);
2040 if(c->rg!=0 || c->rb!=0 || c->ra!=0 ||
2041 c->gr!=0 || c->gb!=0 || c->ga!=0 ||
2042 c->br!=0 || c->bg!=0 || c->ba!=0 ||
2043 c->ar!=0 || c->ag!=0 || c->ab!=0)
2044 msg("<warning> CXForm not SWF-compatible");
2046 cx.a0 = (S16)(c->aa*256);
2047 cx.r0 = (S16)(c->rr*256);
2048 cx.g0 = (S16)(c->gg*256);
2049 cx.b0 = (S16)(c->bb*256);
2058 static int imageInCache(gfxdevice_t*dev, void*data, int width, int height)
2062 static void addImageToCache(gfxdevice_t*dev, void*data, int width, int height)
2066 static int add_image(swfoutput_internal*i, gfximage_t*img, int targetwidth, int targetheight, int* newwidth, int* newheight)
2068 gfxdevice_t*dev = i->dev;
2070 RGBA*mem = (RGBA*)img->data;
2072 int sizex = img->width;
2073 int sizey = img->height;
2074 int is_jpeg = i->jpeg;
2077 int newsizex=sizex, newsizey=sizey;
2080 if(is_jpeg && i->config_jpegsubpixels) {
2081 newsizex = (int)(targetwidth*i->config_jpegsubpixels + 0.5);
2082 newsizey = (int)(targetheight*i->config_jpegsubpixels + 0.5);
2083 } else if(!is_jpeg && i->config_ppmsubpixels) {
2084 newsizex = (int)(targetwidth*i->config_ppmsubpixels + 0.5);
2085 newsizey = (int)(targetheight*i->config_ppmsubpixels + 0.5);
2089 if(sizex<=0 || sizey<=0)
2096 /* TODO: cache images */
2098 if(newsizex<sizex || newsizey<sizey) {
2099 msg("<verbose> Scaling %dx%d image to %dx%d", sizex, sizey, newsizex, newsizey);
2100 newpic = swf_ImageScale(mem, sizex, sizey, newsizex, newsizey);
2101 *newwidth = sizex = newsizex;
2102 *newheight = sizey = newsizey;
2105 *newwidth = newsizex = sizex;
2106 *newheight = newsizey = sizey;
2109 int num_colors = swf_ImageGetNumberOfPaletteEntries(mem,sizex,sizey,0);
2110 int has_alpha = swf_ImageHasAlpha(mem,sizex,sizey);
2112 msg("<verbose> Drawing %dx%d %s%simage (id %d) at size %dx%d (%dx%d), %s%d colors",
2114 has_alpha?(has_alpha==2?"semi-transparent ":"transparent "):"",
2115 is_jpeg?"jpeg-":"", i->currentswfid+1,
2117 targetwidth, targetheight,
2118 /*newsizex, newsizey,*/
2119 num_colors>256?">":"", num_colors>256?256:num_colors);
2121 /*RGBA* pal = (RGBA*)rfx_alloc(sizeof(RGBA)*num_colors);
2122 swf_ImageGetNumberOfPaletteEntries(mem,sizex,sizey,pal);
2124 for(t=0;t<num_colors;t++) {
2125 printf("%02x%02x%02x%02x ",
2126 pal[t].r, pal[t].g, pal[t].b, pal[t].a);
2133 int cacheid = imageInCache(dev, mem, sizex, sizey);
2136 bitid = getNewID(dev);
2138 i->tag = swf_AddImage(i->tag, bitid, mem, sizex, sizey, i->config_jpegquality);
2139 addImageToCache(dev, mem, sizex, sizey);
2149 static SRECT gfxline_getSWFbbox(gfxline_t*line)
2151 gfxbbox_t bbox = gfxline_getbbox(line);
2153 r.xmin = (int)(bbox.xmin*20);
2154 r.ymin = (int)(bbox.ymin*20);
2155 r.xmax = (int)(bbox.xmax*20);
2156 r.ymax = (int)(bbox.ymax*20);
2160 int line_is_empty(gfxline_t*line)
2163 if(line->type != gfx_moveTo)
2170 static void swf_fillbitmap(gfxdevice_t*dev, gfxline_t*line, gfximage_t*img, gfxmatrix_t*matrix, gfxcxform_t*cxform)
2172 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
2174 if(line_is_empty(line))
2180 int targetx = (int)(sqrt(matrix->m00*matrix->m00 + matrix->m01*matrix->m01)*img->width);
2181 int targety = (int)(sqrt(matrix->m10*matrix->m10 + matrix->m11*matrix->m11)*img->height);
2183 int newwidth=0,newheight=0;
2184 int bitid = add_image(i, img, targetx, targety, &newwidth, &newheight);
2187 double fx = (double)img->width / (double)newwidth;
2188 double fy = (double)img->height / (double)newheight;
2191 m.sx = (int)(65536*20*matrix->m00*fx); m.r1 = (int)(65536*20*matrix->m10*fy);
2192 m.r0 = (int)(65536*20*matrix->m01*fx); m.sy = (int)(65536*20*matrix->m11*fy);
2193 m.tx = (int)(matrix->tx*20);
2194 m.ty = (int)(matrix->ty*20);
2197 int myshapeid = getNewID(dev);
2198 i->tag = swf_InsertTag(i->tag,ST_DEFINESHAPE);
2200 swf_ShapeNew(&shape);
2201 int fsid = swf_ShapeAddBitmapFillStyle(shape,&m,bitid,1);
2202 swf_SetU16(i->tag, myshapeid);
2203 SRECT r = gfxline_getSWFbbox(line);
2204 r = swf_ClipRect(i->pagebbox, r);
2205 swf_SetRect(i->tag,&r);
2206 swf_SetShapeStyles(i->tag,shape);
2207 swf_ShapeCountBits(shape,NULL,NULL);
2208 swf_SetShapeBits(i->tag,shape);
2209 swf_ShapeSetAll(i->tag,shape,UNDEFINED_COORD,UNDEFINED_COORD,0,fsid,0);
2210 i->swflastx = i->swflasty = UNDEFINED_COORD;
2211 drawgfxline(dev, line, 1);
2212 swf_ShapeSetEnd(i->tag);
2213 swf_ShapeFree(shape);
2215 msg("<trace> Placing image, shape ID %d, bitmap ID %d", myshapeid, bitid);
2216 i->tag = swf_InsertTag(i->tag,ST_PLACEOBJECT2);
2217 CXFORM cxform2 = gfxcxform_to_cxform(cxform);
2218 swf_ObjectPlace(i->tag,myshapeid,getNewDepth(dev),&i->page_matrix,&cxform2,NULL);
2221 static RGBA col_black = {255,0,0,0};
2223 static void drawoutline(gfxdevice_t*dev, gfxline_t*line)
2225 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
2227 int myshapeid = getNewID(dev);
2228 i->tag = swf_InsertTag(i->tag,ST_DEFINESHAPE3);
2231 swf_ShapeNew(&shape);
2232 int lsid = swf_ShapeAddLineStyle(shape,1,&col_black);
2234 swf_SetU16(i->tag,myshapeid);
2235 SRECT r = gfxline_getSWFbbox(line);
2236 r = swf_ClipRect(i->pagebbox, r);
2237 swf_SetRect(i->tag,&r);
2238 swf_SetShapeStyles(i->tag,shape);
2239 swf_ShapeCountBits(shape,NULL,NULL);
2240 swf_SetShapeBits(i->tag,shape);
2241 swf_ShapeSetAll(i->tag,shape,UNDEFINED_COORD,UNDEFINED_COORD,lsid,0,0);
2242 drawgfxline(dev, line, 1);
2243 swf_ShapeSetEnd(i->tag);
2244 swf_ShapeFree(shape);
2246 i->tag = swf_InsertTag(i->tag,ST_PLACEOBJECT2);
2247 swf_ObjectPlace(i->tag, myshapeid, getNewDepth(dev), 0,0,0);
2250 static void swf_startclip(gfxdevice_t*dev, gfxline_t*line)
2252 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
2257 if(i->clippos >= 127)
2259 msg("<warning> Too many clip levels.");
2263 if(i->config_showclipshapes)
2264 drawoutline(dev, line);
2266 int myshapeid = getNewID(dev);
2267 i->tag = swf_InsertTag(i->tag,ST_DEFINESHAPE3);
2269 memset(&col, 0, sizeof(RGBA));
2272 swf_ShapeNew(&shape);
2273 int fsid = swf_ShapeAddSolidFillStyle(shape,&col);
2275 RGBA markcol = {0,i->mark[0],i->mark[1],i->mark[2]};
2276 swf_ShapeAddSolidFillStyle(shape,&markcol);
2278 swf_SetU16(i->tag,myshapeid);
2279 SRECT r = gfxline_getSWFbbox(line);
2280 r = swf_ClipRect(i->pagebbox, r);
2281 swf_SetRect(i->tag,&r);
2282 swf_SetShapeStyles(i->tag,shape);
2283 swf_ShapeCountBits(shape,NULL,NULL);
2284 swf_SetShapeBits(i->tag,shape);
2285 swf_ShapeSetAll(i->tag,shape,UNDEFINED_COORD,UNDEFINED_COORD,0,fsid,0);
2286 i->swflastx = i->swflasty = UNDEFINED_COORD;
2287 i->shapeisempty = 1;
2288 drawgfxline(dev, line, 1);
2289 if(i->shapeisempty) {
2290 /* an empty clip shape is equivalent to a shape with no area */
2291 int x = line?line->x:0;
2292 int y = line?line->y:0;
2293 moveto(dev, i->tag, x,y);
2294 lineto(dev, i->tag, x,y);
2295 lineto(dev, i->tag, x,y);
2297 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)) {
2298 if(i->config_watermark) {
2299 gfxbbox_t r; r.xmin = r.ymin = 0;r.xmax = i->max_x;r.ymax = i->max_y;
2300 draw_watermark(dev, r, 1);
2303 swf_ShapeSetEnd(i->tag);
2304 swf_ShapeFree(shape);
2306 /* TODO: remember the bbox, and check all shapes against it */
2308 msg("<trace> Placing clip ID %d", myshapeid);
2309 i->tag = swf_InsertTag(i->tag,ST_PLACEOBJECT2);
2310 i->cliptags[i->clippos] = i->tag;
2311 i->clipshapes[i->clippos] = myshapeid;
2312 i->clipdepths[i->clippos] = getNewDepth(dev);
2316 static void swf_endclip(gfxdevice_t*dev)
2318 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
2325 msg("<error> Invalid end of clipping region");
2329 /*swf_ObjectPlaceClip(i->cliptags[i->clippos],i->clipshapes[i->clippos],i->clipdepths[i->clippos],&i->page_matrix,NULL,NULL,
2330 / * clip to depth: * / i->depth <= i->clipdepths[i->clippos]? i->depth : i->depth - 1);
2332 swf_ObjectPlaceClip(i->cliptags[i->clippos],i->clipshapes[i->clippos],i->clipdepths[i->clippos],&i->page_matrix,NULL,NULL,i->depth);
2334 static int gfxline_type(gfxline_t*line)
2340 int haszerosegments=0;
2343 if(line->type == gfx_moveTo) {
2346 } else if(line->type == gfx_lineTo) {
2350 } else if(line->type == gfx_splineTo) {
2352 if(tmpsplines>lines)
2360 if(lines==0 && splines==0) return 0;
2361 else if(lines==1 && splines==0) return 1;
2362 else if(lines==0 && splines==1) return 2;
2363 else if(splines==0) return 3;
2367 static int gfxline_has_dots(gfxline_t*line)
2375 if(line->type == gfx_moveTo) {
2376 /* test the length of the preceding line, and assume it is a dot if
2377 it's length is less than 1.0. But *only* if there's a noticable
2378 gap between the previous line and the next moveTo. (I've come
2379 across a PDF where thousands of "dots" were stringed together,
2381 int last_short_gap = short_gap;
2382 if((fabs(line->x - x) + fabs(line->y - y)) < 1.0) {
2387 if(isline && dist < 1 && !short_gap && !last_short_gap) {
2392 } else if(line->type == gfx_lineTo) {
2393 dist += fabs(line->x - x) + fabs(line->y - y);
2395 } else if(line->type == gfx_splineTo) {
2396 dist += fabs(line->sx - x) + fabs(line->sy - y) +
2397 fabs(line->x - line->sx) + fabs(line->y - line->sy);
2404 if(isline && dist < 1 && !short_gap) {
2410 static int gfxline_fix_short_edges(gfxline_t*line)
2414 if(line->type == gfx_lineTo) {
2415 if(fabs(line->x - x) + fabs(line->y - y) < 0.01) {
2418 } else if(line->type == gfx_splineTo) {
2419 if(fabs(line->sx - x) + fabs(line->sy - y) +
2420 fabs(line->x - line->sx) + fabs(line->y - line->sy) < 0.01) {
2431 static char is_inside_page(gfxdevice_t*dev, gfxcoord_t x, gfxcoord_t y)
2433 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
2434 if(x<i->min_x || x>i->max_x) return 0;
2435 if(y<i->min_y || y>i->max_y) return 0;
2439 gfxline_t* gfxline_move(gfxline_t*line, double x, double y)
2441 gfxline_t*l = line = gfxline_clone(line);
2453 //#define NORMALIZE_POLYGON_POSITIONS
2455 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)
2457 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
2458 if(line_is_empty(line))
2460 int type = gfxline_type(line);
2461 int has_dots = gfxline_has_dots(line);
2462 gfxbbox_t r = gfxline_getbbox(line);
2463 int is_outside_page = !is_inside_page(dev, r.xmin, r.ymin) || !is_inside_page(dev, r.xmax, r.ymax);
2465 /* TODO: * split line into segments, and perform this check for all segments */
2467 if(i->config_disable_polygon_conversion || type>=5 ||
2469 (width <= i->config_caplinewidth
2470 || (cap_style == gfx_capRound && joint_style == gfx_joinRound)
2471 || (cap_style == gfx_capRound && type<=2)))) {} else
2473 /* convert line to polygon */
2474 msg("<trace> draw as polygon, type=%d dots=%d", type, has_dots);
2476 gfxline_fix_short_edges(line);
2477 /* we need to convert the line into a polygon */
2478 gfxpoly_t* poly = gfxpoly_strokeToPoly(line, width, cap_style, joint_style, miterLimit);
2479 gfxline_t*gfxline = gfxpoly_to_gfxline(poly);
2480 dev->fill(dev, gfxline, color);
2481 gfxline_free(gfxline);
2486 msg("<trace> draw as stroke, type=%d dots=%d", type, has_dots);
2489 if(i->config_normalize_polygon_positions) {
2491 double startx = 0, starty = 0;
2492 if(line && line->type == gfx_moveTo) {
2496 line = gfxline_move(line, -startx, -starty);
2497 i->shapeposx = (int)(startx*20);
2498 i->shapeposy = (int)(starty*20);
2501 swfoutput_setstrokecolor(dev, color->r, color->g, color->b, color->a);
2502 swfoutput_setlinewidth(dev, width);
2505 drawgfxline(dev, line, 0);
2507 if(i->config_normalize_polygon_positions) {
2508 free(line); //account for _move
2513 static void swf_fill(gfxdevice_t*dev, gfxline_t*line, gfxcolor_t*color)
2515 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
2516 if(line_is_empty(line))
2520 gfxbbox_t r = gfxline_getbbox(line);
2521 int is_outside_page = !is_inside_page(dev, r.xmin, r.ymin) || !is_inside_page(dev, r.xmax, r.ymax);
2523 //if(i->chardatapos && i->chardata[i->chardatapos-1].color.a) {
2526 if(!i->config_ignoredraworder)
2529 if(i->config_normalize_polygon_positions) {
2531 double startx = 0, starty = 0;
2532 if(line && line->type == gfx_moveTo) {
2536 line = gfxline_move(line, -startx, -starty);
2537 i->shapeposx = (int)(startx*20);
2538 i->shapeposy = (int)(starty*20);
2541 swfoutput_setfillcolor(dev, color->r, color->g, color->b, color->a);
2544 drawgfxline(dev, line, 1);
2546 if(i->currentswfid==2 && r.xmin==0 && r.ymin==0 && r.xmax==i->max_x && r.ymax==i->max_y) {
2547 if(i->config_watermark) {
2548 draw_watermark(dev, r, 1);
2552 msg("<trace> end of swf_fill (shapeid=%d)", i->shapeid);
2554 if(i->config_normalize_polygon_positions) {
2555 free(line); //account for _move
2559 static GRADIENT* gfxgradient_to_GRADIENT(gfxgradient_t*gradient)
2562 gfxgradient_t*g = gradient;
2567 GRADIENT* swfgradient = malloc(sizeof(GRADIENT));
2568 swfgradient->num = num;
2569 swfgradient->rgba = malloc(sizeof(swfgradient->rgba[0])*num);
2570 swfgradient->ratios = malloc(sizeof(swfgradient->ratios[0])*num);
2575 swfgradient->ratios[num] = g->pos*255;
2576 swfgradient->rgba[num] = *(RGBA*)&g->color;
2583 static void swf_fillgradient(gfxdevice_t*dev, gfxline_t*line, gfxgradient_t*gradient, gfxgradienttype_t type, gfxmatrix_t*matrix)
2585 if(line_is_empty(line))
2587 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
2589 if(line_is_empty(line))
2592 GRADIENT* swfgradient = gfxgradient_to_GRADIENT(gradient);
2599 double f = type==gfxgradient_radial?4:4;
2601 m.sx = (int)(matrix->m00*20*f); m.r1 = (int)(matrix->m10*20*f);
2602 m.r0 = (int)(matrix->m01*20*f); m.sy = (int)(matrix->m11*20*f);
2603 m.tx = (int)(matrix->tx*20);
2604 m.ty = (int)(matrix->ty*20);
2607 int myshapeid = getNewID(dev);
2608 i->tag = swf_InsertTag(i->tag, ST_DEFINESHAPE2);
2610 swf_ShapeNew(&shape);
2611 int fsid = swf_ShapeAddGradientFillStyle(shape,&m,swfgradient,type==gfxgradient_radial);
2612 swf_SetU16(i->tag, myshapeid);
2613 SRECT r = gfxline_getSWFbbox(line);
2614 r = swf_ClipRect(i->pagebbox, r);
2615 swf_SetRect(i->tag,&r);
2616 swf_SetShapeStyles(i->tag,shape);
2617 swf_ShapeCountBits(shape,NULL,NULL);
2618 swf_SetShapeBits(i->tag,shape);
2619 swf_ShapeSetAll(i->tag,shape,UNDEFINED_COORD,UNDEFINED_COORD,0,fsid,0);
2620 i->swflastx = i->swflasty = UNDEFINED_COORD;
2621 drawgfxline(dev, line, 1);
2622 swf_ShapeSetEnd(i->tag);
2623 swf_ShapeFree(shape);
2625 int depth = getNewDepth(dev);
2626 msg("<trace> Placing gradient, shape ID %d, depth %d", myshapeid, depth);
2627 i->tag = swf_InsertTag(i->tag,ST_PLACEOBJECT2);
2628 swf_ObjectPlace(i->tag,myshapeid,depth,&i->page_matrix,NULL,NULL);
2630 swf_FreeGradient(swfgradient);free(swfgradient);
2633 static SWFFONT* gfxfont_to_swffont(gfxfont_t*font, const char* id)
2635 SWFFONT*swffont = (SWFFONT*)rfx_calloc(sizeof(SWFFONT));
2637 SRECT bounds = {0,0,0,0};
2639 swffont->version = 2;
2640 swffont->name = (U8*)strdup(id);
2641 swffont->layout = (SWFLAYOUT*)rfx_calloc(sizeof(SWFLAYOUT));
2642 swffont->layout->ascent = 0;
2643 swffont->layout->descent = 0;
2644 swffont->layout->leading = 0;
2645 swffont->layout->bounds = (SRECT*)rfx_calloc(sizeof(SRECT)*font->num_glyphs);
2646 swffont->encoding = FONT_ENCODING_UNICODE;
2647 swffont->numchars = font->num_glyphs;
2648 swffont->maxascii = font->max_unicode;
2649 swffont->ascii2glyph = (int*)rfx_calloc(sizeof(int)*swffont->maxascii);
2650 swffont->glyph2ascii = (U16*)rfx_calloc(sizeof(U16)*swffont->numchars);
2651 swffont->glyph = (SWFGLYPH*)rfx_calloc(sizeof(SWFGLYPH)*swffont->numchars);
2652 swffont->glyphnames = (char**)rfx_calloc(sizeof(char*)*swffont->numchars);
2653 for(t=0;t<font->max_unicode;t++) {
2654 swffont->ascii2glyph[t] = font->unicode2glyph[t];
2656 SRECT max = {0,0,0,0};
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 = 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);
2694 SRECT bbox = swf_ShapeDrawerGetBBox(&draw);
2695 swf_ExpandRect2(&max, &bbox);
2697 swffont->layout->bounds[t] = bbox;
2699 if(advance<32768.0/20) {
2700 swffont->glyph[t].advance = (int)(advance*20);
2702 //msg("<warning> Advance value overflow in glyph %d", t);
2703 swffont->glyph[t].advance = 32767;
2706 draw.dealloc(&draw);
2708 swf_ExpandRect2(&bounds, &swffont->layout->bounds[t]);
2710 for(t=0;t<font->num_glyphs;t++) {
2711 SRECT bbox = swffont->layout->bounds[t];
2713 /* if the glyph doesn't have a bounding box, use the
2714 combined bounding box (necessary e.g. for space characters) */
2715 if(!(bbox.xmin|bbox.ymin|bbox.xmax|bbox.ymax)) {
2716 swffont->layout->bounds[t] = bbox = max;
2719 /* check that the advance value is reasonable, by comparing it
2720 with the bounding box */
2721 if(bbox.xmax>0 && (bbox.xmax*10 < swffont->glyph[t].advance || !swffont->glyph[t].advance)) {
2722 if(swffont->glyph[t].advance)
2723 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);
2724 swffont->glyph[t].advance = bbox.xmax;
2726 //swffont->glyph[t].advance = bbox.xmax - bbox.xmin;
2730 /* Flash player will use the advance value from the char, and the ascent/descent values
2731 from the layout for text selection.
2732 ascent will extend the char into negative y direction, from the baseline, while descent
2733 will extend in positive y direction, also from the baseline.
2734 The baseline is defined as the y-position zero
2737 swffont->layout->ascent = -bounds.ymin;
2738 if(swffont->layout->ascent < 0)
2739 swffont->layout->ascent = 0;
2740 swffont->layout->descent = bounds.ymax;
2741 if(swffont->layout->descent < 0)
2742 swffont->layout->descent = 0;
2743 swffont->layout->leading = bounds.ymax - bounds.ymin;
2745 /* if the font has proper ascent/descent values (>0) and those define
2746 greater line spacing that what we estimated from the bounding boxes,
2747 use the font's parameters */
2748 if(font->ascent*20 > swffont->layout->ascent)
2749 swffont->layout->ascent = font->ascent*20;
2750 if(font->descent*20 > swffont->layout->descent)
2751 swffont->layout->descent = font->descent*20;
2756 static void swf_addfont(gfxdevice_t*dev, gfxfont_t*font)
2758 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
2760 if(i->swffont && i->swffont->name && !strcmp((char*)i->swffont->name,font->id))
2761 return; // the requested font is the current font
2763 fontlist_t*last=0,*l = i->fontlist;
2766 if(!strcmp((char*)l->swffont->name, font->id)) {
2767 return; // we already know this font
2771 l = (fontlist_t*)rfx_calloc(sizeof(fontlist_t));
2772 l->swffont = gfxfont_to_swffont(font, font->id);
2779 swf_FontSetID(l->swffont, getNewID(i->dev));
2781 if(getScreenLogLevel() >= LOGLEVEL_DEBUG) {
2783 // print font information
2784 msg("<debug> Font %s",font->id);
2785 msg("<debug> | ID: %d", l->swffont->id);
2786 msg("<debug> | Version: %d", l->swffont->version);
2787 msg("<debug> | Name: %s", l->swffont->name);
2788 msg("<debug> | Numchars: %d", l->swffont->numchars);
2789 msg("<debug> | Maxascii: %d", l->swffont->maxascii);
2790 msg("<debug> | Style: %d", l->swffont->style);
2791 msg("<debug> | Encoding: %d", l->swffont->encoding);
2792 for(iii=0; iii<l->swffont->numchars;iii++) {
2793 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,
2794 l->swffont->layout->bounds[iii].xmin/20.0,
2795 l->swffont->layout->bounds[iii].ymin/20.0,
2796 l->swffont->layout->bounds[iii].xmax/20.0,
2797 l->swffont->layout->bounds[iii].ymax/20.0
2800 for(t=0;t<l->swffont->maxascii;t++) {
2801 if(l->swffont->ascii2glyph[t] == iii)
2802 msg("<debug> | - maps to %d",t);
2808 static void swf_switchfont(gfxdevice_t*dev, const char*fontid)
2810 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
2812 if(i->swffont && i->swffont->name && !strcmp((char*)i->swffont->name,fontid))
2813 return; // the requested font is the current font
2815 fontlist_t*l = i->fontlist;
2817 if(!strcmp((char*)l->swffont->name, fontid)) {
2818 i->swffont = l->swffont;
2823 msg("<error> Unknown font id: %s", fontid);
2827 static void swf_drawchar(gfxdevice_t*dev, gfxfont_t*font, int glyph, gfxcolor_t*color, gfxmatrix_t*matrix)
2829 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
2831 msg("<error> swf_drawchar called (glyph %d) without font", glyph);
2834 if(!i->swffont || !i->swffont->name || strcmp((char*)i->swffont->name,font->id)) // not equal to current font
2836 /* TODO: remove the need for this (enhance getcharacterbbox so that it can cope
2837 with multiple fonts */
2839 swf_switchfont(dev, font->id); // set the current font
2842 msg("<warning> swf_drawchar: Font is NULL");
2845 if(glyph<0 || glyph>=i->swffont->numchars) {
2846 msg("<warning> No character %d in font %s (%d chars)", glyph, FIXNULL((char*)i->swffont->name), i->swffont->numchars);
2850 setfontscale(dev, matrix->m00, matrix->m01, matrix->m10, matrix->m11, matrix->tx, matrix->ty, 0);
2852 double det = i->fontmatrix.sx/65536.0 * i->fontmatrix.sy/65536.0 -
2853 i->fontmatrix.r0/65536.0 * i->fontmatrix.r1/65536.0;
2854 if(fabs(det) < 0.0005) {
2855 /* x direction equals y direction- the text is invisible */
2856 msg("<verbose> Not drawing invisible character character %d (det=%f, m=[%f %f;%f %f]\n", glyph,
2858 i->fontmatrix.sx/65536.0, i->fontmatrix.r1/65536.0,
2859 i->fontmatrix.r0/65536.0, i->fontmatrix.sy/65536.0);
2863 /*if(i->swffont->glyph[glyph].shape->bitlen <= 16) {
2864 msg("<warning> Glyph %d in current charset (%s, %d characters) is empty",
2865 glyph, FIXNULL((char*)i->swffont->name), i->swffont->numchars);
2869 /* calculate character position with respect to the current font matrix */
2870 double s = 20 * GLYPH_SCALE / det;
2871 double px = matrix->tx - i->fontmatrix.tx/20.0;
2872 double py = matrix->ty - i->fontmatrix.ty/20.0;
2873 int x = (SCOORD)(( px * i->fontmatrix.sy/65536.0 - py * i->fontmatrix.r1/65536.0)*s);
2874 int y = (SCOORD)((- px * i->fontmatrix.r0/65536.0 + py * i->fontmatrix.sx/65536.0)*s);
2875 if(x>32767 || x<-32768 || y>32767 || y<-32768) {
2876 msg("<verbose> Moving character origin to %f %f\n", matrix->tx, matrix->ty);
2878 setfontscale(dev, matrix->m00, matrix->m01, matrix->m10, matrix->m11, matrix->tx, matrix->ty, 1);
2879 /* since we just moved the char origin to the current char's position,
2880 it now has the relative position (0,0) */
2889 msg("<trace> Drawing char %d in font %d at %d,%d in color %02x%02x%02x%02x",
2890 glyph, i->swffont->id, x, y, color->r, color->g, color->b, color->a);
2892 putcharacter(dev, i->swffont->id, glyph, x, y, i->current_font_size, *(RGBA*)color);
2893 swf_FontUseGlyph(i->swffont, glyph);