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 msg("<trace> Placing link ID %d", buttonid);
1714 i->tag = swf_InsertTag(i->tag,ST_PLACEOBJECT2);
1716 if(posx!=0 || posy!=0) {
1718 p.x = (int)(posx*20);
1719 p.y = (int)(posy*20);
1720 p = swf_TurnPoint(p, &i->page_matrix);
1725 swf_ObjectPlace(i->tag, buttonid, getNewDepth(dev),&m,0,0);
1727 swf_ObjectPlace(i->tag, buttonid, getNewDepth(dev),&i->page_matrix,0,0);
1734 for(t=0;t<picpos;t++)
1736 if(pic_xids[t] == xid &&
1737 pic_yids[t] == yid) {
1738 width = pic_width[t];
1739 height = pic_height[t];
1743 pic_ids[picpos] = swfoutput_drawimagelosslessN(&output, pic, pal, width, height, x1,y1,x2,y2,x3,y3,x4,y4, numpalette);
1744 pic_xids[picpos] = xid;
1745 pic_yids[picpos] = yid;
1746 pic_width[picpos] = width;
1747 pic_height[picpos] = height;
1750 pic[width*y+x] = buf[0];
1754 xid += pal[1].r*3 + pal[1].g*11 + pal[1].b*17;
1755 yid += pal[1].r*7 + pal[1].g*5 + pal[1].b*23;
1759 xid += x*r+x*b*3+x*g*7+x*a*11;
1760 yid += y*r*3+y*b*17+y*g*19+y*a*11;
1762 for(t=0;t<picpos;t++)
1764 if(pic_xids[t] == xid &&
1765 pic_yids[t] == yid) {
1774 int swf_setparameter(gfxdevice_t*dev, const char*name, const char*value)
1776 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1778 msg("<trace> swfdevice: %s=%s", name, value);
1779 if(!strcmp(name, "jpegsubpixels")) {
1780 i->config_jpegsubpixels = atof(value);
1781 } else if(!strcmp(name, "ppmsubpixels")) {
1782 i->config_ppmsubpixels = atof(value);
1783 } else if(!strcmp(name, "subpixels")) {
1784 i->config_ppmsubpixels = i->config_jpegsubpixels = atof(value);
1785 } else if(!strcmp(name, "drawonlyshapes")) {
1786 i->config_drawonlyshapes = atoi(value);
1787 } else if(!strcmp(name, "ignoredraworder")) {
1788 i->config_ignoredraworder = atoi(value);
1789 } else if(!strcmp(name, "mark")) {
1790 if(!value || !value[0]) {
1791 if(i->mark) free(i->mark);
1795 i->mark = strdup("...");
1796 for(t=0;t<3;t++) if(value[t]) i->mark[t] = value[t];
1798 } else if(!strcmp(name, "filloverlap")) {
1799 i->config_filloverlap = atoi(value);
1800 } else if(!strcmp(name, "linksopennewwindow")) {
1801 i->config_opennewwindow = atoi(value);
1802 } else if(!strcmp(name, "opennewwindow")) {
1803 i->config_opennewwindow = atoi(value);
1804 } else if(!strcmp(name, "storeallcharacters")) {
1805 i->config_storeallcharacters = atoi(value);
1806 } else if(!strcmp(name, "enablezlib")) {
1807 i->config_enablezlib = atoi(value);
1808 } else if(!strcmp(name, "bboxvars")) {
1809 i->config_bboxvars = atoi(value);
1810 } else if(!strcmp(name, "showclipshapes")) {
1811 i->config_showclipshapes = atoi(value);
1812 } else if(!strcmp(name, "reordertags")) {
1813 i->config_reordertags = atoi(value);
1814 } else if(!strcmp(name, "internallinkfunction")) {
1815 i->config_internallinkfunction = strdup(value);
1816 } else if(!strcmp(name, "externallinkfunction")) {
1817 i->config_externallinkfunction = strdup(value);
1818 } else if(!strcmp(name, "linkfunction")) { //sets both internallinkfunction and externallinkfunction
1819 i->config_internallinkfunction = strdup(value);
1820 i->config_externallinkfunction = strdup(value);
1821 } else if(!strcmp(name, "disable_polygon_conversion")) {
1822 i->config_disable_polygon_conversion = atoi(value);
1823 } else if(!strcmp(name, "normalize_polygon_positions")) {
1824 i->config_normalize_polygon_positions = atoi(value);
1825 } else if(!strcmp(name, "wxwindowparams")) {
1826 i->config_watermark = atoi(value);
1827 } else if(!strcmp(name, "insertstop")) {
1828 i->config_insertstoptag = atoi(value);
1829 } else if(!strcmp(name, "protect")) {
1830 i->config_protect = atoi(value);
1831 if(i->config_protect && i->tag) {
1832 i->tag = swf_InsertTag(i->tag, ST_PROTECT);
1834 } else if(!strcmp(name, "flashversion")) {
1835 i->config_flashversion = atoi(value);
1837 i->swf->fileVersion = i->config_flashversion;
1839 } else if(!strcmp(name, "framerate")) {
1840 i->config_framerate = atoi(value);
1842 i->swf->frameRate = i->config_framerate*0x100;
1844 } else if(!strcmp(name, "minlinewidth")) {
1845 i->config_minlinewidth = atof(value);
1846 } else if(!strcmp(name, "caplinewidth")) {
1847 i->config_caplinewidth = atof(value);
1848 } else if(!strcmp(name, "linktarget")) {
1849 i->config_linktarget = strdup(value);
1850 } else if(!strcmp(name, "dumpfonts")) {
1851 i->config_dumpfonts = atoi(value);
1852 } else if(!strcmp(name, "animate")) {
1853 i->config_animate = atoi(value);
1854 } else if(!strcmp(name, "next_bitmap_is_jpeg")) {
1856 } else if(!strcmp(name, "jpegquality")) {
1857 int val = atoi(value);
1859 if(val>101) val=101;
1860 i->config_jpegquality = val;
1861 } else if(!strcmp(name, "splinequality")) {
1862 int v = atoi(value);
1863 v = 500-(v*5); // 100% = 0.25 pixel, 0% = 25 pixel
1865 i->config_splinemaxerror = v;
1866 } else if(!strcmp(name, "fontquality")) {
1867 int v = atoi(value);
1868 v = 500-(v*5); // 100% = 0.25 pixel, 0% = 25 pixel
1870 i->config_fontsplinemaxerror = v;
1871 } else if(!strcmp(name, "linkcolor")) {
1872 if(strlen(value)!=8) {
1873 fprintf(stderr, "Unknown format for option 'linkcolor'. (%s <-> RRGGBBAA)\n", value);
1876 # define NIBBLE(s) (((s)>='0' && (s)<='9')?((s)-'0'):((s)&0x0f)+9)
1877 i->config_linkcolor.r = NIBBLE(value[0])<<4 | NIBBLE(value[1]);
1878 i->config_linkcolor.g = NIBBLE(value[2])<<4 | NIBBLE(value[3]);
1879 i->config_linkcolor.b = NIBBLE(value[4])<<4 | NIBBLE(value[5]);
1880 i->config_linkcolor.a = NIBBLE(value[6])<<4 | NIBBLE(value[7]);
1881 } else if(!strcmp(name, "help")) {
1882 printf("\nSWF layer options:\n");
1883 printf("jpegdpi=<dpi> resolution adjustment for jpeg images\n");
1884 printf("jpegsubpixels=<pixels> resolution adjustment for jpeg images (same as jpegdpi, but in pixels)\n");
1885 printf("ppmdpi=<dpi> resolution adjustment for lossless images\n");
1886 printf("ppmsubpixels=<pixels resolution adjustment for lossless images (same as ppmdpi, but in pixels)\n");
1887 printf("subpixels=<pixels> shortcut for setting both jpegsubpixels and ppmsubpixels\n");
1888 printf("drawonlyshapes convert everything to shapes (currently broken)\n");
1889 printf("ignoredraworder allow to perform a few optimizations for creating smaller SWFs\n");
1890 printf("linksopennewwindow make links open a new browser window\n");
1891 printf("linktarget target window name of new links\n");
1892 printf("linkcolor=<color) color of links (format: RRGGBBAA)\n");
1893 printf("storeallcharacters don't reduce the fonts to used characters in the output file\n");
1894 printf("enablezlib switch on zlib compression (also done if flashversion>=7)\n");
1895 printf("bboxvars store the bounding box of the SWF file in actionscript variables\n");
1896 printf("reordertags=0/1 (default: 1) perform some tag optimizations\n");
1897 printf("internallinkfunction=<name> when the user clicks a internal link (to a different page) in the converted file, this actionscript function is called\n");
1898 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");
1899 printf("disable_polygon_conversion never convert strokes to polygons (will remove capstyles and joint styles)\n");
1900 printf("caplinewidth=<width> the minimum thichness a line needs to have so that capstyles become visible (and are converted)\n");
1901 printf("insertstop put an ActionScript \"STOP\" tag in every frame\n");
1902 printf("protect add a \"protect\" tag to the file, to prevent loading in the Flash editor\n");
1903 printf("flashversion=<version> the SWF fileversion (6)\n");
1904 printf("minlinewidth=<width> convert horizontal/vertical boxes smaller than this width to lines (0.05) \n");
1905 printf("animate insert a showframe tag after each placeobject (animate draw order of PDF files)\n");
1906 printf("jpegquality=<quality> set compression quality of jpeg images\n");
1913 // --------------------------------------------------------------------
1915 static CXFORM gfxcxform_to_cxform(gfxcxform_t* c)
1918 swf_GetCXForm(0, &cx, 1);
1921 if(c->rg!=0 || c->rb!=0 || c->ra!=0 ||
1922 c->gr!=0 || c->gb!=0 || c->ga!=0 ||
1923 c->br!=0 || c->bg!=0 || c->ba!=0 ||
1924 c->ar!=0 || c->ag!=0 || c->ab!=0)
1925 msg("<warning> CXForm not SWF-compatible");
1927 cx.a0 = (S16)(c->aa*256);
1928 cx.r0 = (S16)(c->rr*256);
1929 cx.g0 = (S16)(c->gg*256);
1930 cx.b0 = (S16)(c->bb*256);
1939 static int imageInCache(gfxdevice_t*dev, void*data, int width, int height)
1943 static void addImageToCache(gfxdevice_t*dev, void*data, int width, int height)
1947 static int add_image(swfoutput_internal*i, gfximage_t*img, int targetwidth, int targetheight, int* newwidth, int* newheight)
1949 gfxdevice_t*dev = i->dev;
1951 RGBA*mem = (RGBA*)img->data;
1953 int sizex = img->width;
1954 int sizey = img->height;
1955 int is_jpeg = i->jpeg;
1958 int newsizex=sizex, newsizey=sizey;
1961 if(is_jpeg && i->config_jpegsubpixels) {
1962 newsizex = (int)(targetwidth*i->config_jpegsubpixels + 0.5);
1963 newsizey = (int)(targetheight*i->config_jpegsubpixels + 0.5);
1964 } else if(!is_jpeg && i->config_ppmsubpixels) {
1965 newsizex = (int)(targetwidth*i->config_ppmsubpixels + 0.5);
1966 newsizey = (int)(targetheight*i->config_ppmsubpixels + 0.5);
1970 if(sizex<=0 || sizey<=0)
1977 /* TODO: cache images */
1979 if(newsizex<sizex || newsizey<sizey) {
1980 msg("<verbose> Scaling %dx%d image to %dx%d", sizex, sizey, newsizex, newsizey);
1981 newpic = swf_ImageScale(mem, sizex, sizey, newsizex, newsizey);
1982 *newwidth = sizex = newsizex;
1983 *newheight = sizey = newsizey;
1986 *newwidth = newsizex = sizex;
1987 *newheight = newsizey = sizey;
1990 int num_colors = swf_ImageGetNumberOfPaletteEntries(mem,sizex,sizey,0);
1991 int has_alpha = swf_ImageHasAlpha(mem,sizex,sizey);
1993 msg("<verbose> Drawing %dx%d %s%simage (id %d) at size %dx%d (%dx%d), %s%d colors",
1995 has_alpha?(has_alpha==2?"semi-transparent ":"transparent "):"",
1996 is_jpeg?"jpeg-":"", i->currentswfid+1,
1998 targetwidth, targetheight,
1999 /*newsizex, newsizey,*/
2000 num_colors>256?">":"", num_colors>256?256:num_colors);
2002 /*RGBA* pal = (RGBA*)rfx_alloc(sizeof(RGBA)*num_colors);
2003 swf_ImageGetNumberOfPaletteEntries(mem,sizex,sizey,pal);
2005 for(t=0;t<num_colors;t++) {
2006 printf("%02x%02x%02x%02x ",
2007 pal[t].r, pal[t].g, pal[t].b, pal[t].a);
2014 int cacheid = imageInCache(dev, mem, sizex, sizey);
2017 bitid = getNewID(dev);
2018 i->tag = swf_AddImage(i->tag, bitid, mem, sizex, sizey, i->config_jpegquality);
2019 addImageToCache(dev, mem, sizex, sizey);
2029 static SRECT gfxline_getSWFbbox(gfxline_t*line)
2031 gfxbbox_t bbox = gfxline_getbbox(line);
2033 r.xmin = (int)(bbox.xmin*20);
2034 r.ymin = (int)(bbox.ymin*20);
2035 r.xmax = (int)(bbox.xmax*20);
2036 r.ymax = (int)(bbox.ymax*20);
2040 int line_is_empty(gfxline_t*line)
2043 if(line->type != gfx_moveTo)
2050 static void swf_fillbitmap(gfxdevice_t*dev, gfxline_t*line, gfximage_t*img, gfxmatrix_t*matrix, gfxcxform_t*cxform)
2052 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
2054 if(line_is_empty(line))
2060 int targetx = (int)(sqrt(matrix->m00*matrix->m00 + matrix->m01*matrix->m01)*img->width);
2061 int targety = (int)(sqrt(matrix->m10*matrix->m10 + matrix->m11*matrix->m11)*img->height);
2063 int newwidth=0,newheight=0;
2064 int bitid = add_image(i, img, targetx, targety, &newwidth, &newheight);
2067 double fx = (double)img->width / (double)newwidth;
2068 double fy = (double)img->height / (double)newheight;
2071 m.sx = (int)(65536*20*matrix->m00*fx); m.r1 = (int)(65536*20*matrix->m10*fy);
2072 m.r0 = (int)(65536*20*matrix->m01*fx); m.sy = (int)(65536*20*matrix->m11*fy);
2073 m.tx = (int)(matrix->tx*20);
2074 m.ty = (int)(matrix->ty*20);
2077 int myshapeid = getNewID(dev);
2078 i->tag = swf_InsertTag(i->tag,ST_DEFINESHAPE);
2080 swf_ShapeNew(&shape);
2081 int fsid = swf_ShapeAddBitmapFillStyle(shape,&m,bitid,1);
2082 swf_SetU16(i->tag, myshapeid);
2083 SRECT r = gfxline_getSWFbbox(line);
2084 r = swf_ClipRect(i->pagebbox, r);
2085 swf_SetRect(i->tag,&r);
2086 swf_SetShapeStyles(i->tag,shape);
2087 swf_ShapeCountBits(shape,NULL,NULL);
2088 swf_SetShapeBits(i->tag,shape);
2089 swf_ShapeSetAll(i->tag,shape,UNDEFINED_COORD,UNDEFINED_COORD,0,fsid,0);
2090 i->swflastx = i->swflasty = UNDEFINED_COORD;
2091 drawgfxline(dev, line);
2092 swf_ShapeSetEnd(i->tag);
2093 swf_ShapeFree(shape);
2095 msg("<trace> Placing image, shape ID %d, bitmap ID %d", myshapeid, bitid);
2096 i->tag = swf_InsertTag(i->tag,ST_PLACEOBJECT2);
2097 CXFORM cxform2 = gfxcxform_to_cxform(cxform);
2098 swf_ObjectPlace(i->tag,myshapeid,getNewDepth(dev),&i->page_matrix,&cxform2,NULL);
2101 static RGBA col_black = {255,0,0,0};
2103 static void drawoutline(gfxdevice_t*dev, gfxline_t*line)
2105 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
2107 int myshapeid = getNewID(dev);
2108 i->tag = swf_InsertTag(i->tag,ST_DEFINESHAPE3);
2111 swf_ShapeNew(&shape);
2112 int lsid = swf_ShapeAddLineStyle(shape,1,&col_black);
2114 swf_SetU16(i->tag,myshapeid);
2115 SRECT r = gfxline_getSWFbbox(line);
2116 r = swf_ClipRect(i->pagebbox, r);
2117 swf_SetRect(i->tag,&r);
2118 swf_SetShapeStyles(i->tag,shape);
2119 swf_ShapeCountBits(shape,NULL,NULL);
2120 swf_SetShapeBits(i->tag,shape);
2121 swf_ShapeSetAll(i->tag,shape,UNDEFINED_COORD,UNDEFINED_COORD,lsid,0,0);
2122 drawgfxline(dev, line);
2123 swf_ShapeSetEnd(i->tag);
2124 swf_ShapeFree(shape);
2126 i->tag = swf_InsertTag(i->tag,ST_PLACEOBJECT2);
2127 swf_ObjectPlace(i->tag, myshapeid, getNewDepth(dev), 0,0,0);
2130 static void swf_startclip(gfxdevice_t*dev, gfxline_t*line)
2132 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
2137 if(i->clippos >= 127)
2139 msg("<warning> Too many clip levels.");
2143 if(i->config_showclipshapes)
2144 drawoutline(dev, line);
2146 int myshapeid = getNewID(dev);
2147 i->tag = swf_InsertTag(i->tag,ST_DEFINESHAPE3);
2149 memset(&col, 0, sizeof(RGBA));
2152 swf_ShapeNew(&shape);
2153 int fsid = swf_ShapeAddSolidFillStyle(shape,&col);
2155 RGBA markcol = {0,i->mark[0],i->mark[1],i->mark[2]};
2156 swf_ShapeAddSolidFillStyle(shape,&markcol);
2158 swf_SetU16(i->tag,myshapeid);
2159 SRECT r = gfxline_getSWFbbox(line);
2160 r = swf_ClipRect(i->pagebbox, r);
2161 swf_SetRect(i->tag,&r);
2162 swf_SetShapeStyles(i->tag,shape);
2163 swf_ShapeCountBits(shape,NULL,NULL);
2164 swf_SetShapeBits(i->tag,shape);
2165 swf_ShapeSetAll(i->tag,shape,UNDEFINED_COORD,UNDEFINED_COORD,0,fsid,0);
2166 i->swflastx = i->swflasty = UNDEFINED_COORD;
2167 i->shapeisempty = 1;
2168 drawgfxline(dev, line);
2169 if(i->shapeisempty) {
2170 /* an empty clip shape is equivalent to a shape with no area */
2171 int x = line?line->x:0;
2172 int y = line?line->y:0;
2173 moveto(dev, i->tag, x,y);
2174 lineto(dev, i->tag, x,y);
2175 lineto(dev, i->tag, x,y);
2177 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)) {
2178 if(i->config_watermark) {
2179 gfxbbox_t r; r.xmin = r.ymin = 0;r.xmax = i->max_x;r.ymax = i->max_y;
2180 draw_watermark(dev, r, 1);
2183 swf_ShapeSetEnd(i->tag);
2184 swf_ShapeFree(shape);
2186 /* TODO: remember the bbox, and check all shapes against it */
2188 msg("<trace> Placing clip ID %d", myshapeid);
2189 i->tag = swf_InsertTag(i->tag,ST_PLACEOBJECT2);
2190 i->cliptags[i->clippos] = i->tag;
2191 i->clipshapes[i->clippos] = myshapeid;
2192 i->clipdepths[i->clippos] = getNewDepth(dev);
2196 static void swf_endclip(gfxdevice_t*dev)
2198 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
2205 msg("<error> Invalid end of clipping region");
2209 /*swf_ObjectPlaceClip(i->cliptags[i->clippos],i->clipshapes[i->clippos],i->clipdepths[i->clippos],&i->page_matrix,NULL,NULL,
2210 / * clip to depth: * / i->depth <= i->clipdepths[i->clippos]? i->depth : i->depth - 1);
2212 swf_ObjectPlaceClip(i->cliptags[i->clippos],i->clipshapes[i->clippos],i->clipdepths[i->clippos],&i->page_matrix,NULL,NULL,i->depth);
2214 static int gfxline_type(gfxline_t*line)
2220 int haszerosegments=0;
2223 if(line->type == gfx_moveTo) {
2226 } else if(line->type == gfx_lineTo) {
2230 } else if(line->type == gfx_splineTo) {
2232 if(tmpsplines>lines)
2240 if(lines==0 && splines==0) return 0;
2241 else if(lines==1 && splines==0) return 1;
2242 else if(lines==0 && splines==1) return 2;
2243 else if(splines==0) return 3;
2247 static int gfxline_has_dots(gfxline_t*line)
2255 if(line->type == gfx_moveTo) {
2256 /* test the length of the preceding line, and assume it is a dot if
2257 it's length is less than 1.0. But *only* if there's a noticable
2258 gap between the previous line and the next moveTo. (I've come
2259 across a PDF where thousands of "dots" were stringed together,
2261 int last_short_gap = short_gap;
2262 if((fabs(line->x - x) + fabs(line->y - y)) < 1.0) {
2267 if(isline && dist < 1 && !short_gap && !last_short_gap) {
2272 } else if(line->type == gfx_lineTo) {
2273 dist += fabs(line->x - x) + fabs(line->y - y);
2275 } else if(line->type == gfx_splineTo) {
2276 dist += fabs(line->sx - x) + fabs(line->sy - y) +
2277 fabs(line->x - line->sx) + fabs(line->y - line->sy);
2284 if(isline && dist < 1 && !short_gap) {
2290 static int gfxline_fix_short_edges(gfxline_t*line)
2294 if(line->type == gfx_lineTo) {
2295 if(fabs(line->x - x) + fabs(line->y - y) < 0.01) {
2298 } else if(line->type == gfx_splineTo) {
2299 if(fabs(line->sx - x) + fabs(line->sy - y) +
2300 fabs(line->x - line->sx) + fabs(line->y - line->sy) < 0.01) {
2311 static char is_inside_page(gfxdevice_t*dev, gfxcoord_t x, gfxcoord_t y)
2313 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
2314 if(x<i->min_x || x>i->max_x) return 0;
2315 if(y<i->min_y || y>i->max_y) return 0;
2319 gfxline_t* gfxline_move(gfxline_t*line, double x, double y)
2321 gfxline_t*l = line = gfxline_clone(line);
2333 //#define NORMALIZE_POLYGON_POSITIONS
2335 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)
2337 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
2338 if(line_is_empty(line))
2340 int type = gfxline_type(line);
2341 int has_dots = gfxline_has_dots(line);
2342 gfxbbox_t r = gfxline_getbbox(line);
2343 int is_outside_page = !is_inside_page(dev, r.xmin, r.ymin) || !is_inside_page(dev, r.xmax, r.ymax);
2345 /* TODO: * split line into segments, and perform this check for all segments */
2347 if(i->config_disable_polygon_conversion || type>=5 ||
2349 (width <= i->config_caplinewidth
2350 || (cap_style == gfx_capRound && joint_style == gfx_joinRound)
2351 || (cap_style == gfx_capRound && type<=2)))) {} else
2353 /* convert line to polygon */
2354 msg("<trace> draw as polygon, type=%d dots=%d", type, has_dots);
2356 gfxline_fix_short_edges(line);
2357 /* we need to convert the line into a polygon */
2358 gfxpoly_t* poly = gfxpoly_strokeToPoly(line, width, cap_style, joint_style, miterLimit);
2359 gfxline_t*gfxline = gfxpoly_to_gfxline(poly);
2360 dev->fill(dev, gfxline, color);
2361 gfxline_free(gfxline);
2366 msg("<trace> draw as stroke, type=%d dots=%d", type, has_dots);
2369 if(i->config_normalize_polygon_positions) {
2371 double startx = 0, starty = 0;
2372 if(line && line->type == gfx_moveTo) {
2376 line = gfxline_move(line, -startx, -starty);
2377 i->shapeposx = (int)(startx*20);
2378 i->shapeposy = (int)(starty*20);
2381 swfoutput_setstrokecolor(dev, color->r, color->g, color->b, color->a);
2382 swfoutput_setlinewidth(dev, width);
2385 drawgfxline(dev, line);
2387 if(i->config_normalize_polygon_positions) {
2388 free(line); //account for _move
2393 static void swf_fill(gfxdevice_t*dev, gfxline_t*line, gfxcolor_t*color)
2395 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
2396 if(line_is_empty(line))
2400 gfxbbox_t r = gfxline_getbbox(line);
2401 int is_outside_page = !is_inside_page(dev, r.xmin, r.ymin) || !is_inside_page(dev, r.xmax, r.ymax);
2403 //if(i->chardatapos && i->chardata[i->chardatapos-1].color.a) {
2406 if(!i->config_ignoredraworder)
2409 if(i->config_normalize_polygon_positions) {
2411 double startx = 0, starty = 0;
2412 if(line && line->type == gfx_moveTo) {
2416 line = gfxline_move(line, -startx, -starty);
2417 i->shapeposx = (int)(startx*20);
2418 i->shapeposy = (int)(starty*20);
2421 swfoutput_setfillcolor(dev, color->r, color->g, color->b, color->a);
2425 drawgfxline(dev, line);
2427 if(i->currentswfid==2 && r.xmin==0 && r.ymin==0 && r.xmax==i->max_x && r.ymax==i->max_y) {
2428 if(i->config_watermark) {
2429 draw_watermark(dev, r, 1);
2433 msg("<trace> end of swf_fill (shapeid=%d)", i->shapeid);
2435 if(i->config_normalize_polygon_positions) {
2436 free(line); //account for _move
2440 static GRADIENT* gfxgradient_to_GRADIENT(gfxgradient_t*gradient)
2443 gfxgradient_t*g = gradient;
2448 GRADIENT* swfgradient = malloc(sizeof(GRADIENT));
2449 swfgradient->num = num;
2450 swfgradient->rgba = malloc(sizeof(swfgradient->rgba[0])*num);
2451 swfgradient->ratios = malloc(sizeof(swfgradient->ratios[0])*num);
2456 swfgradient->ratios[num] = g->pos*255;
2457 swfgradient->rgba[num] = *(RGBA*)&g->color;
2464 static void swf_fillgradient(gfxdevice_t*dev, gfxline_t*line, gfxgradient_t*gradient, gfxgradienttype_t type, gfxmatrix_t*matrix)
2466 if(line_is_empty(line))
2468 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
2470 if(line_is_empty(line))
2473 GRADIENT* swfgradient = gfxgradient_to_GRADIENT(gradient);
2480 double f = type==gfxgradient_radial?4:4;
2482 m.sx = (int)(matrix->m00*20*f); m.r1 = (int)(matrix->m10*20*f);
2483 m.r0 = (int)(matrix->m01*20*f); m.sy = (int)(matrix->m11*20*f);
2484 m.tx = (int)(matrix->tx*20);
2485 m.ty = (int)(matrix->ty*20);
2488 int myshapeid = getNewID(dev);
2489 i->tag = swf_InsertTag(i->tag, ST_DEFINESHAPE2);
2491 swf_ShapeNew(&shape);
2492 int fsid = swf_ShapeAddGradientFillStyle(shape,&m,swfgradient,type==gfxgradient_radial);
2493 swf_SetU16(i->tag, myshapeid);
2494 SRECT r = gfxline_getSWFbbox(line);
2495 r = swf_ClipRect(i->pagebbox, r);
2496 swf_SetRect(i->tag,&r);
2497 swf_SetShapeStyles(i->tag,shape);
2498 swf_ShapeCountBits(shape,NULL,NULL);
2499 swf_SetShapeBits(i->tag,shape);
2500 swf_ShapeSetAll(i->tag,shape,UNDEFINED_COORD,UNDEFINED_COORD,0,fsid,0);
2501 i->swflastx = i->swflasty = UNDEFINED_COORD;
2502 drawgfxline(dev, line);
2503 swf_ShapeSetEnd(i->tag);
2504 swf_ShapeFree(shape);
2506 int depth = getNewDepth(dev);
2507 msg("<trace> Placing gradient, shape ID %d, depth %d", myshapeid, depth);
2508 i->tag = swf_InsertTag(i->tag,ST_PLACEOBJECT2);
2509 swf_ObjectPlace(i->tag,myshapeid,depth,&i->page_matrix,NULL,NULL);
2511 swf_FreeGradient(swfgradient);free(swfgradient);
2514 static SWFFONT* gfxfont_to_swffont(gfxfont_t*font, const char* id)
2516 SWFFONT*swffont = (SWFFONT*)rfx_calloc(sizeof(SWFFONT));
2518 SRECT bounds = {0,0,0,0};
2520 swffont->version = 2;
2521 swffont->name = (U8*)strdup(id);
2522 swffont->layout = (SWFLAYOUT*)rfx_calloc(sizeof(SWFLAYOUT));
2523 swffont->layout->ascent = 0; /* ? */
2524 swffont->layout->descent = 0;
2525 swffont->layout->leading = 0;
2526 swffont->layout->bounds = (SRECT*)rfx_calloc(sizeof(SRECT)*font->num_glyphs);
2527 swffont->encoding = FONT_ENCODING_UNICODE;
2528 swffont->numchars = font->num_glyphs;
2529 swffont->maxascii = font->max_unicode;
2530 swffont->ascii2glyph = (int*)rfx_calloc(sizeof(int)*swffont->maxascii);
2531 swffont->glyph2ascii = (U16*)rfx_calloc(sizeof(U16)*swffont->numchars);
2532 swffont->glyph = (SWFGLYPH*)rfx_calloc(sizeof(SWFGLYPH)*swffont->numchars);
2533 swffont->glyphnames = (char**)rfx_calloc(sizeof(char*)*swffont->numchars);
2534 for(t=0;t<font->max_unicode;t++) {
2535 swffont->ascii2glyph[t] = font->unicode2glyph[t];
2537 for(t=0;t<font->num_glyphs;t++) {
2541 swffont->glyph2ascii[t] = font->glyphs[t].unicode;
2542 if(swffont->glyph2ascii[t] == 0xffff || swffont->glyph2ascii[t] == 0x0000) {
2543 /* flash 8 flashtype requires unique unicode IDs for each character.
2544 We use the Unicode private user area to assign characters, hoping that
2545 the font doesn't contain more than 2048 glyphs */
2546 swffont->glyph2ascii[t] = 0xe000 + (t&0x1fff);
2549 if(font->glyphs[t].name) {
2550 swffont->glyphnames[t] = strdup(font->glyphs[t].name);
2552 swffont->glyphnames[t] = 0;
2554 advance = (int)(font->glyphs[t].advance);
2556 swf_Shape01DrawerInit(&draw, 0);
2557 line = font->glyphs[t].line;
2560 c.x = line->sx * GLYPH_SCALE; c.y = line->sy * GLYPH_SCALE;
2561 to.x = line->x * GLYPH_SCALE; to.y = line->y * GLYPH_SCALE;
2562 if(line->type == gfx_moveTo) {
2563 draw.moveTo(&draw, &to);
2564 } else if(line->type == gfx_lineTo) {
2565 draw.lineTo(&draw, &to);
2566 } else if(line->type == gfx_splineTo) {
2567 draw.splineTo(&draw, &c, &to);
2572 swffont->glyph[t].shape = swf_ShapeDrawerToShape(&draw);
2573 swffont->layout->bounds[t] = swf_ShapeDrawerGetBBox(&draw);
2575 int xmax = swffont->layout->bounds[t].xmax / 20;
2576 if(xmax>0 && xmax*2 < advance) {
2577 printf("fix bad advance value: bbox=%d, advance=%d (%f)\n", xmax, advance, font->glyphs[t].advance);
2581 if(advance<32768/20) {
2582 swffont->glyph[t].advance = advance*20;
2584 swffont->glyph[t].advance = 32767;
2587 draw.dealloc(&draw);
2589 swf_ExpandRect2(&bounds, &swffont->layout->bounds[t]);
2591 if(bounds.ymin < 0 && bounds.ymax > 0) {
2592 swffont->layout->ascent = -bounds.ymin;
2593 swffont->layout->descent = bounds.ymax;
2594 swffont->layout->leading = bounds.ymax - bounds.ymin;
2596 swffont->layout->ascent = (bounds.ymax - bounds.ymin)/2;
2597 swffont->layout->descent = (bounds.ymax - bounds.ymin)/2;
2598 swffont->layout->leading = bounds.ymax - bounds.ymin;
2600 swffont->layout->descent= (bounds.ymax - bounds.ymin);
2601 swffont->layout->ascent = 0;
2602 swffont->layout->leading = bounds.ymax - bounds.ymin;
2607 static void swf_addfont(gfxdevice_t*dev, gfxfont_t*font)
2609 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
2611 if(i->swffont && i->swffont->name && !strcmp((char*)i->swffont->name,font->id))
2612 return; // the requested font is the current font
2614 fontlist_t*last=0,*l = i->fontlist;
2617 if(!strcmp((char*)l->swffont->name, font->id)) {
2618 return; // we already know this font
2622 l = (fontlist_t*)rfx_calloc(sizeof(fontlist_t));
2623 l->swffont = gfxfont_to_swffont(font, font->id);
2630 swf_FontSetID(l->swffont, getNewID(i->dev));
2632 if(getScreenLogLevel() >= LOGLEVEL_DEBUG) {
2634 // print font information
2635 msg("<debug> Font %s",font->id);
2636 msg("<debug> | ID: %d", l->swffont->id);
2637 msg("<debug> | Version: %d", l->swffont->version);
2638 msg("<debug> | Name: %s", l->swffont->name);
2639 msg("<debug> | Numchars: %d", l->swffont->numchars);
2640 msg("<debug> | Maxascii: %d", l->swffont->maxascii);
2641 msg("<debug> | Style: %d", l->swffont->style);
2642 msg("<debug> | Encoding: %d", l->swffont->encoding);
2643 for(iii=0; iii<l->swffont->numchars;iii++) {
2644 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,
2645 l->swffont->layout->bounds[iii].xmin/20.0,
2646 l->swffont->layout->bounds[iii].ymin/20.0,
2647 l->swffont->layout->bounds[iii].xmax/20.0,
2648 l->swffont->layout->bounds[iii].ymax/20.0
2651 for(t=0;t<l->swffont->maxascii;t++) {
2652 if(l->swffont->ascii2glyph[t] == iii)
2653 msg("<debug> | - maps to %d",t);
2659 static void swf_switchfont(gfxdevice_t*dev, const char*fontid)
2661 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
2663 if(i->swffont && i->swffont->name && !strcmp((char*)i->swffont->name,fontid))
2664 return; // the requested font is the current font
2666 fontlist_t*l = i->fontlist;
2668 if(!strcmp((char*)l->swffont->name, fontid)) {
2669 i->swffont = l->swffont;
2674 msg("<error> Unknown font id: %s", fontid);
2678 static void swf_drawchar(gfxdevice_t*dev, gfxfont_t*font, int glyph, gfxcolor_t*color, gfxmatrix_t*matrix)
2680 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
2682 msg("<error> swf_drawchar called (glyph %d) without font", glyph);
2685 if(!i->swffont || !i->swffont->name || strcmp((char*)i->swffont->name,font->id)) // not equal to current font
2687 /* TODO: remove the need for this (enhance getcharacterbbox so that it can cope
2688 with multiple fonts */
2690 swf_switchfont(dev, font->id); // set the current font
2693 msg("<warning> swf_drawchar: Font is NULL");
2696 if(glyph<0 || glyph>=i->swffont->numchars) {
2697 msg("<warning> No character %d in font %s (%d chars)", glyph, FIXNULL((char*)i->swffont->name), i->swffont->numchars);
2701 setfontscale(dev, matrix->m00, matrix->m01, matrix->m10, matrix->m11, matrix->tx, matrix->ty, 0);
2703 double det = i->fontmatrix.sx/65536.0 * i->fontmatrix.sy/65536.0 -
2704 i->fontmatrix.r0/65536.0 * i->fontmatrix.r1/65536.0;
2705 if(fabs(det) < 0.0005) {
2706 /* x direction equals y direction- the text is invisible */
2707 msg("<verbose> Not drawing invisible character character %d (det=%f, m=[%f %f;%f %f]\n", glyph,
2709 i->fontmatrix.sx/65536.0, i->fontmatrix.r1/65536.0,
2710 i->fontmatrix.r0/65536.0, i->fontmatrix.sy/65536.0);
2714 /*if(i->swffont->glyph[glyph].shape->bitlen <= 16) {
2715 msg("<warning> Glyph %d in current charset (%s, %d characters) is empty",
2716 glyph, FIXNULL((char*)i->swffont->name), i->swffont->numchars);
2720 /* calculate character position with respect to the current font matrix */
2721 double s = 20 * GLYPH_SCALE / det;
2722 double px = matrix->tx - i->fontmatrix.tx/20.0;
2723 double py = matrix->ty - i->fontmatrix.ty/20.0;
2724 int x = (SCOORD)(( px * i->fontmatrix.sy/65536.0 - py * i->fontmatrix.r1/65536.0)*s);
2725 int y = (SCOORD)((- px * i->fontmatrix.r0/65536.0 + py * i->fontmatrix.sx/65536.0)*s);
2726 if(x>32767 || x<-32768 || y>32767 || y<-32768) {
2727 msg("<verbose> Moving character origin to %f %f\n", matrix->tx, matrix->ty);
2729 setfontscale(dev, matrix->m00, matrix->m01, matrix->m10, matrix->m11, matrix->tx, matrix->ty, 1);
2737 msg("<trace> Drawing char %d in font %d at %d,%d in color %02x%02x%02x%02x",
2738 glyph, i->swffont->id, x, y, color->r, color->g, color->b, color->a);
2740 putcharacter(dev, i->swffont->id, glyph, x, y, i->current_font_size, *(RGBA*)color);
2741 swf_FontUseGlyph(i->swffont, glyph);