3 Part of the swftools package.
5 Copyright (c) 2001,2002,2003,2004,2005 Matthias Kramm <kramm@quiss.org>
7 Swftools is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 2 of the License, or
10 (at your option) any later version.
12 Swftools is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
17 You should have received a copy of the GNU General Public License
18 along with swftools; if not, write to the Free Software
19 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
24 #include "../../config.h"
40 #include "../rfxswf.h"
41 #include "../gfxdevice.h"
42 #include "../gfxtools.h"
43 #include "../art/libart.h"
45 #include "../gfxpoly.h"
47 #define CHARDATAMAX 8192
51 typedef struct _chardata {
53 int fontid; /* TODO: use a SWFFONT instead */
60 typedef struct _fontlist
63 struct _fontlist*next;
66 typedef long int twip;
68 typedef struct _swfmatrix {
69 double m11,m12,m21,m22,m31,m32;
72 typedef struct _swfoutput_internal
74 gfxdevice_t*dev; // the gfxdevice object where this internal struct resides
76 double config_dumpfonts;
77 double config_ppmsubpixels;
78 double config_jpegsubpixels;
79 int config_opennewwindow;
80 int config_ignoredraworder;
81 int config_drawonlyshapes;
82 int config_jpegquality;
83 int config_storeallcharacters;
84 int config_enablezlib;
85 int config_insertstoptag;
87 int config_flashversion;
88 int config_reordertags;
89 int config_showclipshapes;
90 int config_splinemaxerror;
91 int config_fontsplinemaxerror;
92 int config_filloverlap;
95 int config_disable_polygon_conversion;
96 int config_normalize_polygon_positions;
97 RGBA config_linkcolor;
98 float config_minlinewidth;
99 double config_caplinewidth;
100 char* config_linktarget;
101 char*config_internallinkfunction;
102 char*config_externallinkfunction;
104 double config_framerate;
108 fontlist_t* fontlist;
145 int pic_height[1024];
151 char fillstylechanged;
153 int jpeg; //next image type
160 chardata_t chardata[CHARDATAMAX];
167 int current_font_size;
169 double lastfontm11,lastfontm12,lastfontm21,lastfontm22;
180 } swfoutput_internal;
182 static void swf_fillbitmap(gfxdevice_t*driver, gfxline_t*line, gfximage_t*img, gfxmatrix_t*move, gfxcxform_t*cxform);
183 static int swf_setparameter(gfxdevice_t*driver, const char*key, const char*value);
184 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);
185 static void swf_startclip(gfxdevice_t*dev, gfxline_t*line);
186 static void swf_endclip(gfxdevice_t*dev);
187 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);
188 static void swf_fill(gfxdevice_t*dev, gfxline_t*line, gfxcolor_t*color);
189 static void swf_fillbitmap(gfxdevice_t*dev, gfxline_t*line, gfximage_t*img, gfxmatrix_t*matrix, gfxcxform_t*cxform);
190 static void swf_fillgradient(gfxdevice_t*dev, gfxline_t*line, gfxgradient_t*gradient, gfxgradienttype_t type, gfxmatrix_t*matrix);
191 static void swf_drawchar(gfxdevice_t*dev, gfxfont_t*font, int glyph, gfxcolor_t*color, gfxmatrix_t*matrix);
192 static void swf_addfont(gfxdevice_t*dev, gfxfont_t*font);
193 static void swf_drawlink(gfxdevice_t*dev, gfxline_t*line, const char*action);
194 static void swf_startframe(gfxdevice_t*dev, int width, int height);
195 static void swf_endframe(gfxdevice_t*dev);
196 static gfxresult_t* swf_finish(gfxdevice_t*driver);
198 static swfoutput_internal* init_internal_struct()
200 swfoutput_internal*i = (swfoutput_internal*)malloc(sizeof(swfoutput_internal));
201 memset(i, 0, sizeof(swfoutput_internal));
225 i->fillstylechanged = 0;
232 i->config_dumpfonts=0;
233 i->config_ppmsubpixels=0;
234 i->config_jpegsubpixels=0;
235 i->config_opennewwindow=1;
236 i->config_ignoredraworder=0;
237 i->config_drawonlyshapes=0;
238 i->config_jpegquality=85;
239 i->config_storeallcharacters=0;
240 i->config_enablezlib=0;
241 i->config_insertstoptag=0;
242 i->config_flashversion=6;
243 i->config_framerate=0.25;
244 i->config_splinemaxerror=1;
245 i->config_fontsplinemaxerror=1;
246 i->config_filloverlap=0;
248 i->config_bboxvars=0;
249 i->config_showclipshapes=0;
250 i->config_minlinewidth=0.05;
251 i->config_caplinewidth=1;
252 i->config_linktarget=0;
253 i->config_internallinkfunction=0;
254 i->config_externallinkfunction=0;
255 i->config_reordertags=1;
257 i->config_linkcolor.r = i->config_linkcolor.g = i->config_linkcolor.b = 255;
258 i->config_linkcolor.a = 0x40;
263 static int id_error = 0;
265 static U16 getNewID(gfxdevice_t* dev)
267 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
268 if(i->currentswfid == 65535) {
270 msg("<error> ID Table overflow");
271 msg("<error> This file is too complex to render- SWF only supports 65536 shapes at once");
277 return ++i->currentswfid;
279 static U16 getNewDepth(gfxdevice_t* dev)
281 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
282 if(i->depth == 65535) {
284 msg("<error> Depth Table overflow");
285 msg("<error> This file is too complex to render- SWF only supports 65536 shapes at once");
294 static void startshape(gfxdevice_t* dev);
295 static void starttext(gfxdevice_t* dev);
296 static void endshape(gfxdevice_t* dev);
297 static void endtext(gfxdevice_t* dev);
299 typedef struct _plotxy
304 // write a move-to command into the swf
305 static int movetoxy(gfxdevice_t*dev, TAG*tag, plotxy_t p0)
307 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
308 int rx = (int)(p0.x*20);
309 int ry = (int)(p0.y*20);
310 if(rx!=i->swflastx || ry!=i->swflasty || i->fillstylechanged) {
311 swf_ShapeSetMove (tag, i->shape, rx,ry);
312 i->fillstylechanged = 0;
319 static int moveto(gfxdevice_t*dev, TAG*tag, double x, double y)
321 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
325 return movetoxy(dev, tag, p);
327 static void addPointToBBox(gfxdevice_t*dev, int px, int py)
329 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
335 swf_ExpandRect(&i->bboxrect, p);
337 swf_ExpandRect3(&i->bboxrect, p, i->linewidth*3/2);
341 /*static void plot(gfxdevice_t*dev, int x, int y, TAG*tag)
343 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
344 int width = i->linewidth/4;
348 //swf_ShapeSetLine(tag, i->shape,-width,-width);
349 //swf_ShapeSetLine(tag, i->shape,width*2,0);
350 //swf_ShapeSetLine(tag, i->shape,0,width*2);
351 //swf_ShapeSetLine(tag, i->shape,-width*2,0);
352 //swf_ShapeSetLine(tag, i->shape,0,-width*2);
353 //swf_ShapeSetLine(tag, i->shape,width,width);
356 swf_ShapeSetLine(tag, i->shape,-width,0);
357 swf_ShapeSetLine(tag, i->shape,width,-width);
358 swf_ShapeSetLine(tag, i->shape,width,width);
359 swf_ShapeSetLine(tag, i->shape,-width,width);
360 swf_ShapeSetLine(tag, i->shape,-width,-width);
361 swf_ShapeSetLine(tag, i->shape,width,0);
363 addPointToBBox(dev, x-width ,y-width);
364 addPointToBBox(dev, x+width ,y+width);
367 // write a line-to command into the swf
368 static void linetoxy(gfxdevice_t*dev, TAG*tag, plotxy_t p0)
370 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
371 int px = (int)(p0.x*20);
372 int py = (int)(p0.y*20);
373 int rx = (px-i->swflastx);
374 int ry = (py-i->swflasty);
376 swf_ShapeSetLine (tag, i->shape, rx,ry);
377 addPointToBBox(dev, i->swflastx,i->swflasty);
378 addPointToBBox(dev, px,py);
379 }/* else if(!i->fill) {
380 // treat lines of length 0 as plots, making them
381 // at least 1 twip wide so Flash will display them
382 plot(dev, i->swflastx, i->swflasty, tag);
389 static void lineto(gfxdevice_t*dev, TAG*tag, double x, double y)
394 linetoxy(dev,tag, p);
397 // write a spline-to command into the swf
398 static void splineto(gfxdevice_t*dev, TAG*tag, plotxy_t control,plotxy_t end)
400 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
401 int lastlastx = i->swflastx;
402 int lastlasty = i->swflasty;
404 int cx = ((int)(control.x*20)-i->swflastx);
405 int cy = ((int)(control.y*20)-i->swflasty);
408 int ex = ((int)(end.x*20)-i->swflastx);
409 int ey = ((int)(end.y*20)-i->swflasty);
413 if((cx || cy) && (ex || ey)) {
414 swf_ShapeSetCurve(tag, i->shape, cx,cy,ex,ey);
415 addPointToBBox(dev, lastlastx ,lastlasty );
416 addPointToBBox(dev, lastlastx+cx,lastlasty+cy);
417 addPointToBBox(dev, lastlastx+cx+ex,lastlasty+cy+ey);
418 } else if(cx || cy || ex || ey) {
419 swf_ShapeSetLine(tag, i->shape, cx+ex,cy+ey);
420 addPointToBBox(dev, lastlastx ,lastlasty );
421 addPointToBBox(dev, lastlastx+cx,lastlasty+cy);
422 addPointToBBox(dev, lastlastx+cx+ex,lastlasty+cy+ey);
428 /* write a line, given two points and the transformation
430 /*static void line(gfxdevice_t*dev, TAG*tag, plotxy_t p0, plotxy_t p1)
432 moveto(dev, tag, p0);
433 lineto(dev, tag, p1);
436 void resetdrawer(gfxdevice_t*dev)
438 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
443 static void stopFill(gfxdevice_t*dev)
445 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
446 if(i->lastwasfill!=0)
448 swf_ShapeSetStyle(i->tag,i->shape,i->linestyleid,0x8000,0);
449 i->fillstylechanged = 1;
453 static void startFill(gfxdevice_t*dev)
455 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
456 if(i->lastwasfill!=1)
458 swf_ShapeSetStyle(i->tag,i->shape,0x8000,i->fillstyleid,0);
459 i->fillstylechanged = 1;
464 static inline int colorcompare(gfxdevice_t*dev, RGBA*a,RGBA*b)
476 static SRECT getcharacterbbox(gfxdevice_t*dev, SWFFONT*font, MATRIX* m)
478 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
481 memset(&r, 0, sizeof(r));
484 if(debug) printf("\n");
485 for(t=0;t<i->chardatapos;t++)
487 if(i->chardata[t].fontid != font->id) {
488 msg("<error> Internal error: fontid %d != fontid %d", i->chardata[t].fontid, font->id);
491 SRECT b = font->layout->bounds[i->chardata[t].charid];
492 b.xmin *= i->chardata[t].size;
493 b.ymin *= i->chardata[t].size;
494 b.xmax *= i->chardata[t].size;
495 b.ymax *= i->chardata[t].size;
497 /* divide by 1024, rounding xmax/ymax up */
498 b.xmax += 1023; b.ymax += 1023; b.xmin /= 1024; b.ymin /= 1024; b.xmax /= 1024; b.ymax /= 1024;
500 b.xmin += i->chardata[t].x;
501 b.ymin += i->chardata[t].y;
502 b.xmax += i->chardata[t].x;
503 b.ymax += i->chardata[t].y;
505 /* until we solve the INTERNAL_SCALING problem (see below)
506 make sure the bounding box is big enough */
512 b = swf_TurnRect(b, m);
514 if(debug) printf("(%f,%f,%f,%f) -> (%f,%f,%f,%f) [font %d/%d, char %d]\n",
515 font->layout->bounds[i->chardata[t].charid].xmin/20.0,
516 font->layout->bounds[i->chardata[t].charid].ymin/20.0,
517 font->layout->bounds[i->chardata[t].charid].xmax/20.0,
518 font->layout->bounds[i->chardata[t].charid].ymax/20.0,
523 i->chardata[t].fontid,
525 i->chardata[t].charid
527 swf_ExpandRect2(&r, &b);
529 if(debug) printf("-----> (%f,%f,%f,%f)\n",
537 static void putcharacters(gfxdevice_t*dev, TAG*tag)
539 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
543 color.r = i->chardata[0].color.r^255;
552 int charadvance[128];
555 int glyphbits=1; //TODO: can this be zero?
558 if(tag->id != ST_DEFINETEXT &&
559 tag->id != ST_DEFINETEXT2) {
560 msg("<error> internal error: putcharacters needs an text tag, not %d\n",tag->id);
563 if(!i->chardatapos) {
564 msg("<warning> putcharacters called with zero characters");
567 for(pass = 0; pass < 2; pass++)
577 advancebits++; // add sign bit
578 swf_SetU8(tag, glyphbits);
579 swf_SetU8(tag, advancebits);
582 for(t=0;t<=i->chardatapos;t++)
584 if(lastfontid != i->chardata[t].fontid ||
585 lastx!=i->chardata[t].x ||
586 lasty!=i->chardata[t].y ||
587 !colorcompare(dev,&color, &i->chardata[t].color) ||
589 lastsize != i->chardata[t].size ||
592 if(charstorepos && pass==0)
595 for(s=0;s<charstorepos;s++)
597 while(charids[s]>=(1<<glyphbits))
599 while(charadvance[s]>=(1<<advancebits))
603 if(charstorepos && pass==1)
605 tag->writeBit = 0; // Q&D
606 swf_SetBits(tag, 0, 1); // GLYPH Record
607 swf_SetBits(tag, charstorepos, 7); // number of glyphs
609 for(s=0;s<charstorepos;s++)
611 swf_SetBits(tag, charids[s], glyphbits);
612 swf_SetBits(tag, charadvance[s], advancebits);
617 if(pass == 1 && t<i->chardatapos)
623 if(lastx != i->chardata[t].x ||
624 lasty != i->chardata[t].y)
626 newx = i->chardata[t].x;
627 newy = i->chardata[t].y;
633 if(!colorcompare(dev,&color, &i->chardata[t].color))
635 color = i->chardata[t].color;
638 font.id = i->chardata[t].fontid;
639 if(lastfontid != i->chardata[t].fontid || lastsize != i->chardata[t].size)
642 tag->writeBit = 0; // Q&D
643 swf_TextSetInfoRecord(tag, newfont, i->chardata[t].size, newcolor, newx,newy);
646 lastfontid = i->chardata[t].fontid;
647 lastx = i->chardata[t].x;
648 lasty = i->chardata[t].y;
649 lastsize = i->chardata[t].size;
652 if(t==i->chardatapos)
656 int nextt = t==i->chardatapos-1?t:t+1;
657 int rel = i->chardata[nextt].x-i->chardata[t].x;
658 if(rel>=0 && (rel<(1<<(advancebits-1)) || pass==0)) {
660 lastx=i->chardata[nextt].x;
664 lastx=i->chardata[t].x;
666 charids[charstorepos] = i->chardata[t].charid;
667 charadvance[charstorepos] = advance;
674 static void putcharacter(gfxdevice_t*dev, int fontid, int charid, int x,int y, int size, RGBA color)
676 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
677 if(i->chardatapos == CHARDATAMAX)
679 msg("<warning> Character buffer too small. SWF will be slightly bigger");
683 i->chardata[i->chardatapos].fontid = fontid;
684 i->chardata[i->chardatapos].charid = charid;
685 i->chardata[i->chardatapos].x = x;
686 i->chardata[i->chardatapos].y = y;
687 i->chardata[i->chardatapos].color = color;
688 i->chardata[i->chardatapos].size = size;
692 /* Notice: we can only put chars in the range -1639,1638 (-32768/20,32768/20).
693 So if we set this value to high, the char coordinates will overflow.
694 If we set it to low, however, the char positions will be inaccurate */
695 #define GLYPH_SCALE 1
697 static void endtext(gfxdevice_t*dev)
699 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
703 i->tag = swf_InsertTag(i->tag,ST_DEFINETEXT2);
704 swf_SetU16(i->tag, i->textid);
707 r = getcharacterbbox(dev, i->swffont, &i->fontmatrix);
708 r = swf_ClipRect(i->pagebbox, r);
709 swf_SetRect(i->tag,&r);
711 swf_SetMatrix(i->tag,&i->fontmatrix);
713 msg("<trace> Placing text (%d characters) as ID %d", i->chardatapos, i->textid);
715 putcharacters(dev, i->tag);
718 if(i->swf->fileVersion >= 8) {
719 i->tag = swf_InsertTag(i->tag, ST_CSMTEXTSETTINGS);
720 swf_SetU16(i->tag, i->textid);
722 //swf_SetU8(i->tag, /*subpixel grid*/(2<<3)|/*flashtype*/0x40);
723 swf_SetU8(i->tag, /*grid*/(1<<3)|/*flashtype*/0x40);
724 //swf_SetU8(i->tag, /*no grid*/(0<<3)|/*flashtype*/0x40);
726 swf_SetU32(i->tag, 0);//thickness
727 swf_SetU32(i->tag, 0);//sharpness
728 swf_SetU8(i->tag, 0);//reserved
730 i->tag = swf_InsertTag(i->tag,ST_PLACEOBJECT2);
732 swf_ObjectPlace(i->tag,i->textid,getNewDepth(dev),&i->page_matrix,NULL,NULL);
736 /* set's the matrix which is to be applied to characters drawn by swfoutput_drawchar() */
737 static void setfontscale(gfxdevice_t*dev,double m11,double m12, double m21,double m22,double x, double y, char force)
743 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
744 if(i->lastfontm11 == m11 &&
745 i->lastfontm12 == m12 &&
746 i->lastfontm21 == m21 &&
747 i->lastfontm22 == m22 && !force)
752 i->lastfontm11 = m11;
753 i->lastfontm12 = m12;
754 i->lastfontm21 = m21;
755 i->lastfontm22 = m22;
757 double xsize = sqrt(m11*m11 + m12*m12);
758 double ysize = sqrt(m21*m21 + m22*m22);
759 i->current_font_size = (xsize>ysize?xsize:ysize)*1;
760 if(i->current_font_size < 1)
761 i->current_font_size = 1;
762 double ifs = 1.0 / (i->current_font_size*GLYPH_SCALE);
765 m.sx = (S32)((m11*ifs)*65536); m.r1 = (S32)((m21*ifs)*65536);
766 m.r0 = (S32)((m12*ifs)*65536); m.sy = (S32)((m22*ifs)*65536);
767 /* this is the position of the first char to set a new fontmatrix-
768 we hope that it's close enough to all other characters using the
769 font, so we use its position as origin for the matrix */
775 static int watermark2_width=47;
776 static int watermark2_height=11;
777 static int watermark2[47] = {95,1989,71,0,2015,337,1678,0,2015,5,1921,320,1938,25,2006,1024,
778 1042,21,13,960,1039,976,8,2000,1359,1088,31,1989,321,1728,0,1152,
779 1344,832,0,1984,0,896,1088,1088,896,0,1984,128,256,512,1984};
781 static void draw_watermark(gfxdevice_t*dev, gfxbbox_t r, char drawall)
783 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
784 double wx = r.xmax / 5.0;
785 double tx = r.xmax*4.0 / 5.0;
786 double ty = r.ymax-wx*watermark2_height/watermark2_width;
787 double sx = (r.xmax - tx) / watermark2_width;
788 double sy = (r.ymax - ty) / watermark2_height;
791 if(ty > 0 && px > 1.0 && py > 1.0) {
793 for(y=0;y<watermark2_height;y++)
794 for(x=0;x<watermark2_width;x++) {
795 if(((watermark2[x]>>y)&1)) {
796 if(!drawall && rand()%5)
798 unsigned int b = rand();
799 moveto(dev, i->tag, x*sx+tx+((b>>1)&1)/20.0, y*sy+ty+((b>>3)&1)/20.0);
800 lineto(dev, i->tag, x*sx+px+tx+((b>>2)&1)/20.0, y*sy+ty+((b>>3)&1)/20.0);
801 lineto(dev, i->tag, x*sx+px+tx+((b>>2)&1)/20.0, y*sy+py+ty+((b>>4)&1)/20.0);
802 lineto(dev, i->tag, x*sx+tx+((b>>1)&1)/20.0, y*sy+py+ty+((b>>4)&1)/20.0);
803 lineto(dev, i->tag, x*sx+tx+((b>>1)&1)/20.0, y*sy+ty+((b>>3)&1)/20.0);
809 static void swfoutput_setfillcolor(gfxdevice_t* dev, U8 r, U8 g, U8 b, U8 a)
811 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
812 if(i->fillrgb.r == r &&
815 i->fillrgb.a == a) return;
824 static void insert_watermark(gfxdevice_t*dev, char drawall)
826 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
827 if(!drawall && i->watermarks>20)
833 swfoutput_setfillcolor(dev, 0,0,255,192);
835 swfoutput_setfillcolor(dev, rand(),rand(),rand(),(rand()&127)+128);
840 gfxbbox_t r; r.xmin = r.ymin = 0;
843 draw_watermark(dev, r, drawall);
849 static void endpage(gfxdevice_t*dev)
851 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
862 if(i->config_watermark) {
863 insert_watermark(dev, 1);
869 void swf_startframe(gfxdevice_t*dev, int width, int height)
871 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
872 if(!i->firstpage && !i->pagefinished)
874 msg("<verbose> Starting new SWF page of size %dx%d", width, height);
876 swf_GetMatrix(0, &i->page_matrix);
877 i->page_matrix.tx = 0;
878 i->page_matrix.ty = 0;
885 /* create a bbox structure with the page size. This is used
886 for clipping shape and text bounding boxes. As we don't want to
887 generate bounding boxes which extend beyond the movie size (in
888 order to not confuse Flash), we clip everything against i->pagebbox */
889 i->pagebbox.xmin = 0;
890 i->pagebbox.ymin = 0;
891 i->pagebbox.xmax = width*20;
892 i->pagebbox.ymax = height*20;
894 /* increase SWF's bounding box */
895 swf_ExpandRect2(&i->swf->movieSize, &i->pagebbox);
897 i->lastframeno = i->frameno;
902 void swf_endframe(gfxdevice_t*dev)
904 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
909 if( (i->swf->fileVersion <= 8) && (i->config_insertstoptag) ) {
911 atag = action_Stop(atag);
912 atag = action_End(atag);
913 i->tag = swf_InsertTag(i->tag,ST_DOACTION);
914 swf_ActionSet(i->tag,atag);
916 i->tag = swf_InsertTag(i->tag,ST_SHOWFRAME);
919 for(i->depth;i->depth>i->startdepth;i->depth--) {
920 i->tag = swf_InsertTag(i->tag,ST_REMOVEOBJECT2);
921 swf_SetU16(i->tag,i->depth);
923 i->depth = i->startdepth;
926 static void setBackground(gfxdevice_t*dev, int x1, int y1, int x2, int y2)
928 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
930 rgb.a = rgb.r = rgb.g = rgb.b = 0xff;
934 int shapeid = getNewID(dev);
939 i->tag = swf_InsertTag(i->tag, ST_DEFINESHAPE);
941 fs1 = swf_ShapeAddSolidFillStyle(s, &rgb);
942 swf_SetU16(i->tag,shapeid);
943 swf_SetRect(i->tag,&r);
944 swf_SetShapeHeader(i->tag,s);
945 swf_ShapeSetAll(i->tag,s,x1,y1,ls1,fs1,0);
946 swf_ShapeSetLine(i->tag,s,(x2-x1),0);
947 swf_ShapeSetLine(i->tag,s,0,(y2-y1));
948 swf_ShapeSetLine(i->tag,s,(x1-x2),0);
949 swf_ShapeSetLine(i->tag,s,0,(y1-y2));
950 swf_ShapeSetEnd(i->tag);
952 i->tag = swf_InsertTag(i->tag, ST_PLACEOBJECT2);
953 swf_ObjectPlace(i->tag,shapeid,getNewDepth(dev),0,0,0);
954 i->tag = swf_InsertTag(i->tag, ST_PLACEOBJECT2);
955 swf_ObjectPlaceClip(i->tag,shapeid,getNewDepth(dev),0,0,0,65535);
958 /* initialize the swf writer */
959 void gfxdevice_swf_init(gfxdevice_t* dev)
961 memset(dev, 0, sizeof(gfxdevice_t));
965 dev->internal = init_internal_struct();
967 dev->startpage = swf_startframe;
968 dev->endpage = swf_endframe;
969 dev->finish = swf_finish;
970 dev->fillbitmap = swf_fillbitmap;
971 dev->setparameter = swf_setparameter;
972 dev->stroke = swf_stroke;
973 dev->startclip = swf_startclip;
974 dev->endclip = swf_endclip;
975 dev->fill = swf_fill;
976 dev->fillbitmap = swf_fillbitmap;
977 dev->fillgradient = swf_fillgradient;
978 dev->addfont = swf_addfont;
979 dev->drawchar = swf_drawchar;
980 dev->drawlink = swf_drawlink;
982 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
987 msg("<verbose> initializing swf output\n", i->max_x,i->max_y);
991 i->swf = (SWF*)rfx_calloc(sizeof(SWF));
992 i->swf->fileVersion = i->config_flashversion;
993 i->swf->frameRate = i->config_framerate*0x100;
994 i->swf->movieSize.xmin = 0;
995 i->swf->movieSize.ymin = 0;
996 i->swf->movieSize.xmax = 0;
997 i->swf->movieSize.ymax = 0;
999 i->swf->firstTag = swf_InsertTag(NULL,ST_SETBACKGROUNDCOLOR);
1000 i->tag = i->swf->firstTag;
1001 rgb.a = rgb.r = rgb.g = rgb.b = 0xff;
1003 swf_SetRGB(i->tag,&rgb);
1005 i->startdepth = i->depth = 0;
1007 if(i->config_protect)
1008 i->tag = swf_InsertTag(i->tag, ST_PROTECT);
1011 static void startshape(gfxdevice_t*dev)
1013 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1018 //if(i->chardatapos && i->chardata[i->chardatapos-1].color.a)
1021 i->tag = swf_InsertTag(i->tag,ST_DEFINESHAPE3);
1023 swf_ShapeNew(&i->shape);
1024 i->linestyleid = swf_ShapeAddLineStyle(i->shape,i->linewidth,&i->strokergb);
1025 i->fillstyleid = swf_ShapeAddSolidFillStyle(i->shape,&i->fillrgb);
1027 RGBA markcol = {0,i->mark[0],i->mark[1],i->mark[2]};
1028 swf_ShapeAddSolidFillStyle(i->shape,&markcol);
1031 i->shapeid = getNewID(dev);
1033 msg("<debug> Using shape id %d", i->shapeid);
1035 swf_SetU16(i->tag,i->shapeid); // ID
1037 i->bboxrectpos = i->tag->len;
1039 swf_SetRect(i->tag,&i->pagebbox);
1041 memset(&i->bboxrect, 0, sizeof(i->bboxrect));
1043 swf_SetShapeStyles(i->tag,i->shape);
1044 swf_ShapeCountBits(i->shape,NULL,NULL);
1045 swf_SetShapeBits(i->tag,i->shape);
1047 /* TODO: do we really need this? */
1048 //swf_ShapeSetAll(i->tag,i->shape,/*x*/0,/*y*/0,i->linestyleid,0,0);
1049 //swf_ShapeSetAll(i->tag,i->shape,/*x*/UNDEFINED_COORD,/*y*/UNDEFINED_COORD,i->linestyleid,0,0);
1050 i->swflastx=i->swflasty=UNDEFINED_COORD;
1051 i->lastwasfill = -1;
1052 i->shapeisempty = 1;
1055 static void starttext(gfxdevice_t*dev)
1057 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1061 if(i->config_watermark) {
1062 insert_watermark(dev, 0);
1064 i->textid = getNewID(dev);
1065 i->swflastx=i->swflasty=0;
1069 /* TODO: move to ../lib/rfxswf */
1070 void changeRect(gfxdevice_t*dev, TAG*tag, int pos, SRECT*newrect)
1072 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1073 /* determine length of old rect */
1077 swf_GetRect(tag, &old);
1078 swf_ResetReadBits(tag);
1079 int pos_end = tag->pos;
1081 int len = tag->len - pos_end;
1082 U8*data = (U8*)malloc(len);
1083 memcpy(data, &tag->data[pos_end], len);
1086 swf_SetRect(tag, newrect);
1087 swf_SetBlock(tag, data, len);
1089 tag->pos = tag->readBit = 0;
1092 void cancelshape(gfxdevice_t*dev)
1094 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1095 /* delete old shape tag */
1097 i->tag = i->tag->prev;
1098 swf_DeleteTag(todel);
1099 if(i->shape) {swf_ShapeFree(i->shape);i->shape=0;}
1101 i->bboxrectpos = -1;
1103 // i->currentswfid--; // this was an *exceptionally* bad idea
1106 void fixAreas(gfxdevice_t*dev)
1108 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1109 if(!i->shapeisempty && i->fill &&
1110 (i->bboxrect.xmin == i->bboxrect.xmax ||
1111 i->bboxrect.ymin == i->bboxrect.ymax) &&
1112 i->config_minlinewidth >= 0.001
1114 msg("<debug> Shape has size 0: width=%.2f height=%.2f",
1115 (i->bboxrect.xmax-i->bboxrect.xmin)/20.0,
1116 (i->bboxrect.ymax-i->bboxrect.ymin)/20.0
1119 SRECT r = i->bboxrect;
1121 if(r.xmin == r.xmax && r.ymin == r.ymax) {
1122 /* this thing comes down to a single dot- nothing to fix here */
1128 RGBA save_col = i->strokergb;
1129 int save_width = i->linewidth;
1131 i->strokergb = i->fillrgb;
1132 i->linewidth = (int)(i->config_minlinewidth*20);
1133 if(i->linewidth==0) i->linewidth = 1;
1138 moveto(dev, i->tag, r.xmin/20.0,r.ymin/20.0);
1139 lineto(dev, i->tag, r.xmax/20.0,r.ymax/20.0);
1141 i->strokergb = save_col;
1142 i->linewidth = save_width;
1147 static void endshape_noput(gfxdevice_t*dev)
1149 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1152 //changeRect(dev, i->tag, i->bboxrectpos, &i->bboxrect);
1155 swf_ShapeFree(i->shape);
1163 static void endshape(gfxdevice_t*dev)
1165 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1171 if(i->shapeisempty ||
1173 (i->bboxrect.xmin == i->bboxrect.xmax &&
1174 i->bboxrect.ymin == i->bboxrect.ymax))
1176 // delete the shape again, we didn't do anything
1177 msg("<debug> cancelling shape: bbox is (%f,%f,%f,%f)",
1178 i->bboxrect.xmin /20.0,
1179 i->bboxrect.ymin /20.0,
1180 i->bboxrect.xmax /20.0,
1181 i->bboxrect.ymax /20.0
1187 swf_ShapeSetEnd(i->tag);
1189 SRECT r = swf_ClipRect(i->pagebbox, i->bboxrect);
1190 changeRect(dev, i->tag, i->bboxrectpos, &r);
1192 msg("<trace> Placing shape ID %d", i->shapeid);
1194 i->tag = swf_InsertTag(i->tag,ST_PLACEOBJECT2);
1195 MATRIX m = i->page_matrix;
1196 m.tx += i->shapeposx;
1197 m.ty += i->shapeposy;
1198 swf_ObjectPlace(i->tag,i->shapeid,getNewDepth(dev),&m,NULL,NULL);
1200 if(i->config_animate) {
1201 i->tag = swf_InsertTag(i->tag,ST_SHOWFRAME);
1204 swf_ShapeFree(i->shape);
1207 i->bboxrectpos = -1;
1214 void wipeSWF(SWF*swf)
1216 TAG*tag = swf->firstTag;
1218 TAG*next = tag->next;
1219 if(tag->id != ST_SETBACKGROUNDCOLOR &&
1220 tag->id != ST_END &&
1221 tag->id != ST_DOACTION &&
1222 tag->id != ST_SHOWFRAME) {
1229 void swfoutput_finalize(gfxdevice_t*dev)
1231 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1233 if(i->tag && i->tag->id == ST_END)
1234 return; //already done
1236 if(i->config_bboxvars) {
1237 TAG* tag = swf_InsertTag(i->swf->firstTag, ST_DOACTION);
1239 a = action_PushString(a, "xmin");
1240 a = action_PushFloat(a, i->swf->movieSize.xmin / 20.0);
1241 a = action_SetVariable(a);
1242 a = action_PushString(a, "ymin");
1243 a = action_PushFloat(a, i->swf->movieSize.ymin / 20.0);
1244 a = action_SetVariable(a);
1245 a = action_PushString(a, "xmax");
1246 a = action_PushFloat(a, i->swf->movieSize.xmax / 20.0);
1247 a = action_SetVariable(a);
1248 a = action_PushString(a, "ymax");
1249 a = action_PushFloat(a, i->swf->movieSize.ymax / 20.0);
1250 a = action_SetVariable(a);
1251 a = action_PushString(a, "width");
1252 a = action_PushFloat(a, (i->swf->movieSize.xmax - i->swf->movieSize.xmin) / 20.0);
1253 a = action_SetVariable(a);
1254 a = action_PushString(a, "height");
1255 a = action_PushFloat(a, (i->swf->movieSize.ymax - i->swf->movieSize.ymin) / 20.0);
1256 a = action_SetVariable(a);
1258 swf_ActionSet(tag, a);
1263 free(i->mark);i->mark = 0;
1267 fontlist_t *iterator = i->fontlist;
1269 TAG*mtag = i->swf->firstTag;
1270 if(iterator->swffont) {
1271 if(!i->config_storeallcharacters) {
1272 msg("<debug> Reducing font %s", iterator->swffont->name);
1273 swf_FontReduce(iterator->swffont);
1275 int used = iterator->swffont->use && iterator->swffont->use->used_glyphs;
1277 mtag = swf_InsertTag(mtag, ST_DEFINEFONT2);
1278 swf_FontSetDefine2(mtag, iterator->swffont);
1282 iterator = iterator->next;
1284 i->tag = swf_InsertTag(i->tag,ST_END);
1285 TAG* tag = i->tag->prev;
1287 /* remove the removeobject2 tags between the last ST_SHOWFRAME
1288 and the ST_END- they confuse the flash player */
1289 while(tag->id == ST_REMOVEOBJECT2) {
1290 TAG* prev = tag->prev;
1298 if(i->config_enablezlib || i->config_flashversion>=6) {
1299 i->swf->compressed = 1;
1302 /* Initialize AVM2 if it is a Flash9 file */
1303 if(i->config_flashversion>=9 && i->config_insertstoptag) {
1304 AVM2_InsertStops(i->swf);
1306 // if(i->config_reordertags)
1307 // swf_Optimize(i->swf);
1310 int swfresult_save(gfxresult_t*gfx, const char*filename)
1312 SWF*swf = (SWF*)gfx->internal;
1315 fi = open(filename, O_BINARY|O_CREAT|O_TRUNC|O_WRONLY, 0777);
1320 msg("<fatal> Could not create \"%s\". ", FIXNULL(filename));
1324 if FAILED(swf_WriteSWF(fi,swf))
1325 msg("<error> WriteSWF() failed.\n");
1331 void* swfresult_get(gfxresult_t*gfx, const char*name)
1333 SWF*swf = (SWF*)gfx->internal;
1334 if(!strcmp(name, "swf")) {
1335 return (void*)swf_CopySWF(swf);
1336 } else if(!strcmp(name, "xmin")) {
1337 return (void*)(swf->movieSize.xmin/20);
1338 } else if(!strcmp(name, "ymin")) {
1339 return (void*)(swf->movieSize.ymin/20);
1340 } else if(!strcmp(name, "xmax")) {
1341 return (void*)(swf->movieSize.xmax/20);
1342 } else if(!strcmp(name, "ymax")) {
1343 return (void*)(swf->movieSize.ymax/20);
1344 } else if(!strcmp(name, "width")) {
1345 return (void*)((swf->movieSize.xmax - swf->movieSize.xmin)/20);
1346 } else if(!strcmp(name, "height")) {
1347 return (void*)((swf->movieSize.ymax - swf->movieSize.ymin)/20);
1351 void swfresult_destroy(gfxresult_t*gfx)
1354 swf_FreeTags((SWF*)gfx->internal);
1355 free(gfx->internal);
1358 memset(gfx, 0, sizeof(gfxresult_t));
1362 static void swfoutput_destroy(gfxdevice_t* dev);
1364 gfxresult_t* swf_finish(gfxdevice_t* dev)
1366 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1369 if(i->config_linktarget) {
1370 free(i->config_linktarget);
1371 i->config_linktarget = 0;
1374 swfoutput_finalize(dev);
1375 SWF* swf = i->swf;i->swf = 0;
1376 swfoutput_destroy(dev);
1378 result = (gfxresult_t*)rfx_calloc(sizeof(gfxresult_t));
1379 result->internal = swf;
1380 result->save = swfresult_save;
1382 result->get = swfresult_get;
1383 result->destroy = swfresult_destroy;
1387 /* Perform cleaning up */
1388 static void swfoutput_destroy(gfxdevice_t* dev)
1390 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1392 /* not initialized yet- nothing to destroy */
1396 fontlist_t *tmp,*iterator = i->fontlist;
1398 if(iterator->swffont) {
1399 swf_FontFree(iterator->swffont);iterator->swffont=0;
1402 iterator = iterator->next;
1405 if(i->swf) {swf_FreeTags(i->swf);free(i->swf);i->swf = 0;}
1408 memset(dev, 0, sizeof(gfxdevice_t));
1411 static void swfoutput_setstrokecolor(gfxdevice_t* dev, U8 r, U8 g, U8 b, U8 a)
1413 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1414 if(i->strokergb.r == r &&
1415 i->strokergb.g == g &&
1416 i->strokergb.b == b &&
1417 i->strokergb.a == a) return;
1427 //#define ROUND_UP 19
1428 //#define ROUND_UP 10
1430 static void swfoutput_setlinewidth(gfxdevice_t*dev, double _linewidth)
1432 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1433 if(i->linewidth == (U16)(_linewidth*20+19.0/20.0))
1437 i->linewidth = (U16)(_linewidth*20+19.0/20.0);
1441 static void drawlink(gfxdevice_t*dev, ActionTAG*,ActionTAG*, gfxline_t*points, char mouseover);
1442 static void swfoutput_namedlink(gfxdevice_t*dev, char*name, gfxline_t*points);
1443 static void swfoutput_linktopage(gfxdevice_t*dev, int page, gfxline_t*points);
1444 static void swfoutput_linktourl(gfxdevice_t*dev, const char*url, gfxline_t*points);
1446 /*void swfoutput_drawlink(gfxdevice_t*dev, char*url, gfxline_t*points)
1448 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1449 dev->drawlink(dev, points, url);
1452 void swf_drawlink(gfxdevice_t*dev, gfxline_t*points, const char*url)
1454 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1456 if(!strncmp("http://pdf2swf:", url, 15)) {
1457 char*tmp = strdup(url);
1458 int l = strlen(tmp);
1461 swfoutput_namedlink(dev, tmp+15, points);
1464 } else if(!strncmp("page", url, 4)) {
1467 if(url[t]<'0' || url[t]>'9')
1470 int page = atoi(&url[4]);
1471 if(page<0) page = 0;
1472 swfoutput_linktopage(dev, page, points);
1475 swfoutput_linktourl(dev, url, points);
1478 void swfoutput_linktourl(gfxdevice_t*dev, const char*url, gfxline_t*points)
1480 ActionTAG* actions = 0;
1481 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1487 if(i->config_externallinkfunction) {
1488 actions = action_PushString(actions, url); //parameter
1489 actions = action_PushInt(actions, 1); //number of parameters (1)
1490 actions = action_PushString(actions, i->config_externallinkfunction); //function name
1491 actions = action_CallFunction(actions);
1492 } else if(!i->config_linktarget) {
1493 if(!i->config_opennewwindow)
1494 actions = action_GetUrl(actions, url, "_parent");
1496 actions = action_GetUrl(actions, url, "_this");
1498 actions = action_GetUrl(actions, url, i->config_linktarget);
1500 actions = action_End(actions);
1502 drawlink(dev, actions, 0, points, 0);
1504 void swfoutput_linktopage(gfxdevice_t*dev, int page, gfxline_t*points)
1506 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1507 ActionTAG* actions = 0;
1514 if(!i->config_internallinkfunction) {
1515 actions = action_GotoFrame(actions, page-1);
1516 actions = action_End(actions);
1518 actions = action_PushInt(actions, page); //parameter
1519 actions = action_PushInt(actions, 1); //number of parameters (1)
1520 actions = action_PushString(actions, i->config_internallinkfunction); //function name
1521 actions = action_CallFunction(actions);
1522 actions = action_End(actions);
1525 drawlink(dev, actions, 0, points, 0);
1528 /* Named Links (a.k.a. Acrobatmenu) are used to implement various gadgets
1529 of the viewer objects, like subtitles, index elements etc.
1531 void swfoutput_namedlink(gfxdevice_t*dev, char*name, gfxline_t*points)
1533 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1534 ActionTAG *actions1,*actions2;
1535 char*tmp = strdup(name);
1543 if(!strncmp(tmp, "call:", 5))
1545 char*x = strchr(&tmp[5], ':');
1547 actions1 = action_PushInt(0, 0); //number of parameters (0)
1548 actions1 = action_PushString(actions1, &tmp[5]); //function name
1549 actions1 = action_CallFunction(actions1);
1550 actions1 = action_End(actions1);
1553 actions1 = action_PushString(0, x+1); //parameter
1554 actions1 = action_PushInt(actions1, 1); //number of parameters (1)
1555 actions1 = action_PushString(actions1, &tmp[5]); //function name
1556 actions1 = action_CallFunction(actions1);
1557 actions1 = action_End(actions1);
1559 actions2 = action_End(0);
1564 actions1 = action_PushString(0, "/:subtitle");
1565 actions1 = action_PushString(actions1, name);
1566 actions1 = action_SetVariable(actions1);
1567 actions1 = action_End(actions1);
1569 actions2 = action_PushString(0, "/:subtitle");
1570 actions2 = action_PushString(actions2, "");
1571 actions2 = action_SetVariable(actions2);
1572 actions2 = action_End(actions2);
1575 drawlink(dev, actions1, actions2, points, mouseover);
1577 swf_ActionFree(actions1);
1578 swf_ActionFree(actions2);
1582 static void drawgfxline(gfxdevice_t*dev, gfxline_t*line)
1584 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1585 gfxcoord_t lastx=0,lasty=0,px=0,py=0;
1587 int lines= 0, splines=0;
1591 /* check whether the next segment is zero */
1592 if(line->type == gfx_moveTo) {
1593 moveto(dev, i->tag, line->x, line->y);
1594 px = lastx = line->x;
1595 py = lasty = line->y;
1597 } if(line->type == gfx_lineTo) {
1598 lineto(dev, i->tag, line->x, line->y);
1603 } else if(line->type == gfx_splineTo) {
1605 s.x = line->sx;p.x = line->x;
1606 s.y = line->sy;p.y = line->y;
1607 splineto(dev, i->tag, s, p);
1615 msg("<trace> drawgfxline, %d lines, %d splines", lines, splines);
1619 static void drawlink(gfxdevice_t*dev, ActionTAG*actions1, ActionTAG*actions2, gfxline_t*points, char mouseover)
1621 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1630 int buttonid = getNewID(dev);
1631 gfxbbox_t bbox = gfxline_getbbox(points);
1634 myshapeid = getNewID(dev);
1635 i->tag = swf_InsertTag(i->tag,ST_DEFINESHAPE3);
1636 swf_ShapeNew(&i->shape);
1637 rgb.r = rgb.b = rgb.a = rgb.g = 0;
1638 fsid = swf_ShapeAddSolidFillStyle(i->shape,&rgb);
1639 swf_SetU16(i->tag, myshapeid);
1640 r.xmin = (int)(bbox.xmin*20);
1641 r.ymin = (int)(bbox.ymin*20);
1642 r.xmax = (int)(bbox.xmax*20);
1643 r.ymax = (int)(bbox.ymax*20);
1644 r = swf_ClipRect(i->pagebbox, r);
1645 swf_SetRect(i->tag,&r);
1646 swf_SetShapeStyles(i->tag,i->shape);
1647 swf_ShapeCountBits(i->shape,NULL,NULL);
1648 swf_SetShapeBits(i->tag,i->shape);
1649 swf_ShapeSetAll(i->tag,i->shape,/*x*/0,/*y*/0,0,fsid,0);
1650 i->swflastx = i->swflasty = 0;
1651 drawgfxline(dev, points);
1652 swf_ShapeSetEnd(i->tag);
1655 myshapeid2 = getNewID(dev);
1656 i->tag = swf_InsertTag(i->tag,ST_DEFINESHAPE3);
1657 swf_ShapeNew(&i->shape);
1659 rgb = i->config_linkcolor;
1661 fsid = swf_ShapeAddSolidFillStyle(i->shape,&rgb);
1662 swf_SetU16(i->tag, myshapeid2);
1663 r.xmin = (int)(bbox.xmin*20);
1664 r.ymin = (int)(bbox.ymin*20);
1665 r.xmax = (int)(bbox.xmax*20);
1666 r.ymax = (int)(bbox.ymax*20);
1667 r = swf_ClipRect(i->pagebbox, r);
1668 swf_SetRect(i->tag,&r);
1669 swf_SetShapeStyles(i->tag,i->shape);
1670 swf_ShapeCountBits(i->shape,NULL,NULL);
1671 swf_SetShapeBits(i->tag,i->shape);
1672 swf_ShapeSetAll(i->tag,i->shape,/*x*/0,/*y*/0,0,fsid,0);
1673 i->swflastx = i->swflasty = 0;
1674 drawgfxline(dev, points);
1675 swf_ShapeSetEnd(i->tag);
1679 i->tag = swf_InsertTag(i->tag,ST_DEFINEBUTTON);
1680 swf_SetU16(i->tag,buttonid); //id
1681 swf_ButtonSetFlags(i->tag, 0); //menu=no
1682 swf_ButtonSetRecord(i->tag,0x01,myshapeid,i->depth,0,0);
1683 swf_ButtonSetRecord(i->tag,0x02,myshapeid2,i->depth,0,0);
1684 swf_ButtonSetRecord(i->tag,0x04,myshapeid2,i->depth,0,0);
1685 swf_ButtonSetRecord(i->tag,0x08,myshapeid,i->depth,0,0);
1686 swf_SetU8(i->tag,0);
1687 swf_ActionSet(i->tag,actions1);
1688 swf_SetU8(i->tag,0);
1692 i->tag = swf_InsertTag(i->tag,ST_DEFINEBUTTON2);
1693 swf_SetU16(i->tag,buttonid); //id
1694 swf_ButtonSetFlags(i->tag, 0); //menu=no
1695 swf_ButtonSetRecord(i->tag,0x01,myshapeid,i->depth,0,0);
1696 swf_ButtonSetRecord(i->tag,0x02,myshapeid2,i->depth,0,0);
1697 swf_ButtonSetRecord(i->tag,0x04,myshapeid2,i->depth,0,0);
1698 swf_ButtonSetRecord(i->tag,0x08,myshapeid,i->depth,0,0);
1699 swf_SetU8(i->tag,0); // end of button records
1700 swf_ButtonSetCondition(i->tag, BC_IDLE_OVERUP);
1701 swf_ActionSet(i->tag,actions1);
1703 swf_ButtonSetCondition(i->tag, BC_OVERUP_IDLE);
1704 swf_ActionSet(i->tag,actions2);
1705 swf_SetU8(i->tag,0);
1706 swf_ButtonPostProcess(i->tag, 2);
1708 swf_SetU8(i->tag,0);
1709 swf_ButtonPostProcess(i->tag, 1);
1713 sprintf(name, "link%d", buttonid);
1715 msg("<trace> Placing link ID %d", buttonid);
1716 i->tag = swf_InsertTag(i->tag,ST_PLACEOBJECT2);
1718 if(posx!=0 || posy!=0) {
1720 p.x = (int)(posx*20);
1721 p.y = (int)(posy*20);
1722 p = swf_TurnPoint(p, &i->page_matrix);
1727 swf_ObjectPlace(i->tag, buttonid, getNewDepth(dev),&m,0,name);
1729 swf_ObjectPlace(i->tag, buttonid, getNewDepth(dev),&i->page_matrix,0,name);
1736 for(t=0;t<picpos;t++)
1738 if(pic_xids[t] == xid &&
1739 pic_yids[t] == yid) {
1740 width = pic_width[t];
1741 height = pic_height[t];
1745 pic_ids[picpos] = swfoutput_drawimagelosslessN(&output, pic, pal, width, height, x1,y1,x2,y2,x3,y3,x4,y4, numpalette);
1746 pic_xids[picpos] = xid;
1747 pic_yids[picpos] = yid;
1748 pic_width[picpos] = width;
1749 pic_height[picpos] = height;
1752 pic[width*y+x] = buf[0];
1756 xid += pal[1].r*3 + pal[1].g*11 + pal[1].b*17;
1757 yid += pal[1].r*7 + pal[1].g*5 + pal[1].b*23;
1761 xid += x*r+x*b*3+x*g*7+x*a*11;
1762 yid += y*r*3+y*b*17+y*g*19+y*a*11;
1764 for(t=0;t<picpos;t++)
1766 if(pic_xids[t] == xid &&
1767 pic_yids[t] == yid) {
1776 int swf_setparameter(gfxdevice_t*dev, const char*name, const char*value)
1778 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1780 msg("<trace> swfdevice: %s=%s", name, value);
1781 if(!strcmp(name, "jpegsubpixels")) {
1782 i->config_jpegsubpixels = atof(value);
1783 } else if(!strcmp(name, "ppmsubpixels")) {
1784 i->config_ppmsubpixels = atof(value);
1785 } else if(!strcmp(name, "subpixels")) {
1786 i->config_ppmsubpixels = i->config_jpegsubpixels = atof(value);
1787 } else if(!strcmp(name, "drawonlyshapes")) {
1788 i->config_drawonlyshapes = atoi(value);
1789 } else if(!strcmp(name, "ignoredraworder")) {
1790 i->config_ignoredraworder = atoi(value);
1791 } else if(!strcmp(name, "mark")) {
1792 if(!value || !value[0]) {
1793 if(i->mark) free(i->mark);
1797 i->mark = strdup("...");
1798 for(t=0;t<3;t++) if(value[t]) i->mark[t] = value[t];
1800 } else if(!strcmp(name, "filloverlap")) {
1801 i->config_filloverlap = atoi(value);
1802 } else if(!strcmp(name, "linksopennewwindow")) {
1803 i->config_opennewwindow = atoi(value);
1804 } else if(!strcmp(name, "opennewwindow")) {
1805 i->config_opennewwindow = atoi(value);
1806 } else if(!strcmp(name, "storeallcharacters")) {
1807 i->config_storeallcharacters = atoi(value);
1808 } else if(!strcmp(name, "enablezlib")) {
1809 i->config_enablezlib = atoi(value);
1810 } else if(!strcmp(name, "bboxvars")) {
1811 i->config_bboxvars = atoi(value);
1812 } else if(!strcmp(name, "showclipshapes")) {
1813 i->config_showclipshapes = atoi(value);
1814 } else if(!strcmp(name, "reordertags")) {
1815 i->config_reordertags = atoi(value);
1816 } else if(!strcmp(name, "internallinkfunction")) {
1817 i->config_internallinkfunction = strdup(value);
1818 } else if(!strcmp(name, "externallinkfunction")) {
1819 i->config_externallinkfunction = strdup(value);
1820 } else if(!strcmp(name, "linkfunction")) { //sets both internallinkfunction and externallinkfunction
1821 i->config_internallinkfunction = strdup(value);
1822 i->config_externallinkfunction = strdup(value);
1823 } else if(!strcmp(name, "disable_polygon_conversion")) {
1824 i->config_disable_polygon_conversion = atoi(value);
1825 } else if(!strcmp(name, "normalize_polygon_positions")) {
1826 i->config_normalize_polygon_positions = atoi(value);
1827 } else if(!strcmp(name, "wxwindowparams")) {
1828 i->config_watermark = atoi(value);
1829 } else if(!strcmp(name, "insertstop")) {
1830 i->config_insertstoptag = atoi(value);
1831 } else if(!strcmp(name, "protect")) {
1832 i->config_protect = atoi(value);
1833 if(i->config_protect && i->tag) {
1834 i->tag = swf_InsertTag(i->tag, ST_PROTECT);
1836 } else if(!strcmp(name, "flashversion")) {
1837 i->config_flashversion = atoi(value);
1839 i->swf->fileVersion = i->config_flashversion;
1841 } else if(!strcmp(name, "framerate")) {
1842 i->config_framerate = atoi(value);
1844 i->swf->frameRate = i->config_framerate*0x100;
1846 } else if(!strcmp(name, "minlinewidth")) {
1847 i->config_minlinewidth = atof(value);
1848 } else if(!strcmp(name, "caplinewidth")) {
1849 i->config_caplinewidth = atof(value);
1850 } else if(!strcmp(name, "linktarget")) {
1851 i->config_linktarget = strdup(value);
1852 } else if(!strcmp(name, "dumpfonts")) {
1853 i->config_dumpfonts = atoi(value);
1854 } else if(!strcmp(name, "animate")) {
1855 i->config_animate = atoi(value);
1856 } else if(!strcmp(name, "next_bitmap_is_jpeg")) {
1858 } else if(!strcmp(name, "jpegquality")) {
1859 int val = atoi(value);
1861 if(val>101) val=101;
1862 i->config_jpegquality = val;
1863 } else if(!strcmp(name, "splinequality")) {
1864 int v = atoi(value);
1865 v = 500-(v*5); // 100% = 0.25 pixel, 0% = 25 pixel
1867 i->config_splinemaxerror = v;
1868 } else if(!strcmp(name, "fontquality")) {
1869 int v = atoi(value);
1870 v = 500-(v*5); // 100% = 0.25 pixel, 0% = 25 pixel
1872 i->config_fontsplinemaxerror = v;
1873 } else if(!strcmp(name, "linkcolor")) {
1874 if(strlen(value)!=8) {
1875 fprintf(stderr, "Unknown format for option 'linkcolor'. (%s <-> RRGGBBAA)\n", value);
1878 # define NIBBLE(s) (((s)>='0' && (s)<='9')?((s)-'0'):((s)&0x0f)+9)
1879 i->config_linkcolor.r = NIBBLE(value[0])<<4 | NIBBLE(value[1]);
1880 i->config_linkcolor.g = NIBBLE(value[2])<<4 | NIBBLE(value[3]);
1881 i->config_linkcolor.b = NIBBLE(value[4])<<4 | NIBBLE(value[5]);
1882 i->config_linkcolor.a = NIBBLE(value[6])<<4 | NIBBLE(value[7]);
1883 } else if(!strcmp(name, "help")) {
1884 printf("\nSWF layer options:\n");
1885 printf("jpegdpi=<dpi> resolution adjustment for jpeg images\n");
1886 printf("jpegsubpixels=<pixels> resolution adjustment for jpeg images (same as jpegdpi, but in pixels)\n");
1887 printf("ppmdpi=<dpi> resolution adjustment for lossless images\n");
1888 printf("ppmsubpixels=<pixels resolution adjustment for lossless images (same as ppmdpi, but in pixels)\n");
1889 printf("subpixels=<pixels> shortcut for setting both jpegsubpixels and ppmsubpixels\n");
1890 printf("drawonlyshapes convert everything to shapes (currently broken)\n");
1891 printf("ignoredraworder allow to perform a few optimizations for creating smaller SWFs\n");
1892 printf("linksopennewwindow make links open a new browser window\n");
1893 printf("linktarget target window name of new links\n");
1894 printf("linkcolor=<color) color of links (format: RRGGBBAA)\n");
1895 printf("storeallcharacters don't reduce the fonts to used characters in the output file\n");
1896 printf("enablezlib switch on zlib compression (also done if flashversion>=7)\n");
1897 printf("bboxvars store the bounding box of the SWF file in actionscript variables\n");
1898 printf("reordertags=0/1 (default: 1) perform some tag optimizations\n");
1899 printf("internallinkfunction=<name> when the user clicks a internal link (to a different page) in the converted file, this actionscript function is called\n");
1900 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");
1901 printf("disable_polygon_conversion never convert strokes to polygons (will remove capstyles and joint styles)\n");
1902 printf("caplinewidth=<width> the minimum thichness a line needs to have so that capstyles become visible (and are converted)\n");
1903 printf("insertstop put an ActionScript \"STOP\" tag in every frame\n");
1904 printf("protect add a \"protect\" tag to the file, to prevent loading in the Flash editor\n");
1905 printf("flashversion=<version> the SWF fileversion (6)\n");
1906 printf("minlinewidth=<width> convert horizontal/vertical boxes smaller than this width to lines (0.05) \n");
1907 printf("animate insert a showframe tag after each placeobject (animate draw order of PDF files)\n");
1908 printf("jpegquality=<quality> set compression quality of jpeg images\n");
1915 // --------------------------------------------------------------------
1917 static CXFORM gfxcxform_to_cxform(gfxcxform_t* c)
1920 swf_GetCXForm(0, &cx, 1);
1923 if(c->rg!=0 || c->rb!=0 || c->ra!=0 ||
1924 c->gr!=0 || c->gb!=0 || c->ga!=0 ||
1925 c->br!=0 || c->bg!=0 || c->ba!=0 ||
1926 c->ar!=0 || c->ag!=0 || c->ab!=0)
1927 msg("<warning> CXForm not SWF-compatible");
1929 cx.a0 = (S16)(c->aa*256);
1930 cx.r0 = (S16)(c->rr*256);
1931 cx.g0 = (S16)(c->gg*256);
1932 cx.b0 = (S16)(c->bb*256);
1941 static int imageInCache(gfxdevice_t*dev, void*data, int width, int height)
1945 static void addImageToCache(gfxdevice_t*dev, void*data, int width, int height)
1949 static int add_image(swfoutput_internal*i, gfximage_t*img, int targetwidth, int targetheight, int* newwidth, int* newheight)
1951 gfxdevice_t*dev = i->dev;
1953 RGBA*mem = (RGBA*)img->data;
1955 int sizex = img->width;
1956 int sizey = img->height;
1957 int is_jpeg = i->jpeg;
1960 int newsizex=sizex, newsizey=sizey;
1963 if(is_jpeg && i->config_jpegsubpixels) {
1964 newsizex = (int)(targetwidth*i->config_jpegsubpixels + 0.5);
1965 newsizey = (int)(targetheight*i->config_jpegsubpixels + 0.5);
1966 } else if(!is_jpeg && i->config_ppmsubpixels) {
1967 newsizex = (int)(targetwidth*i->config_ppmsubpixels + 0.5);
1968 newsizey = (int)(targetheight*i->config_ppmsubpixels + 0.5);
1972 if(sizex<=0 || sizey<=0)
1979 /* TODO: cache images */
1981 if(newsizex<sizex || newsizey<sizey) {
1982 msg("<verbose> Scaling %dx%d image to %dx%d", sizex, sizey, newsizex, newsizey);
1983 newpic = swf_ImageScale(mem, sizex, sizey, newsizex, newsizey);
1984 *newwidth = sizex = newsizex;
1985 *newheight = sizey = newsizey;
1988 *newwidth = newsizex = sizex;
1989 *newheight = newsizey = sizey;
1992 int num_colors = swf_ImageGetNumberOfPaletteEntries(mem,sizex,sizey,0);
1993 int has_alpha = swf_ImageHasAlpha(mem,sizex,sizey);
1995 msg("<verbose> Drawing %dx%d %s%simage (id %d) at size %dx%d (%dx%d), %s%d colors",
1997 has_alpha?(has_alpha==2?"semi-transparent ":"transparent "):"",
1998 is_jpeg?"jpeg-":"", i->currentswfid+1,
2000 targetwidth, targetheight,
2001 /*newsizex, newsizey,*/
2002 num_colors>256?">":"", num_colors>256?256:num_colors);
2004 /*RGBA* pal = (RGBA*)rfx_alloc(sizeof(RGBA)*num_colors);
2005 swf_ImageGetNumberOfPaletteEntries(mem,sizex,sizey,pal);
2007 for(t=0;t<num_colors;t++) {
2008 printf("%02x%02x%02x%02x ",
2009 pal[t].r, pal[t].g, pal[t].b, pal[t].a);
2016 int cacheid = imageInCache(dev, mem, sizex, sizey);
2019 bitid = getNewID(dev);
2020 i->tag = swf_AddImage(i->tag, bitid, mem, sizex, sizey, i->config_jpegquality);
2021 addImageToCache(dev, mem, sizex, sizey);
2031 static SRECT gfxline_getSWFbbox(gfxline_t*line)
2033 gfxbbox_t bbox = gfxline_getbbox(line);
2035 r.xmin = (int)(bbox.xmin*20);
2036 r.ymin = (int)(bbox.ymin*20);
2037 r.xmax = (int)(bbox.xmax*20);
2038 r.ymax = (int)(bbox.ymax*20);
2042 int line_is_empty(gfxline_t*line)
2045 if(line->type != gfx_moveTo)
2052 static void swf_fillbitmap(gfxdevice_t*dev, gfxline_t*line, gfximage_t*img, gfxmatrix_t*matrix, gfxcxform_t*cxform)
2054 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
2056 if(line_is_empty(line))
2062 int targetx = (int)(sqrt(matrix->m00*matrix->m00 + matrix->m01*matrix->m01)*img->width);
2063 int targety = (int)(sqrt(matrix->m10*matrix->m10 + matrix->m11*matrix->m11)*img->height);
2065 int newwidth=0,newheight=0;
2066 int bitid = add_image(i, img, targetx, targety, &newwidth, &newheight);
2069 double fx = (double)img->width / (double)newwidth;
2070 double fy = (double)img->height / (double)newheight;
2073 m.sx = (int)(65536*20*matrix->m00*fx); m.r1 = (int)(65536*20*matrix->m10*fy);
2074 m.r0 = (int)(65536*20*matrix->m01*fx); m.sy = (int)(65536*20*matrix->m11*fy);
2075 m.tx = (int)(matrix->tx*20);
2076 m.ty = (int)(matrix->ty*20);
2079 int myshapeid = getNewID(dev);
2080 i->tag = swf_InsertTag(i->tag,ST_DEFINESHAPE);
2082 swf_ShapeNew(&shape);
2083 int fsid = swf_ShapeAddBitmapFillStyle(shape,&m,bitid,1);
2084 swf_SetU16(i->tag, myshapeid);
2085 SRECT r = gfxline_getSWFbbox(line);
2086 r = swf_ClipRect(i->pagebbox, r);
2087 swf_SetRect(i->tag,&r);
2088 swf_SetShapeStyles(i->tag,shape);
2089 swf_ShapeCountBits(shape,NULL,NULL);
2090 swf_SetShapeBits(i->tag,shape);
2091 swf_ShapeSetAll(i->tag,shape,UNDEFINED_COORD,UNDEFINED_COORD,0,fsid,0);
2092 i->swflastx = i->swflasty = UNDEFINED_COORD;
2093 drawgfxline(dev, line);
2094 swf_ShapeSetEnd(i->tag);
2095 swf_ShapeFree(shape);
2097 msg("<trace> Placing image, shape ID %d, bitmap ID %d", myshapeid, bitid);
2098 i->tag = swf_InsertTag(i->tag,ST_PLACEOBJECT2);
2099 CXFORM cxform2 = gfxcxform_to_cxform(cxform);
2100 swf_ObjectPlace(i->tag,myshapeid,getNewDepth(dev),&i->page_matrix,&cxform2,NULL);
2103 static RGBA col_black = {255,0,0,0};
2105 static void drawoutline(gfxdevice_t*dev, gfxline_t*line)
2107 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
2109 int myshapeid = getNewID(dev);
2110 i->tag = swf_InsertTag(i->tag,ST_DEFINESHAPE3);
2113 swf_ShapeNew(&shape);
2114 int lsid = swf_ShapeAddLineStyle(shape,1,&col_black);
2116 swf_SetU16(i->tag,myshapeid);
2117 SRECT r = gfxline_getSWFbbox(line);
2118 r = swf_ClipRect(i->pagebbox, r);
2119 swf_SetRect(i->tag,&r);
2120 swf_SetShapeStyles(i->tag,shape);
2121 swf_ShapeCountBits(shape,NULL,NULL);
2122 swf_SetShapeBits(i->tag,shape);
2123 swf_ShapeSetAll(i->tag,shape,UNDEFINED_COORD,UNDEFINED_COORD,lsid,0,0);
2124 drawgfxline(dev, line);
2125 swf_ShapeSetEnd(i->tag);
2126 swf_ShapeFree(shape);
2128 i->tag = swf_InsertTag(i->tag,ST_PLACEOBJECT2);
2129 swf_ObjectPlace(i->tag, myshapeid, getNewDepth(dev), 0,0,0);
2132 static void swf_startclip(gfxdevice_t*dev, gfxline_t*line)
2134 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
2139 if(i->clippos >= 127)
2141 msg("<warning> Too many clip levels.");
2145 if(i->config_showclipshapes)
2146 drawoutline(dev, line);
2148 int myshapeid = getNewID(dev);
2149 i->tag = swf_InsertTag(i->tag,ST_DEFINESHAPE3);
2151 memset(&col, 0, sizeof(RGBA));
2154 swf_ShapeNew(&shape);
2155 int fsid = swf_ShapeAddSolidFillStyle(shape,&col);
2157 RGBA markcol = {0,i->mark[0],i->mark[1],i->mark[2]};
2158 swf_ShapeAddSolidFillStyle(shape,&markcol);
2160 swf_SetU16(i->tag,myshapeid);
2161 SRECT r = gfxline_getSWFbbox(line);
2162 r = swf_ClipRect(i->pagebbox, r);
2163 swf_SetRect(i->tag,&r);
2164 swf_SetShapeStyles(i->tag,shape);
2165 swf_ShapeCountBits(shape,NULL,NULL);
2166 swf_SetShapeBits(i->tag,shape);
2167 swf_ShapeSetAll(i->tag,shape,UNDEFINED_COORD,UNDEFINED_COORD,0,fsid,0);
2168 i->swflastx = i->swflasty = UNDEFINED_COORD;
2169 i->shapeisempty = 1;
2170 drawgfxline(dev, line);
2171 if(i->shapeisempty) {
2172 /* an empty clip shape is equivalent to a shape with no area */
2173 int x = line?line->x:0;
2174 int y = line?line->y:0;
2175 moveto(dev, i->tag, x,y);
2176 lineto(dev, i->tag, x,y);
2177 lineto(dev, i->tag, x,y);
2179 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)) {
2180 if(i->config_watermark) {
2181 gfxbbox_t r; r.xmin = r.ymin = 0;r.xmax = i->max_x;r.ymax = i->max_y;
2182 draw_watermark(dev, r, 1);
2185 swf_ShapeSetEnd(i->tag);
2186 swf_ShapeFree(shape);
2188 /* TODO: remember the bbox, and check all shapes against it */
2190 msg("<trace> Placing clip ID %d", myshapeid);
2191 i->tag = swf_InsertTag(i->tag,ST_PLACEOBJECT2);
2192 i->cliptags[i->clippos] = i->tag;
2193 i->clipshapes[i->clippos] = myshapeid;
2194 i->clipdepths[i->clippos] = getNewDepth(dev);
2198 static void swf_endclip(gfxdevice_t*dev)
2200 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
2207 msg("<error> Invalid end of clipping region");
2211 /*swf_ObjectPlaceClip(i->cliptags[i->clippos],i->clipshapes[i->clippos],i->clipdepths[i->clippos],&i->page_matrix,NULL,NULL,
2212 / * clip to depth: * / i->depth <= i->clipdepths[i->clippos]? i->depth : i->depth - 1);
2214 swf_ObjectPlaceClip(i->cliptags[i->clippos],i->clipshapes[i->clippos],i->clipdepths[i->clippos],&i->page_matrix,NULL,NULL,i->depth);
2216 static int gfxline_type(gfxline_t*line)
2222 int haszerosegments=0;
2225 if(line->type == gfx_moveTo) {
2228 } else if(line->type == gfx_lineTo) {
2232 } else if(line->type == gfx_splineTo) {
2234 if(tmpsplines>lines)
2242 if(lines==0 && splines==0) return 0;
2243 else if(lines==1 && splines==0) return 1;
2244 else if(lines==0 && splines==1) return 2;
2245 else if(splines==0) return 3;
2249 static int gfxline_has_dots(gfxline_t*line)
2257 if(line->type == gfx_moveTo) {
2258 /* test the length of the preceding line, and assume it is a dot if
2259 it's length is less than 1.0. But *only* if there's a noticable
2260 gap between the previous line and the next moveTo. (I've come
2261 across a PDF where thousands of "dots" were stringed together,
2263 int last_short_gap = short_gap;
2264 if((fabs(line->x - x) + fabs(line->y - y)) < 1.0) {
2269 if(isline && dist < 1 && !short_gap && !last_short_gap) {
2274 } else if(line->type == gfx_lineTo) {
2275 dist += fabs(line->x - x) + fabs(line->y - y);
2277 } else if(line->type == gfx_splineTo) {
2278 dist += fabs(line->sx - x) + fabs(line->sy - y) +
2279 fabs(line->x - line->sx) + fabs(line->y - line->sy);
2286 if(isline && dist < 1 && !short_gap) {
2292 static int gfxline_fix_short_edges(gfxline_t*line)
2296 if(line->type == gfx_lineTo) {
2297 if(fabs(line->x - x) + fabs(line->y - y) < 0.01) {
2300 } else if(line->type == gfx_splineTo) {
2301 if(fabs(line->sx - x) + fabs(line->sy - y) +
2302 fabs(line->x - line->sx) + fabs(line->y - line->sy) < 0.01) {
2313 static char is_inside_page(gfxdevice_t*dev, gfxcoord_t x, gfxcoord_t y)
2315 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
2316 if(x<i->min_x || x>i->max_x) return 0;
2317 if(y<i->min_y || y>i->max_y) return 0;
2321 gfxline_t* gfxline_move(gfxline_t*line, double x, double y)
2323 gfxline_t*l = line = gfxline_clone(line);
2335 //#define NORMALIZE_POLYGON_POSITIONS
2337 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)
2339 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
2340 if(line_is_empty(line))
2342 int type = gfxline_type(line);
2343 int has_dots = gfxline_has_dots(line);
2344 gfxbbox_t r = gfxline_getbbox(line);
2345 int is_outside_page = !is_inside_page(dev, r.xmin, r.ymin) || !is_inside_page(dev, r.xmax, r.ymax);
2347 /* TODO: * split line into segments, and perform this check for all segments */
2349 if(i->config_disable_polygon_conversion || type>=5 ||
2351 (width <= i->config_caplinewidth
2352 || (cap_style == gfx_capRound && joint_style == gfx_joinRound)
2353 || (cap_style == gfx_capRound && type<=2)))) {} else
2355 /* convert line to polygon */
2356 msg("<trace> draw as polygon, type=%d dots=%d", type, has_dots);
2358 gfxline_fix_short_edges(line);
2359 /* we need to convert the line into a polygon */
2360 gfxpoly_t* poly = gfxpoly_strokeToPoly(line, width, cap_style, joint_style, miterLimit);
2361 gfxline_t*gfxline = gfxpoly_to_gfxline(poly);
2362 dev->fill(dev, gfxline, color);
2363 gfxline_free(gfxline);
2368 msg("<trace> draw as stroke, type=%d dots=%d", type, has_dots);
2371 if(i->config_normalize_polygon_positions) {
2373 double startx = 0, starty = 0;
2374 if(line && line->type == gfx_moveTo) {
2378 line = gfxline_move(line, -startx, -starty);
2379 i->shapeposx = (int)(startx*20);
2380 i->shapeposy = (int)(starty*20);
2383 swfoutput_setstrokecolor(dev, color->r, color->g, color->b, color->a);
2384 swfoutput_setlinewidth(dev, width);
2387 drawgfxline(dev, line);
2389 if(i->config_normalize_polygon_positions) {
2390 free(line); //account for _move
2395 static void swf_fill(gfxdevice_t*dev, gfxline_t*line, gfxcolor_t*color)
2397 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
2398 if(line_is_empty(line))
2402 gfxbbox_t r = gfxline_getbbox(line);
2403 int is_outside_page = !is_inside_page(dev, r.xmin, r.ymin) || !is_inside_page(dev, r.xmax, r.ymax);
2405 //if(i->chardatapos && i->chardata[i->chardatapos-1].color.a) {
2408 if(!i->config_ignoredraworder)
2411 if(i->config_normalize_polygon_positions) {
2413 double startx = 0, starty = 0;
2414 if(line && line->type == gfx_moveTo) {
2418 line = gfxline_move(line, -startx, -starty);
2419 i->shapeposx = (int)(startx*20);
2420 i->shapeposy = (int)(starty*20);
2423 swfoutput_setfillcolor(dev, color->r, color->g, color->b, color->a);
2427 drawgfxline(dev, line);
2429 if(i->currentswfid==2 && r.xmin==0 && r.ymin==0 && r.xmax==i->max_x && r.ymax==i->max_y) {
2430 if(i->config_watermark) {
2431 draw_watermark(dev, r, 1);
2435 msg("<trace> end of swf_fill (shapeid=%d)", i->shapeid);
2437 if(i->config_normalize_polygon_positions) {
2438 free(line); //account for _move
2442 static GRADIENT* gfxgradient_to_GRADIENT(gfxgradient_t*gradient)
2445 gfxgradient_t*g = gradient;
2450 GRADIENT* swfgradient = malloc(sizeof(GRADIENT));
2451 swfgradient->num = num;
2452 swfgradient->rgba = malloc(sizeof(swfgradient->rgba[0])*num);
2453 swfgradient->ratios = malloc(sizeof(swfgradient->ratios[0])*num);
2458 swfgradient->ratios[num] = g->pos*255;
2459 swfgradient->rgba[num] = *(RGBA*)&g->color;
2466 static void swf_fillgradient(gfxdevice_t*dev, gfxline_t*line, gfxgradient_t*gradient, gfxgradienttype_t type, gfxmatrix_t*matrix)
2468 if(line_is_empty(line))
2470 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
2472 if(line_is_empty(line))
2475 GRADIENT* swfgradient = gfxgradient_to_GRADIENT(gradient);
2482 double f = type==gfxgradient_radial?4:4;
2484 m.sx = (int)(matrix->m00*20*f); m.r1 = (int)(matrix->m10*20*f);
2485 m.r0 = (int)(matrix->m01*20*f); m.sy = (int)(matrix->m11*20*f);
2486 m.tx = (int)(matrix->tx*20);
2487 m.ty = (int)(matrix->ty*20);
2490 int myshapeid = getNewID(dev);
2491 i->tag = swf_InsertTag(i->tag, ST_DEFINESHAPE2);
2493 swf_ShapeNew(&shape);
2494 int fsid = swf_ShapeAddGradientFillStyle(shape,&m,swfgradient,type==gfxgradient_radial);
2495 swf_SetU16(i->tag, myshapeid);
2496 SRECT r = gfxline_getSWFbbox(line);
2497 r = swf_ClipRect(i->pagebbox, r);
2498 swf_SetRect(i->tag,&r);
2499 swf_SetShapeStyles(i->tag,shape);
2500 swf_ShapeCountBits(shape,NULL,NULL);
2501 swf_SetShapeBits(i->tag,shape);
2502 swf_ShapeSetAll(i->tag,shape,UNDEFINED_COORD,UNDEFINED_COORD,0,fsid,0);
2503 i->swflastx = i->swflasty = UNDEFINED_COORD;
2504 drawgfxline(dev, line);
2505 swf_ShapeSetEnd(i->tag);
2506 swf_ShapeFree(shape);
2508 int depth = getNewDepth(dev);
2509 msg("<trace> Placing gradient, shape ID %d, depth %d", myshapeid, depth);
2510 i->tag = swf_InsertTag(i->tag,ST_PLACEOBJECT2);
2511 swf_ObjectPlace(i->tag,myshapeid,depth,&i->page_matrix,NULL,NULL);
2513 swf_FreeGradient(swfgradient);free(swfgradient);
2516 static SWFFONT* gfxfont_to_swffont(gfxfont_t*font, const char* id)
2518 SWFFONT*swffont = (SWFFONT*)rfx_calloc(sizeof(SWFFONT));
2520 SRECT bounds = {0,0,0,0};
2522 swffont->version = 2;
2523 swffont->name = (U8*)strdup(id);
2524 swffont->layout = (SWFLAYOUT*)rfx_calloc(sizeof(SWFLAYOUT));
2525 swffont->layout->ascent = 0; /* ? */
2526 swffont->layout->descent = 0;
2527 swffont->layout->leading = 0;
2528 swffont->layout->bounds = (SRECT*)rfx_calloc(sizeof(SRECT)*font->num_glyphs);
2529 swffont->encoding = FONT_ENCODING_UNICODE;
2530 swffont->numchars = font->num_glyphs;
2531 swffont->maxascii = font->max_unicode;
2532 swffont->ascii2glyph = (int*)rfx_calloc(sizeof(int)*swffont->maxascii);
2533 swffont->glyph2ascii = (U16*)rfx_calloc(sizeof(U16)*swffont->numchars);
2534 swffont->glyph = (SWFGLYPH*)rfx_calloc(sizeof(SWFGLYPH)*swffont->numchars);
2535 swffont->glyphnames = (char**)rfx_calloc(sizeof(char*)*swffont->numchars);
2536 for(t=0;t<font->max_unicode;t++) {
2537 swffont->ascii2glyph[t] = font->unicode2glyph[t];
2539 for(t=0;t<font->num_glyphs;t++) {
2543 swffont->glyph2ascii[t] = font->glyphs[t].unicode;
2544 if(swffont->glyph2ascii[t] == 0xffff || swffont->glyph2ascii[t] == 0x0000) {
2545 /* flash 8 flashtype requires unique unicode IDs for each character.
2546 We use the Unicode private user area to assign characters, hoping that
2547 the font doesn't contain more than 2048 glyphs */
2548 swffont->glyph2ascii[t] = 0xe000 + (t&0x1fff);
2551 if(font->glyphs[t].name) {
2552 swffont->glyphnames[t] = strdup(font->glyphs[t].name);
2554 swffont->glyphnames[t] = 0;
2556 advance = (int)(font->glyphs[t].advance);
2558 swf_Shape01DrawerInit(&draw, 0);
2559 line = font->glyphs[t].line;
2562 c.x = line->sx * GLYPH_SCALE; c.y = line->sy * GLYPH_SCALE;
2563 to.x = line->x * GLYPH_SCALE; to.y = line->y * GLYPH_SCALE;
2564 if(line->type == gfx_moveTo) {
2565 draw.moveTo(&draw, &to);
2566 } else if(line->type == gfx_lineTo) {
2567 draw.lineTo(&draw, &to);
2568 } else if(line->type == gfx_splineTo) {
2569 draw.splineTo(&draw, &c, &to);
2574 swffont->glyph[t].shape = swf_ShapeDrawerToShape(&draw);
2575 swffont->layout->bounds[t] = swf_ShapeDrawerGetBBox(&draw);
2577 int xmax = swffont->layout->bounds[t].xmax / 20;
2578 if(xmax>0 && xmax*2 < advance) {
2579 printf("fix bad advance value: bbox=%d, advance=%d (%f)\n", xmax, advance, font->glyphs[t].advance);
2583 if(advance<32768/20) {
2584 swffont->glyph[t].advance = advance*20;
2586 swffont->glyph[t].advance = 32767;
2589 draw.dealloc(&draw);
2591 swf_ExpandRect2(&bounds, &swffont->layout->bounds[t]);
2593 if(bounds.ymin < 0 && bounds.ymax > 0) {
2594 swffont->layout->ascent = -bounds.ymin;
2595 swffont->layout->descent = bounds.ymax;
2596 swffont->layout->leading = bounds.ymax - bounds.ymin;
2598 swffont->layout->ascent = (bounds.ymax - bounds.ymin)/2;
2599 swffont->layout->descent = (bounds.ymax - bounds.ymin)/2;
2600 swffont->layout->leading = bounds.ymax - bounds.ymin;
2602 swffont->layout->descent= (bounds.ymax - bounds.ymin);
2603 swffont->layout->ascent = 0;
2604 swffont->layout->leading = bounds.ymax - bounds.ymin;
2609 static void swf_addfont(gfxdevice_t*dev, gfxfont_t*font)
2611 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
2613 if(i->swffont && i->swffont->name && !strcmp((char*)i->swffont->name,font->id))
2614 return; // the requested font is the current font
2616 fontlist_t*last=0,*l = i->fontlist;
2619 if(!strcmp((char*)l->swffont->name, font->id)) {
2620 return; // we already know this font
2624 l = (fontlist_t*)rfx_calloc(sizeof(fontlist_t));
2625 l->swffont = gfxfont_to_swffont(font, font->id);
2632 swf_FontSetID(l->swffont, getNewID(i->dev));
2634 if(getScreenLogLevel() >= LOGLEVEL_DEBUG) {
2636 // print font information
2637 msg("<debug> Font %s",font->id);
2638 msg("<debug> | ID: %d", l->swffont->id);
2639 msg("<debug> | Version: %d", l->swffont->version);
2640 msg("<debug> | Name: %s", l->swffont->name);
2641 msg("<debug> | Numchars: %d", l->swffont->numchars);
2642 msg("<debug> | Maxascii: %d", l->swffont->maxascii);
2643 msg("<debug> | Style: %d", l->swffont->style);
2644 msg("<debug> | Encoding: %d", l->swffont->encoding);
2645 for(iii=0; iii<l->swffont->numchars;iii++) {
2646 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,
2647 l->swffont->layout->bounds[iii].xmin/20.0,
2648 l->swffont->layout->bounds[iii].ymin/20.0,
2649 l->swffont->layout->bounds[iii].xmax/20.0,
2650 l->swffont->layout->bounds[iii].ymax/20.0
2653 for(t=0;t<l->swffont->maxascii;t++) {
2654 if(l->swffont->ascii2glyph[t] == iii)
2655 msg("<debug> | - maps to %d",t);
2661 static void swf_switchfont(gfxdevice_t*dev, const char*fontid)
2663 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
2665 if(i->swffont && i->swffont->name && !strcmp((char*)i->swffont->name,fontid))
2666 return; // the requested font is the current font
2668 fontlist_t*l = i->fontlist;
2670 if(!strcmp((char*)l->swffont->name, fontid)) {
2671 i->swffont = l->swffont;
2676 msg("<error> Unknown font id: %s", fontid);
2680 static void swf_drawchar(gfxdevice_t*dev, gfxfont_t*font, int glyph, gfxcolor_t*color, gfxmatrix_t*matrix)
2682 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
2684 msg("<error> swf_drawchar called (glyph %d) without font", glyph);
2687 if(!i->swffont || !i->swffont->name || strcmp((char*)i->swffont->name,font->id)) // not equal to current font
2689 /* TODO: remove the need for this (enhance getcharacterbbox so that it can cope
2690 with multiple fonts */
2692 swf_switchfont(dev, font->id); // set the current font
2695 msg("<warning> swf_drawchar: Font is NULL");
2698 if(glyph<0 || glyph>=i->swffont->numchars) {
2699 msg("<warning> No character %d in font %s (%d chars)", glyph, FIXNULL((char*)i->swffont->name), i->swffont->numchars);
2703 setfontscale(dev, matrix->m00, matrix->m01, matrix->m10, matrix->m11, matrix->tx, matrix->ty, 0);
2705 double det = i->fontmatrix.sx/65536.0 * i->fontmatrix.sy/65536.0 -
2706 i->fontmatrix.r0/65536.0 * i->fontmatrix.r1/65536.0;
2707 if(fabs(det) < 0.0005) {
2708 /* x direction equals y direction- the text is invisible */
2709 msg("<verbose> Not drawing invisible character character %d (det=%f, m=[%f %f;%f %f]\n", glyph,
2711 i->fontmatrix.sx/65536.0, i->fontmatrix.r1/65536.0,
2712 i->fontmatrix.r0/65536.0, i->fontmatrix.sy/65536.0);
2716 /*if(i->swffont->glyph[glyph].shape->bitlen <= 16) {
2717 msg("<warning> Glyph %d in current charset (%s, %d characters) is empty",
2718 glyph, FIXNULL((char*)i->swffont->name), i->swffont->numchars);
2722 /* calculate character position with respect to the current font matrix */
2723 double s = 20 * GLYPH_SCALE / det;
2724 double px = matrix->tx - i->fontmatrix.tx/20.0;
2725 double py = matrix->ty - i->fontmatrix.ty/20.0;
2726 int x = (SCOORD)(( px * i->fontmatrix.sy/65536.0 - py * i->fontmatrix.r1/65536.0)*s);
2727 int y = (SCOORD)((- px * i->fontmatrix.r0/65536.0 + py * i->fontmatrix.sx/65536.0)*s);
2728 if(x>32767 || x<-32768 || y>32767 || y<-32768) {
2729 msg("<verbose> Moving character origin to %f %f\n", matrix->tx, matrix->ty);
2731 setfontscale(dev, matrix->m00, matrix->m01, matrix->m10, matrix->m11, matrix->tx, matrix->ty, 1);
2739 msg("<trace> Drawing char %d in font %d at %d,%d in color %02x%02x%02x%02x",
2740 glyph, i->swffont->id, x, y, color->r, color->g, color->b, color->a);
2742 putcharacter(dev, i->swffont->id, glyph, x, y, i->current_font_size, *(RGBA*)color);
2743 swf_FontUseGlyph(i->swffont, glyph);