3 Part of the swftools package.
5 Copyright (c) 2001,2002,2003,2004,2005 Matthias Kramm <kramm@quiss.org>
7 Swftools is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 2 of the License, or
10 (at your option) any later version.
12 Swftools is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
17 You should have received a copy of the GNU General Public License
18 along with swftools; if not, write to the Free Software
19 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
24 #include "../../config.h"
40 #include "../rfxswf.h"
41 #include "../gfxdevice.h"
42 #include "../gfxtools.h"
44 #include "../gfxpoly.h"
47 #define CHARDATAMAX 8192
51 typedef struct _chardata {
53 int fontid; /* TODO: use a SWFFONT instead */
60 typedef struct _fontlist
63 struct _fontlist*next;
66 typedef long int twip;
68 typedef struct _swfmatrix {
69 double m11,m12,m21,m22,m31,m32;
72 typedef struct _swfoutput_internal
74 gfxdevice_t*dev; // the gfxdevice object where this internal struct resides
76 double config_dumpfonts;
77 double config_ppmsubpixels;
78 double config_jpegsubpixels;
80 int config_simpleviewer;
81 int config_opennewwindow;
82 int config_ignoredraworder;
83 int config_drawonlyshapes;
84 int config_frameresets;
85 int config_linknameurl;
86 int config_jpegquality;
87 int config_storeallcharacters;
88 int config_enablezlib;
89 int config_insertstoptag;
91 int config_flashversion;
92 int config_reordertags;
93 int config_showclipshapes;
94 int config_splinemaxerror;
95 int config_fontsplinemaxerror;
96 int config_filloverlap;
99 int config_disable_polygon_conversion;
100 int config_normalize_polygon_positions;
101 char config_disablelinks;
102 RGBA config_linkcolor;
103 float config_minlinewidth;
104 double config_caplinewidth;
105 char* config_linktarget;
106 char*config_internallinkfunction;
107 char*config_externallinkfunction;
109 double config_framerate;
113 fontlist_t* fontlist;
152 int pic_height[1024];
159 char fillstylechanged;
161 int jpeg; //next image type
168 chardata_t chardata[CHARDATAMAX];
175 int current_font_size;
177 double lastfontm11,lastfontm12,lastfontm21,lastfontm22;
188 } swfoutput_internal;
190 static void swf_fillbitmap(gfxdevice_t*driver, gfxline_t*line, gfximage_t*img, gfxmatrix_t*move, gfxcxform_t*cxform);
191 static int swf_setparameter(gfxdevice_t*driver, const char*key, const char*value);
192 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);
193 static void swf_startclip(gfxdevice_t*dev, gfxline_t*line);
194 static void swf_endclip(gfxdevice_t*dev);
195 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);
196 static void swf_fill(gfxdevice_t*dev, gfxline_t*line, gfxcolor_t*color);
197 static void swf_fillbitmap(gfxdevice_t*dev, gfxline_t*line, gfximage_t*img, gfxmatrix_t*matrix, gfxcxform_t*cxform);
198 static void swf_fillgradient(gfxdevice_t*dev, gfxline_t*line, gfxgradient_t*gradient, gfxgradienttype_t type, gfxmatrix_t*matrix);
199 static void swf_drawchar(gfxdevice_t*dev, gfxfont_t*font, int glyph, gfxcolor_t*color, gfxmatrix_t*matrix);
200 static void swf_addfont(gfxdevice_t*dev, gfxfont_t*font);
201 static void swf_drawlink(gfxdevice_t*dev, gfxline_t*line, const char*action);
202 static void swf_startframe(gfxdevice_t*dev, int width, int height);
203 static void swf_endframe(gfxdevice_t*dev);
204 static gfxresult_t* swf_finish(gfxdevice_t*driver);
206 static swfoutput_internal* init_internal_struct()
208 swfoutput_internal*i = (swfoutput_internal*)malloc(sizeof(swfoutput_internal));
209 memset(i, 0, sizeof(swfoutput_internal));
233 i->fillstylechanged = 0;
240 i->config_disablelinks=0;
241 i->config_dumpfonts=0;
242 i->config_ppmsubpixels=0;
243 i->config_jpegsubpixels=0;
244 i->config_opennewwindow=1;
245 i->config_ignoredraworder=0;
246 i->config_drawonlyshapes=0;
247 i->config_jpegquality=85;
248 i->config_storeallcharacters=0;
249 i->config_enablezlib=0;
250 i->config_insertstoptag=0;
251 i->config_flashversion=6;
252 i->config_framerate=0.25;
253 i->config_splinemaxerror=1;
254 i->config_fontsplinemaxerror=1;
255 i->config_filloverlap=0;
257 i->config_bboxvars=0;
258 i->config_showclipshapes=0;
259 i->config_minlinewidth=0.05;
260 i->config_caplinewidth=1;
261 i->config_linktarget=0;
262 i->config_internallinkfunction=0;
263 i->config_externallinkfunction=0;
264 i->config_reordertags=1;
265 i->config_linknameurl=0;
267 i->config_linkcolor.r = i->config_linkcolor.g = i->config_linkcolor.b = 255;
268 i->config_linkcolor.a = 0x40;
273 static int id_error = 0;
275 static U16 getNewID(gfxdevice_t* dev)
277 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
278 if(i->currentswfid == 65535) {
280 msg("<error> ID Table overflow");
281 msg("<error> This file is too complex to render- SWF only supports 65536 shapes at once");
287 return ++i->currentswfid;
289 static U16 getNewDepth(gfxdevice_t* dev)
291 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
292 if(i->depth == 65520) {
294 msg("<error> Depth Table overflow");
295 msg("<error> This file is too complex to render- SWF only supports 65536 shapes at once");
304 static void startshape(gfxdevice_t* dev);
305 static void starttext(gfxdevice_t* dev);
306 static void endshape(gfxdevice_t* dev);
307 static void endtext(gfxdevice_t* dev);
309 typedef struct _plotxy
314 // write a move-to command into the swf
315 static int movetoxy(gfxdevice_t*dev, TAG*tag, plotxy_t p0)
317 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
318 int rx = (int)(p0.x*20);
319 int ry = (int)(p0.y*20);
320 if(rx!=i->swflastx || ry!=i->swflasty || i->fillstylechanged) {
321 swf_ShapeSetMove (tag, i->shape, rx,ry);
322 i->fillstylechanged = 0;
329 static int moveto(gfxdevice_t*dev, TAG*tag, double x, double y)
331 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
335 return movetoxy(dev, tag, p);
337 static void addPointToBBox(gfxdevice_t*dev, int px, int py)
339 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
345 swf_ExpandRect(&i->bboxrect, p);
347 swf_ExpandRect3(&i->bboxrect, p, i->linewidth*3/2);
351 /*static void plot(gfxdevice_t*dev, int x, int y, TAG*tag)
353 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
354 int width = i->linewidth/4;
358 //swf_ShapeSetLine(tag, i->shape,-width,-width);
359 //swf_ShapeSetLine(tag, i->shape,width*2,0);
360 //swf_ShapeSetLine(tag, i->shape,0,width*2);
361 //swf_ShapeSetLine(tag, i->shape,-width*2,0);
362 //swf_ShapeSetLine(tag, i->shape,0,-width*2);
363 //swf_ShapeSetLine(tag, i->shape,width,width);
366 swf_ShapeSetLine(tag, i->shape,-width,0);
367 swf_ShapeSetLine(tag, i->shape,width,-width);
368 swf_ShapeSetLine(tag, i->shape,width,width);
369 swf_ShapeSetLine(tag, i->shape,-width,width);
370 swf_ShapeSetLine(tag, i->shape,-width,-width);
371 swf_ShapeSetLine(tag, i->shape,width,0);
373 addPointToBBox(dev, x-width ,y-width);
374 addPointToBBox(dev, x+width ,y+width);
377 // write a line-to command into the swf
378 static void linetoxy(gfxdevice_t*dev, TAG*tag, plotxy_t p0)
380 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
381 int px = (int)(p0.x*20);
382 int py = (int)(p0.y*20);
383 int rx = (px-i->swflastx);
384 int ry = (py-i->swflasty);
386 swf_ShapeSetLine (tag, i->shape, rx,ry);
387 addPointToBBox(dev, i->swflastx,i->swflasty);
388 addPointToBBox(dev, px,py);
389 }/* else if(!i->fill) {
390 // treat lines of length 0 as plots, making them
391 // at least 1 twip wide so Flash will display them
392 plot(dev, i->swflastx, i->swflasty, tag);
399 static void lineto(gfxdevice_t*dev, TAG*tag, double x, double y)
404 linetoxy(dev,tag, p);
407 // write a spline-to command into the swf
408 static void splineto(gfxdevice_t*dev, TAG*tag, plotxy_t control,plotxy_t end)
410 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
411 int lastlastx = i->swflastx;
412 int lastlasty = i->swflasty;
414 int cx = ((int)(control.x*20)-i->swflastx);
415 int cy = ((int)(control.y*20)-i->swflasty);
418 int ex = ((int)(end.x*20)-i->swflastx);
419 int ey = ((int)(end.y*20)-i->swflasty);
423 if((cx || cy) && (ex || ey)) {
424 swf_ShapeSetCurve(tag, i->shape, cx,cy,ex,ey);
425 addPointToBBox(dev, lastlastx ,lastlasty );
426 addPointToBBox(dev, lastlastx+cx,lastlasty+cy);
427 addPointToBBox(dev, lastlastx+cx+ex,lastlasty+cy+ey);
428 } else if(cx || cy || ex || ey) {
429 swf_ShapeSetLine(tag, i->shape, cx+ex,cy+ey);
430 addPointToBBox(dev, lastlastx ,lastlasty );
431 addPointToBBox(dev, lastlastx+cx,lastlasty+cy);
432 addPointToBBox(dev, lastlastx+cx+ex,lastlasty+cy+ey);
438 /* write a line, given two points and the transformation
440 /*static void line(gfxdevice_t*dev, TAG*tag, plotxy_t p0, plotxy_t p1)
442 moveto(dev, tag, p0);
443 lineto(dev, tag, p1);
446 void resetdrawer(gfxdevice_t*dev)
448 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
453 static void stopFill(gfxdevice_t*dev)
455 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
456 if(i->lastwasfill!=0)
458 swf_ShapeSetStyle(i->tag,i->shape,i->linestyleid,0x8000,0);
459 i->fillstylechanged = 1;
463 static void startFill(gfxdevice_t*dev)
465 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
466 if(i->lastwasfill!=1)
468 swf_ShapeSetStyle(i->tag,i->shape,0x8000,i->fillstyleid,0);
469 i->fillstylechanged = 1;
474 static inline int colorcompare(gfxdevice_t*dev, RGBA*a,RGBA*b)
486 static SRECT getcharacterbbox(gfxdevice_t*dev, SWFFONT*font, MATRIX* m)
488 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
491 memset(&r, 0, sizeof(r));
494 if(debug) printf("\n");
495 for(t=0;t<i->chardatapos;t++)
497 if(i->chardata[t].fontid != font->id) {
498 msg("<error> Internal error: fontid %d != fontid %d", i->chardata[t].fontid, font->id);
501 SRECT b = font->layout->bounds[i->chardata[t].charid];
502 b.xmin *= i->chardata[t].size;
503 b.ymin *= i->chardata[t].size;
504 b.xmax *= i->chardata[t].size;
505 b.ymax *= i->chardata[t].size;
507 /* divide by 1024, rounding xmax/ymax up */
508 b.xmax += 1023; b.ymax += 1023; b.xmin /= 1024; b.ymin /= 1024; b.xmax /= 1024; b.ymax /= 1024;
510 b.xmin += i->chardata[t].x;
511 b.ymin += i->chardata[t].y;
512 b.xmax += i->chardata[t].x;
513 b.ymax += i->chardata[t].y;
515 /* until we solve the INTERNAL_SCALING problem (see below)
516 make sure the bounding box is big enough */
522 b = swf_TurnRect(b, m);
524 if(debug) printf("(%f,%f,%f,%f) -> (%f,%f,%f,%f) [font %d/%d, char %d]\n",
525 font->layout->bounds[i->chardata[t].charid].xmin/20.0,
526 font->layout->bounds[i->chardata[t].charid].ymin/20.0,
527 font->layout->bounds[i->chardata[t].charid].xmax/20.0,
528 font->layout->bounds[i->chardata[t].charid].ymax/20.0,
533 i->chardata[t].fontid,
535 i->chardata[t].charid
537 swf_ExpandRect2(&r, &b);
539 if(debug) printf("-----> (%f,%f,%f,%f)\n",
547 static void putcharacters(gfxdevice_t*dev, TAG*tag)
549 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
553 color.r = i->chardata[0].color.r^255;
562 int charadvance[128];
565 int glyphbits=1; //TODO: can this be zero?
568 if(tag->id != ST_DEFINETEXT &&
569 tag->id != ST_DEFINETEXT2) {
570 msg("<error> internal error: putcharacters needs an text tag, not %d\n",tag->id);
573 if(!i->chardatapos) {
574 msg("<warning> putcharacters called with zero characters");
577 for(pass = 0; pass < 2; pass++)
587 advancebits++; // add sign bit
588 swf_SetU8(tag, glyphbits);
589 swf_SetU8(tag, advancebits);
592 for(t=0;t<=i->chardatapos;t++)
594 if(lastfontid != i->chardata[t].fontid ||
595 lastx!=i->chardata[t].x ||
596 lasty!=i->chardata[t].y ||
597 !colorcompare(dev,&color, &i->chardata[t].color) ||
599 lastsize != i->chardata[t].size ||
602 if(charstorepos && pass==0)
605 for(s=0;s<charstorepos;s++)
607 while(charids[s]>=(1<<glyphbits))
609 while(charadvance[s]>=(1<<advancebits))
613 if(charstorepos && pass==1)
615 tag->writeBit = 0; // Q&D
616 swf_SetBits(tag, 0, 1); // GLYPH Record
617 swf_SetBits(tag, charstorepos, 7); // number of glyphs
619 for(s=0;s<charstorepos;s++)
621 swf_SetBits(tag, charids[s], glyphbits);
622 swf_SetBits(tag, charadvance[s], advancebits);
627 if(pass == 1 && t<i->chardatapos)
633 if(lastx != i->chardata[t].x ||
634 lasty != i->chardata[t].y)
636 newx = i->chardata[t].x;
637 newy = i->chardata[t].y;
643 if(!colorcompare(dev,&color, &i->chardata[t].color))
645 color = i->chardata[t].color;
648 font.id = i->chardata[t].fontid;
649 if(lastfontid != i->chardata[t].fontid || lastsize != i->chardata[t].size)
652 tag->writeBit = 0; // Q&D
653 swf_TextSetInfoRecord(tag, newfont, i->chardata[t].size, newcolor, newx,newy);
656 lastfontid = i->chardata[t].fontid;
657 lastx = i->chardata[t].x;
658 lasty = i->chardata[t].y;
659 lastsize = i->chardata[t].size;
662 if(t==i->chardatapos)
666 int nextt = t==i->chardatapos-1?t:t+1;
667 int rel = i->chardata[nextt].x-i->chardata[t].x;
668 if(rel>=0 && (rel<(1<<(advancebits-1)) || pass==0)) {
670 lastx=i->chardata[nextt].x;
674 lastx=i->chardata[t].x;
676 charids[charstorepos] = i->chardata[t].charid;
677 charadvance[charstorepos] = advance;
684 static void putcharacter(gfxdevice_t*dev, int fontid, int charid, int x,int y, int size, RGBA color)
686 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
687 if(i->chardatapos == CHARDATAMAX)
689 msg("<warning> Character buffer too small. SWF will be slightly bigger");
693 i->chardata[i->chardatapos].fontid = fontid;
694 i->chardata[i->chardatapos].charid = charid;
695 i->chardata[i->chardatapos].x = x;
696 i->chardata[i->chardatapos].y = y;
697 i->chardata[i->chardatapos].color = color;
698 i->chardata[i->chardatapos].size = size;
702 /* Notice: we can only put chars in the range -1639,1638 (-32768/20,32768/20).
703 So if we set this value to high, the char coordinates will overflow.
704 If we set it to low, however, the char positions will be inaccurate */
705 #define GLYPH_SCALE 1
707 static void endtext(gfxdevice_t*dev)
709 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
713 i->tag = swf_InsertTag(i->tag,ST_DEFINETEXT2);
714 swf_SetU16(i->tag, i->textid);
717 r = getcharacterbbox(dev, i->swffont, &i->fontmatrix);
718 r = swf_ClipRect(i->pagebbox, r);
719 swf_SetRect(i->tag,&r);
721 swf_SetMatrix(i->tag,&i->fontmatrix);
723 msg("<trace> Placing text (%d characters) as ID %d", i->chardatapos, i->textid);
725 putcharacters(dev, i->tag);
728 if(i->swf->fileVersion >= 8) {
729 i->tag = swf_InsertTag(i->tag, ST_CSMTEXTSETTINGS);
730 swf_SetU16(i->tag, i->textid);
732 //swf_SetU8(i->tag, /*subpixel grid*/(2<<3)|/*flashtype*/0x40);
733 swf_SetU8(i->tag, /*grid*/(1<<3)|/*flashtype*/0x40);
734 //swf_SetU8(i->tag, /*no grid*/(0<<3)|/*flashtype*/0x40);
736 swf_SetU32(i->tag, 0);//thickness
737 swf_SetU32(i->tag, 0);//sharpness
738 swf_SetU8(i->tag, 0);//reserved
740 i->tag = swf_InsertTag(i->tag,ST_PLACEOBJECT2);
742 swf_ObjectPlace(i->tag,i->textid,getNewDepth(dev),&i->page_matrix,NULL,NULL);
746 /* set's the matrix which is to be applied to characters drawn by swfoutput_drawchar() */
747 static void setfontscale(gfxdevice_t*dev,double m11,double m12, double m21,double m22,double x, double y, char force)
753 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
754 if(i->lastfontm11 == m11 &&
755 i->lastfontm12 == m12 &&
756 i->lastfontm21 == m21 &&
757 i->lastfontm22 == m22 && !force)
762 i->lastfontm11 = m11;
763 i->lastfontm12 = m12;
764 i->lastfontm21 = m21;
765 i->lastfontm22 = m22;
767 double xsize = sqrt(m11*m11 + m12*m12);
768 double ysize = sqrt(m21*m21 + m22*m22);
769 i->current_font_size = (xsize>ysize?xsize:ysize)*1;
770 if(i->current_font_size < 1)
771 i->current_font_size = 1;
772 double ifs = 1.0 / (i->current_font_size*GLYPH_SCALE);
775 m.sx = (S32)((m11*ifs)*65536); m.r1 = (S32)((m21*ifs)*65536);
776 m.r0 = (S32)((m12*ifs)*65536); m.sy = (S32)((m22*ifs)*65536);
777 /* this is the position of the first char to set a new fontmatrix-
778 we hope that it's close enough to all other characters using the
779 font, so we use its position as origin for the matrix */
785 static int watermark2_width=47;
786 static int watermark2_height=11;
787 static int watermark2[47] = {95,1989,71,0,2015,337,1678,0,2015,5,1921,320,1938,25,2006,1024,
788 1042,21,13,960,1039,976,8,2000,1359,1088,31,1989,321,1728,0,1152,
789 1344,832,0,1984,0,896,1088,1088,896,0,1984,128,256,512,1984};
791 static void draw_watermark(gfxdevice_t*dev, gfxbbox_t r, char drawall)
793 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
794 double wx = r.xmax / 5.0;
795 double tx = r.xmax*4.0 / 5.0;
796 double ty = r.ymax-wx*watermark2_height/watermark2_width;
797 double sx = (r.xmax - tx) / watermark2_width;
798 double sy = (r.ymax - ty) / watermark2_height;
801 if(ty > 0 && px > 1.0 && py > 1.0) {
803 for(y=0;y<watermark2_height;y++)
804 for(x=0;x<watermark2_width;x++) {
805 if(((watermark2[x]>>y)&1)) {
806 if(!drawall && rand()%5)
808 unsigned int b = rand();
809 moveto(dev, i->tag, x*sx+tx+((b>>1)&1)/20.0, y*sy+ty+((b>>3)&1)/20.0);
810 lineto(dev, i->tag, x*sx+px+tx+((b>>2)&1)/20.0, y*sy+ty+((b>>3)&1)/20.0);
811 lineto(dev, i->tag, x*sx+px+tx+((b>>2)&1)/20.0, y*sy+py+ty+((b>>4)&1)/20.0);
812 lineto(dev, i->tag, x*sx+tx+((b>>1)&1)/20.0, y*sy+py+ty+((b>>4)&1)/20.0);
813 lineto(dev, i->tag, x*sx+tx+((b>>1)&1)/20.0, y*sy+ty+((b>>3)&1)/20.0);
819 static void swfoutput_setfillcolor(gfxdevice_t* dev, U8 r, U8 g, U8 b, U8 a)
821 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
822 if(i->fillrgb.r == r &&
825 i->fillrgb.a == a) return;
834 static void insert_watermark(gfxdevice_t*dev, char drawall)
836 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
837 if(!drawall && i->watermarks>20)
843 swfoutput_setfillcolor(dev, 0,0,255,192);
845 swfoutput_setfillcolor(dev, rand(),rand(),rand(),(rand()&127)+128);
850 gfxbbox_t r; r.xmin = r.ymin = 0;
853 draw_watermark(dev, r, drawall);
859 static void endpage(gfxdevice_t*dev)
861 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
872 if(i->config_watermark) {
873 insert_watermark(dev, 1);
879 static void addViewer(gfxdevice_t* dev)
881 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
884 RGBA button_colors[3]= {{0xbf,0x00,0x00,0x80},{0xbf,0x20,0x20,0xc0}, {0xbf,0xc0,0xc0,0xff}};
886 int button_sizex = 20;
887 int button_sizey = 20;
889 RGBA black = {255,0,0,0};
891 i->tag = swf_InsertTag(i->tag,ST_DEFINESHAPE3);
893 int ls1 = swf_ShapeAddLineStyle(s,40,&black);
894 int fs1 = swf_ShapeAddSolidFillStyle(s,&button_colors[t/2]);
895 int shapeid = ids[t] = getNewID(dev);
896 swf_SetU16(i->tag,shapeid);
898 r.xmin = -20*button_sizex;
899 r.xmax = 20*button_sizex;
901 r.ymax = 40*button_sizey;
902 swf_SetRect(i->tag,&r); // set shape bounds
903 swf_SetShapeHeader(i->tag,s); // write all styles to tag
904 swf_ShapeSetAll(i->tag,s,0*button_sizex,0,ls1,fs1,0);
905 swf_ShapeSetLine(i->tag,s,(1-(t&1)*2)*20*button_sizex,20*button_sizey);
906 swf_ShapeSetLine(i->tag,s,-(1-(t&1)*2)*20*button_sizex,20*button_sizey);
907 swf_ShapeSetLine(i->tag,s,0,-40*button_sizey);
908 swf_ShapeSetEnd(i->tag); // finish drawing
909 swf_ShapeFree(s); // clean shape structure (which isn't needed anymore after writing the tag)
911 ActionTAG*a1=0,*a2=0,*a3=0;
912 a1 = action_NextFrame(a1);
913 a1 = action_Stop(a1);
916 a2 = action_PreviousFrame(a2);
917 a2 = action_Stop(a2);
920 a3 = action_Stop(a3);
923 i->tag = swf_InsertTag(i->tag, ST_DOACTION);
924 swf_ActionSet(i->tag,a3);
926 i->tag = swf_InsertTag(i->tag,ST_DEFINEBUTTON);
927 int buttonid1 = getNewID(dev);
928 swf_SetU16(i->tag, buttonid1);
929 swf_ButtonSetRecord(i->tag,BS_UP|BS_HIT,ids[0],1,NULL,NULL);
930 swf_ButtonSetRecord(i->tag,BS_OVER,ids[2],1,NULL,NULL);
931 swf_ButtonSetRecord(i->tag,BS_DOWN,ids[4],1,NULL,NULL);
932 swf_SetU8(i->tag,0); // end of button records
933 swf_ActionSet(i->tag,a1);
935 i->tag = swf_InsertTag(i->tag,ST_DEFINEBUTTON);
936 int buttonid2 = getNewID(dev);
937 swf_SetU16(i->tag, buttonid2);
938 swf_ButtonSetRecord(i->tag,BS_UP|BS_HIT,ids[1],1,NULL,NULL);
939 swf_ButtonSetRecord(i->tag,BS_OVER,ids[3],1,NULL,NULL);
940 swf_ButtonSetRecord(i->tag,BS_DOWN,ids[5],1,NULL,NULL);
941 swf_SetU8(i->tag,0); // end of button records
942 swf_ActionSet(i->tag,a2);
944 i->tag = swf_InsertTag(i->tag,ST_PLACEOBJECT2);
946 swf_GetMatrix(0, &m);
947 m.tx = button_sizex*20+200;
948 swf_ObjectPlace(i->tag, buttonid2, 65534,&m,0,0);
949 i->tag = swf_InsertTag(i->tag,ST_PLACEOBJECT2);
950 m.tx = button_sizex*20+200+200;
951 swf_ObjectPlace(i->tag, buttonid1, 65535,&m,0,0);
955 void swf_startframe(gfxdevice_t*dev, int width, int height)
957 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
959 if(i->config_protect) {
960 i->tag = swf_InsertTag(i->tag, ST_PROTECT);
961 i->config_protect = 0;
963 if(i->config_simpleviewer) {
968 if(!i->firstpage && !i->pagefinished)
971 msg("<verbose> Starting new SWF page of size %dx%d", width, height);
973 swf_GetMatrix(0, &i->page_matrix);
974 i->page_matrix.tx = 0;
975 i->page_matrix.ty = 0;
982 /* create a bbox structure with the page size. This is used
983 for clipping shape and text bounding boxes. As we don't want to
984 generate bounding boxes which extend beyond the movie size (in
985 order to not confuse Flash), we clip everything against i->pagebbox */
986 i->pagebbox.xmin = 0;
987 i->pagebbox.ymin = 0;
988 i->pagebbox.xmax = width*20;
989 i->pagebbox.ymax = height*20;
991 /* increase SWF's bounding box */
992 swf_ExpandRect2(&i->swf->movieSize, &i->pagebbox);
994 i->lastframeno = i->frameno;
999 void swf_endframe(gfxdevice_t*dev)
1001 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1003 if(!i->pagefinished)
1006 if( (i->swf->fileVersion <= 8) && (i->config_insertstoptag) ) {
1008 atag = action_Stop(atag);
1009 atag = action_End(atag);
1010 i->tag = swf_InsertTag(i->tag,ST_DOACTION);
1011 swf_ActionSet(i->tag,atag);
1013 i->tag = swf_InsertTag(i->tag,ST_SHOWFRAME);
1016 for(i->depth;i->depth>i->startdepth;i->depth--) {
1017 i->tag = swf_InsertTag(i->tag,ST_REMOVEOBJECT2);
1018 swf_SetU16(i->tag,i->depth);
1020 i->depth = i->startdepth;
1022 if(i->config_frameresets) {
1023 for(i->currentswfid;i->currentswfid>i->startids;i->currentswfid--) {
1024 i->tag = swf_InsertTag(i->tag,ST_FREECHARACTER);
1025 swf_SetU16(i->tag,i->currentswfid);
1027 i->currentswfid = i->startids;
1031 static void setBackground(gfxdevice_t*dev, int x1, int y1, int x2, int y2)
1033 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1035 rgb.a = rgb.r = rgb.g = rgb.b = 0xff;
1039 int shapeid = getNewID(dev);
1044 i->tag = swf_InsertTag(i->tag, ST_DEFINESHAPE);
1046 fs1 = swf_ShapeAddSolidFillStyle(s, &rgb);
1047 swf_SetU16(i->tag,shapeid);
1048 swf_SetRect(i->tag,&r);
1049 swf_SetShapeHeader(i->tag,s);
1050 swf_ShapeSetAll(i->tag,s,x1,y1,ls1,fs1,0);
1051 swf_ShapeSetLine(i->tag,s,(x2-x1),0);
1052 swf_ShapeSetLine(i->tag,s,0,(y2-y1));
1053 swf_ShapeSetLine(i->tag,s,(x1-x2),0);
1054 swf_ShapeSetLine(i->tag,s,0,(y1-y2));
1055 swf_ShapeSetEnd(i->tag);
1057 i->tag = swf_InsertTag(i->tag, ST_PLACEOBJECT2);
1058 swf_ObjectPlace(i->tag,shapeid,getNewDepth(dev),0,0,0);
1059 i->tag = swf_InsertTag(i->tag, ST_PLACEOBJECT2);
1060 swf_ObjectPlaceClip(i->tag,shapeid,getNewDepth(dev),0,0,0,65535);
1063 /* initialize the swf writer */
1064 void gfxdevice_swf_init(gfxdevice_t* dev)
1066 memset(dev, 0, sizeof(gfxdevice_t));
1070 dev->internal = init_internal_struct(); // set config to default values
1072 dev->startpage = swf_startframe;
1073 dev->endpage = swf_endframe;
1074 dev->finish = swf_finish;
1075 dev->fillbitmap = swf_fillbitmap;
1076 dev->setparameter = swf_setparameter;
1077 dev->stroke = swf_stroke;
1078 dev->startclip = swf_startclip;
1079 dev->endclip = swf_endclip;
1080 dev->fill = swf_fill;
1081 dev->fillbitmap = swf_fillbitmap;
1082 dev->fillgradient = swf_fillgradient;
1083 dev->addfont = swf_addfont;
1084 dev->drawchar = swf_drawchar;
1085 dev->drawlink = swf_drawlink;
1087 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1090 msg("<verbose> initializing swf output\n", i->max_x,i->max_y);
1094 i->swf = (SWF*)rfx_calloc(sizeof(SWF));
1095 i->swf->fileVersion = 0;
1096 i->swf->frameRate = 0x80;
1097 i->swf->movieSize.xmin = 0;
1098 i->swf->movieSize.ymin = 0;
1099 i->swf->movieSize.xmax = 0;
1100 i->swf->movieSize.ymax = 0;
1102 i->swf->firstTag = swf_InsertTag(NULL,ST_SETBACKGROUNDCOLOR);
1103 i->tag = i->swf->firstTag;
1105 rgb.a = rgb.r = rgb.g = rgb.b = 0xff;
1107 swf_SetRGB(i->tag,&rgb);
1109 i->startdepth = i->depth = 0;
1110 i->startids = i->currentswfid = 0;
1113 static void startshape(gfxdevice_t*dev)
1115 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1120 //if(i->chardatapos && i->chardata[i->chardatapos-1].color.a)
1123 i->tag = swf_InsertTag(i->tag,ST_DEFINESHAPE3);
1125 swf_ShapeNew(&i->shape);
1126 i->linestyleid = swf_ShapeAddLineStyle(i->shape,i->linewidth,&i->strokergb);
1127 i->fillstyleid = swf_ShapeAddSolidFillStyle(i->shape,&i->fillrgb);
1129 RGBA markcol = {0,i->mark[0],i->mark[1],i->mark[2]};
1130 swf_ShapeAddSolidFillStyle(i->shape,&markcol);
1133 i->shapeid = getNewID(dev);
1135 msg("<debug> Using shape id %d", i->shapeid);
1137 swf_SetU16(i->tag,i->shapeid); // ID
1139 i->bboxrectpos = i->tag->len;
1141 swf_SetRect(i->tag,&i->pagebbox);
1143 memset(&i->bboxrect, 0, sizeof(i->bboxrect));
1145 swf_SetShapeStyles(i->tag,i->shape);
1146 swf_ShapeCountBits(i->shape,NULL,NULL);
1147 swf_SetShapeBits(i->tag,i->shape);
1149 /* TODO: do we really need this? */
1150 //swf_ShapeSetAll(i->tag,i->shape,/*x*/0,/*y*/0,i->linestyleid,0,0);
1151 //swf_ShapeSetAll(i->tag,i->shape,/*x*/UNDEFINED_COORD,/*y*/UNDEFINED_COORD,i->linestyleid,0,0);
1152 i->swflastx=i->swflasty=UNDEFINED_COORD;
1153 i->lastwasfill = -1;
1154 i->shapeisempty = 1;
1157 static void starttext(gfxdevice_t*dev)
1159 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1163 if(i->config_watermark) {
1164 insert_watermark(dev, 0);
1166 i->textid = getNewID(dev);
1167 i->swflastx=i->swflasty=0;
1171 /* TODO: move to ../lib/rfxswf */
1172 void changeRect(gfxdevice_t*dev, TAG*tag, int pos, SRECT*newrect)
1174 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1175 /* determine length of old rect */
1179 swf_GetRect(tag, &old);
1180 swf_ResetReadBits(tag);
1181 int pos_end = tag->pos;
1183 int len = tag->len - pos_end;
1184 U8*data = (U8*)malloc(len);
1185 memcpy(data, &tag->data[pos_end], len);
1188 swf_SetRect(tag, newrect);
1189 swf_SetBlock(tag, data, len);
1191 tag->pos = tag->readBit = 0;
1194 void cancelshape(gfxdevice_t*dev)
1196 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1197 /* delete old shape tag */
1199 i->tag = i->tag->prev;
1200 swf_DeleteTag(0, todel);
1201 if(i->shape) {swf_ShapeFree(i->shape);i->shape=0;}
1203 i->bboxrectpos = -1;
1205 // i->currentswfid--; // doesn't work, for some reason
1208 void fixAreas(gfxdevice_t*dev)
1210 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1211 if(!i->shapeisempty && i->fill &&
1212 (i->bboxrect.xmin == i->bboxrect.xmax ||
1213 i->bboxrect.ymin == i->bboxrect.ymax) &&
1214 i->config_minlinewidth >= 0.001
1216 msg("<debug> Shape has size 0: width=%.2f height=%.2f",
1217 (i->bboxrect.xmax-i->bboxrect.xmin)/20.0,
1218 (i->bboxrect.ymax-i->bboxrect.ymin)/20.0
1221 SRECT r = i->bboxrect;
1223 if(r.xmin == r.xmax && r.ymin == r.ymax) {
1224 /* this thing comes down to a single dot- nothing to fix here */
1230 RGBA save_col = i->strokergb;
1231 int save_width = i->linewidth;
1233 i->strokergb = i->fillrgb;
1234 i->linewidth = (int)(i->config_minlinewidth*20);
1235 if(i->linewidth==0) i->linewidth = 1;
1240 moveto(dev, i->tag, r.xmin/20.0,r.ymin/20.0);
1241 lineto(dev, i->tag, r.xmax/20.0,r.ymax/20.0);
1243 i->strokergb = save_col;
1244 i->linewidth = save_width;
1249 static void endshape_noput(gfxdevice_t*dev)
1251 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1254 //changeRect(dev, i->tag, i->bboxrectpos, &i->bboxrect);
1257 swf_ShapeFree(i->shape);
1265 static void endshape(gfxdevice_t*dev)
1267 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1273 if(i->shapeisempty ||
1275 (i->bboxrect.xmin == i->bboxrect.xmax &&
1276 i->bboxrect.ymin == i->bboxrect.ymax))
1278 // delete the shape again, we didn't do anything
1279 msg("<debug> cancelling shape: bbox is (%f,%f,%f,%f)",
1280 i->bboxrect.xmin /20.0,
1281 i->bboxrect.ymin /20.0,
1282 i->bboxrect.xmax /20.0,
1283 i->bboxrect.ymax /20.0
1289 swf_ShapeSetEnd(i->tag);
1291 SRECT r = swf_ClipRect(i->pagebbox, i->bboxrect);
1292 changeRect(dev, i->tag, i->bboxrectpos, &r);
1294 msg("<trace> Placing shape ID %d", i->shapeid);
1296 i->tag = swf_InsertTag(i->tag,ST_PLACEOBJECT2);
1297 MATRIX m = i->page_matrix;
1298 m.tx += i->shapeposx;
1299 m.ty += i->shapeposy;
1300 swf_ObjectPlace(i->tag,i->shapeid,getNewDepth(dev),&m,NULL,NULL);
1302 if(i->config_animate) {
1303 i->tag = swf_InsertTag(i->tag,ST_SHOWFRAME);
1306 swf_ShapeFree(i->shape);
1309 i->bboxrectpos = -1;
1316 void wipeSWF(SWF*swf)
1318 TAG*tag = swf->firstTag;
1320 TAG*next = tag->next;
1321 if(tag->id != ST_SETBACKGROUNDCOLOR &&
1322 tag->id != ST_END &&
1323 tag->id != ST_DOACTION &&
1324 tag->id != ST_SHOWFRAME) {
1325 swf_DeleteTag(swf, tag);
1331 void swfoutput_finalize(gfxdevice_t*dev)
1333 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1335 if(i->tag && i->tag->id == ST_END)
1336 return; //already done
1338 i->swf->fileVersion = i->config_flashversion;
1339 i->swf->frameRate = i->config_framerate*0x100;
1341 if(i->config_bboxvars) {
1342 TAG* tag = swf_InsertTag(i->swf->firstTag, ST_DOACTION);
1344 a = action_PushString(a, "xmin");
1345 a = action_PushFloat(a, i->swf->movieSize.xmin / 20.0);
1346 a = action_SetVariable(a);
1347 a = action_PushString(a, "ymin");
1348 a = action_PushFloat(a, i->swf->movieSize.ymin / 20.0);
1349 a = action_SetVariable(a);
1350 a = action_PushString(a, "xmax");
1351 a = action_PushFloat(a, i->swf->movieSize.xmax / 20.0);
1352 a = action_SetVariable(a);
1353 a = action_PushString(a, "ymax");
1354 a = action_PushFloat(a, i->swf->movieSize.ymax / 20.0);
1355 a = action_SetVariable(a);
1356 a = action_PushString(a, "width");
1357 a = action_PushFloat(a, (i->swf->movieSize.xmax - i->swf->movieSize.xmin) / 20.0);
1358 a = action_SetVariable(a);
1359 a = action_PushString(a, "height");
1360 a = action_PushFloat(a, (i->swf->movieSize.ymax - i->swf->movieSize.ymin) / 20.0);
1361 a = action_SetVariable(a);
1363 swf_ActionSet(tag, a);
1368 free(i->mark);i->mark = 0;
1372 fontlist_t *iterator = i->fontlist;
1374 TAG*mtag = i->swf->firstTag;
1375 if(iterator->swffont) {
1376 if(!i->config_storeallcharacters) {
1377 msg("<debug> Reducing font %s", iterator->swffont->name);
1378 swf_FontReduce(iterator->swffont);
1380 int used = iterator->swffont->use && iterator->swffont->use->used_glyphs;
1382 mtag = swf_InsertTag(mtag, ST_DEFINEFONT2);
1383 swf_FontSetDefine2(mtag, iterator->swffont);
1387 iterator = iterator->next;
1390 i->tag = swf_InsertTag(i->tag,ST_END);
1391 TAG* tag = i->tag->prev;
1393 /* remove the removeobject2 tags between the last ST_SHOWFRAME
1394 and the ST_END- they confuse the flash player */
1395 while(tag->id == ST_REMOVEOBJECT2) {
1396 TAG* prev = tag->prev;
1397 swf_DeleteTag(i->swf, tag);
1404 if(i->config_enablezlib || i->config_flashversion>=6) {
1405 i->swf->compressed = 1;
1408 /* Add AVM2 actionscript */
1409 if(i->config_flashversion>=9 &&
1410 (i->config_insertstoptag || i->hasbuttons)) {
1411 swf_AddButtonLinks(i->swf, i->config_insertstoptag,
1412 i->config_internallinkfunction||i->config_externallinkfunction);
1414 // if(i->config_reordertags)
1415 // swf_Optimize(i->swf);
1418 int swfresult_save(gfxresult_t*gfx, const char*filename)
1420 SWF*swf = (SWF*)gfx->internal;
1423 fi = open(filename, O_BINARY|O_CREAT|O_TRUNC|O_WRONLY, 0777);
1428 msg("<fatal> Could not create \"%s\". ", FIXNULL(filename));
1432 if FAILED(swf_WriteSWF(fi,swf))
1433 msg("<error> WriteSWF() failed.\n");
1439 void* swfresult_get(gfxresult_t*gfx, const char*name)
1441 SWF*swf = (SWF*)gfx->internal;
1442 if(!strcmp(name, "swf")) {
1443 return (void*)swf_CopySWF(swf);
1444 } else if(!strcmp(name, "xmin")) {
1445 return (void*)(swf->movieSize.xmin/20);
1446 } else if(!strcmp(name, "ymin")) {
1447 return (void*)(swf->movieSize.ymin/20);
1448 } else if(!strcmp(name, "xmax")) {
1449 return (void*)(swf->movieSize.xmax/20);
1450 } else if(!strcmp(name, "ymax")) {
1451 return (void*)(swf->movieSize.ymax/20);
1452 } else if(!strcmp(name, "width")) {
1453 return (void*)((swf->movieSize.xmax - swf->movieSize.xmin)/20);
1454 } else if(!strcmp(name, "height")) {
1455 return (void*)((swf->movieSize.ymax - swf->movieSize.ymin)/20);
1459 void swfresult_destroy(gfxresult_t*gfx)
1462 swf_FreeTags((SWF*)gfx->internal);
1463 free(gfx->internal);
1466 memset(gfx, 0, sizeof(gfxresult_t));
1470 static void swfoutput_destroy(gfxdevice_t* dev);
1472 gfxresult_t* swf_finish(gfxdevice_t* dev)
1474 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1477 if(i->config_linktarget) {
1478 free(i->config_linktarget);
1479 i->config_linktarget = 0;
1482 swfoutput_finalize(dev);
1483 SWF* swf = i->swf;i->swf = 0;
1484 swfoutput_destroy(dev);
1486 result = (gfxresult_t*)rfx_calloc(sizeof(gfxresult_t));
1487 result->internal = swf;
1488 result->save = swfresult_save;
1490 result->get = swfresult_get;
1491 result->destroy = swfresult_destroy;
1495 /* Perform cleaning up */
1496 static void swfoutput_destroy(gfxdevice_t* dev)
1498 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1500 /* not initialized yet- nothing to destroy */
1504 fontlist_t *tmp,*iterator = i->fontlist;
1506 if(iterator->swffont) {
1507 swf_FontFree(iterator->swffont);iterator->swffont=0;
1510 iterator = iterator->next;
1513 if(i->swf) {swf_FreeTags(i->swf);free(i->swf);i->swf = 0;}
1516 memset(dev, 0, sizeof(gfxdevice_t));
1519 static void swfoutput_setstrokecolor(gfxdevice_t* dev, U8 r, U8 g, U8 b, U8 a)
1521 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1522 if(i->strokergb.r == r &&
1523 i->strokergb.g == g &&
1524 i->strokergb.b == b &&
1525 i->strokergb.a == a) return;
1535 //#define ROUND_UP 19
1536 //#define ROUND_UP 10
1538 static void swfoutput_setlinewidth(gfxdevice_t*dev, double _linewidth)
1540 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1541 if(i->linewidth == (U16)(_linewidth*20+19.0/20.0))
1545 i->linewidth = (U16)(_linewidth*20+19.0/20.0);
1549 static void drawlink(gfxdevice_t*dev, ActionTAG*,ActionTAG*, gfxline_t*points, char mouseover, const char*url);
1550 static void swfoutput_namedlink(gfxdevice_t*dev, char*name, gfxline_t*points);
1551 static void swfoutput_linktopage(gfxdevice_t*dev, int page, gfxline_t*points);
1552 static void swfoutput_linktourl(gfxdevice_t*dev, const char*url, gfxline_t*points);
1554 /*void swfoutput_drawlink(gfxdevice_t*dev, char*url, gfxline_t*points)
1556 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1557 dev->drawlink(dev, points, url);
1560 void swf_drawlink(gfxdevice_t*dev, gfxline_t*points, const char*url)
1562 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1564 if(i->config_disablelinks)
1567 if(!strncmp("http://pdf2swf:", url, 15)) {
1568 char*tmp = strdup(url);
1569 int l = strlen(tmp);
1572 swfoutput_namedlink(dev, tmp+15, points);
1575 } else if(!strncmp("page", url, 4)) {
1578 if(url[t]<'0' || url[t]>'9')
1581 int page = atoi(&url[4]);
1582 if(page<0) page = 0;
1583 swfoutput_linktopage(dev, page, points);
1586 swfoutput_linktourl(dev, url, points);
1589 void swfoutput_linktourl(gfxdevice_t*dev, const char*url, gfxline_t*points)
1591 ActionTAG* actions = 0;
1592 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1598 /* TODO: escape special characters in url */
1600 if(i->config_externallinkfunction && i->config_flashversion<=8) {
1601 actions = action_PushString(actions, url); //parameter
1602 actions = action_PushInt(actions, 1); //number of parameters (1)
1603 actions = action_PushString(actions, i->config_externallinkfunction); //function name
1604 actions = action_CallFunction(actions);
1605 } else if(!i->config_linktarget) {
1606 if(!i->config_opennewwindow)
1607 actions = action_GetUrl(actions, url, "_parent");
1609 actions = action_GetUrl(actions, url, "_this");
1611 actions = action_GetUrl(actions, url, i->config_linktarget);
1613 actions = action_End(actions);
1615 drawlink(dev, actions, 0, points, 0, url);
1617 void swfoutput_linktopage(gfxdevice_t*dev, int page, gfxline_t*points)
1619 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1620 ActionTAG* actions = 0;
1627 if(!i->config_internallinkfunction || i->config_flashversion>=9) {
1628 actions = action_GotoFrame(actions, page-1);
1629 actions = action_End(actions);
1631 actions = action_PushInt(actions, page); //parameter
1632 actions = action_PushInt(actions, 1); //number of parameters (1)
1633 actions = action_PushString(actions, i->config_internallinkfunction); //function name
1634 actions = action_CallFunction(actions);
1635 actions = action_End(actions);
1639 sprintf(name, "page%d", page);
1641 drawlink(dev, actions, 0, points, 0, name);
1644 /* Named Links (a.k.a. Acrobatmenu) are used to implement various gadgets
1645 of the viewer objects, like subtitles, index elements etc.
1647 void swfoutput_namedlink(gfxdevice_t*dev, char*name, gfxline_t*points)
1649 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1650 ActionTAG *actions1,*actions2;
1651 char*tmp = strdup(name);
1659 if(!strncmp(tmp, "call:", 5))
1661 char*x = strchr(&tmp[5], ':');
1663 actions1 = action_PushInt(0, 0); //number of parameters (0)
1664 actions1 = action_PushString(actions1, &tmp[5]); //function name
1665 actions1 = action_CallFunction(actions1);
1666 actions1 = action_End(actions1);
1669 actions1 = action_PushString(0, x+1); //parameter
1670 actions1 = action_PushInt(actions1, 1); //number of parameters (1)
1671 actions1 = action_PushString(actions1, &tmp[5]); //function name
1672 actions1 = action_CallFunction(actions1);
1673 actions1 = action_End(actions1);
1675 actions2 = action_End(0);
1680 actions1 = action_PushString(0, "/:subtitle");
1681 actions1 = action_PushString(actions1, name);
1682 actions1 = action_SetVariable(actions1);
1683 actions1 = action_End(actions1);
1685 actions2 = action_PushString(0, "/:subtitle");
1686 actions2 = action_PushString(actions2, "");
1687 actions2 = action_SetVariable(actions2);
1688 actions2 = action_End(actions2);
1691 drawlink(dev, actions1, actions2, points, mouseover, name);
1693 swf_ActionFree(actions1);
1694 swf_ActionFree(actions2);
1698 static void drawgfxline(gfxdevice_t*dev, gfxline_t*line, int fill)
1700 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1701 gfxcoord_t lastx=0,lasty=0,px=0,py=0;
1703 int lines= 0, splines=0;
1710 /* check whether the next segment is zero */
1711 if(line->type == gfx_moveTo) {
1712 moveto(dev, i->tag, line->x, line->y);
1713 px = lastx = line->x;
1714 py = lasty = line->y;
1716 } if(line->type == gfx_lineTo) {
1717 lineto(dev, i->tag, line->x, line->y);
1722 } else if(line->type == gfx_splineTo) {
1724 s.x = line->sx;p.x = line->x;
1725 s.y = line->sy;p.y = line->y;
1726 splineto(dev, i->tag, s, p);
1734 msg("<trace> drawgfxline, %d lines, %d splines", lines, splines);
1738 static void drawlink(gfxdevice_t*dev, ActionTAG*actions1, ActionTAG*actions2, gfxline_t*points, char mouseover, const char*url)
1740 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1749 int buttonid = getNewID(dev);
1750 gfxbbox_t bbox = gfxline_getbbox(points);
1755 myshapeid = getNewID(dev);
1756 i->tag = swf_InsertTag(i->tag,ST_DEFINESHAPE3);
1757 swf_ShapeNew(&i->shape);
1758 rgb.r = rgb.b = rgb.a = rgb.g = 0;
1759 fsid = swf_ShapeAddSolidFillStyle(i->shape,&rgb);
1760 swf_SetU16(i->tag, myshapeid);
1761 r.xmin = (int)(bbox.xmin*20);
1762 r.ymin = (int)(bbox.ymin*20);
1763 r.xmax = (int)(bbox.xmax*20);
1764 r.ymax = (int)(bbox.ymax*20);
1765 r = swf_ClipRect(i->pagebbox, r);
1766 swf_SetRect(i->tag,&r);
1767 swf_SetShapeStyles(i->tag,i->shape);
1768 swf_ShapeCountBits(i->shape,NULL,NULL);
1769 swf_SetShapeBits(i->tag,i->shape);
1770 swf_ShapeSetAll(i->tag,i->shape,/*x*/0,/*y*/0,0,fsid,0);
1771 i->swflastx = i->swflasty = 0;
1772 drawgfxline(dev, points, 1);
1773 swf_ShapeSetEnd(i->tag);
1776 myshapeid2 = getNewID(dev);
1777 i->tag = swf_InsertTag(i->tag,ST_DEFINESHAPE3);
1778 swf_ShapeNew(&i->shape);
1780 rgb = i->config_linkcolor;
1782 fsid = swf_ShapeAddSolidFillStyle(i->shape,&rgb);
1783 swf_SetU16(i->tag, myshapeid2);
1784 r.xmin = (int)(bbox.xmin*20);
1785 r.ymin = (int)(bbox.ymin*20);
1786 r.xmax = (int)(bbox.xmax*20);
1787 r.ymax = (int)(bbox.ymax*20);
1788 r = swf_ClipRect(i->pagebbox, r);
1789 swf_SetRect(i->tag,&r);
1790 swf_SetShapeStyles(i->tag,i->shape);
1791 swf_ShapeCountBits(i->shape,NULL,NULL);
1792 swf_SetShapeBits(i->tag,i->shape);
1793 swf_ShapeSetAll(i->tag,i->shape,/*x*/0,/*y*/0,0,fsid,0);
1794 i->swflastx = i->swflasty = 0;
1795 drawgfxline(dev, points, 1);
1796 swf_ShapeSetEnd(i->tag);
1800 i->tag = swf_InsertTag(i->tag,ST_DEFINEBUTTON);
1801 swf_SetU16(i->tag,buttonid); //id
1802 swf_ButtonSetFlags(i->tag, 0); //menu=no
1803 swf_ButtonSetRecord(i->tag,0x01,myshapeid,i->depth,0,0);
1804 swf_ButtonSetRecord(i->tag,0x02,myshapeid2,i->depth,0,0);
1805 swf_ButtonSetRecord(i->tag,0x04,myshapeid2,i->depth,0,0);
1806 swf_ButtonSetRecord(i->tag,0x08,myshapeid,i->depth,0,0);
1807 swf_SetU8(i->tag,0);
1808 swf_ActionSet(i->tag,actions1);
1809 swf_SetU8(i->tag,0);
1813 i->tag = swf_InsertTag(i->tag,ST_DEFINEBUTTON2);
1814 swf_SetU16(i->tag,buttonid); //id
1815 swf_ButtonSetFlags(i->tag, 0); //menu=no
1816 swf_ButtonSetRecord(i->tag,0x01,myshapeid,i->depth,0,0);
1817 swf_ButtonSetRecord(i->tag,0x02,myshapeid2,i->depth,0,0);
1818 swf_ButtonSetRecord(i->tag,0x04,myshapeid2,i->depth,0,0);
1819 swf_ButtonSetRecord(i->tag,0x08,myshapeid,i->depth,0,0);
1820 swf_SetU8(i->tag,0); // end of button records
1821 swf_ButtonSetCondition(i->tag, BC_IDLE_OVERUP);
1822 swf_ActionSet(i->tag,actions1);
1824 swf_ButtonSetCondition(i->tag, BC_OVERUP_IDLE);
1825 swf_ActionSet(i->tag,actions2);
1826 swf_SetU8(i->tag,0);
1827 swf_ButtonPostProcess(i->tag, 2);
1829 swf_SetU8(i->tag,0);
1830 swf_ButtonPostProcess(i->tag, 1);
1834 const char* name = 0;
1835 if(i->config_linknameurl) {
1839 sprintf(buf, "button%d", buttonid);
1842 msg("<trace> Placing link ID %d", buttonid);
1843 i->tag = swf_InsertTag(i->tag,ST_PLACEOBJECT2);
1845 if(posx!=0 || posy!=0) {
1847 p.x = (int)(posx*20);
1848 p.y = (int)(posy*20);
1849 p = swf_TurnPoint(p, &i->page_matrix);
1854 swf_ObjectPlace(i->tag, buttonid, getNewDepth(dev),&m,0,name);
1856 swf_ObjectPlace(i->tag, buttonid, getNewDepth(dev),&i->page_matrix,0,name);
1863 for(t=0;t<picpos;t++)
1865 if(pic_xids[t] == xid &&
1866 pic_yids[t] == yid) {
1867 width = pic_width[t];
1868 height = pic_height[t];
1872 pic_ids[picpos] = swfoutput_drawimagelosslessN(&output, pic, pal, width, height, x1,y1,x2,y2,x3,y3,x4,y4, numpalette);
1873 pic_xids[picpos] = xid;
1874 pic_yids[picpos] = yid;
1875 pic_width[picpos] = width;
1876 pic_height[picpos] = height;
1879 pic[width*y+x] = buf[0];
1883 xid += pal[1].r*3 + pal[1].g*11 + pal[1].b*17;
1884 yid += pal[1].r*7 + pal[1].g*5 + pal[1].b*23;
1888 xid += x*r+x*b*3+x*g*7+x*a*11;
1889 yid += y*r*3+y*b*17+y*g*19+y*a*11;
1891 for(t=0;t<picpos;t++)
1893 if(pic_xids[t] == xid &&
1894 pic_yids[t] == yid) {
1903 int swf_setparameter(gfxdevice_t*dev, const char*name, const char*value)
1905 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1907 msg("<trace> swfdevice: %s=%s", name, value);
1908 if(!strcmp(name, "jpegsubpixels")) {
1909 i->config_jpegsubpixels = atof(value);
1910 } else if(!strcmp(name, "ppmsubpixels")) {
1911 i->config_ppmsubpixels = atof(value);
1912 } else if(!strcmp(name, "subpixels")) {
1913 i->config_ppmsubpixels = i->config_jpegsubpixels = atof(value);
1914 } else if(!strcmp(name, "drawonlyshapes")) {
1915 i->config_drawonlyshapes = atoi(value);
1916 } else if(!strcmp(name, "ignoredraworder")) {
1917 i->config_ignoredraworder = atoi(value);
1918 } else if(!strcmp(name, "mark")) {
1919 if(!value || !value[0]) {
1920 if(i->mark) free(i->mark);
1924 i->mark = strdup("...");
1925 for(t=0;t<3;t++) if(value[t]) i->mark[t] = value[t];
1927 } else if(!strcmp(name, "filloverlap")) {
1928 i->config_filloverlap = atoi(value);
1929 } else if(!strcmp(name, "linksopennewwindow")) {
1930 i->config_opennewwindow = atoi(value);
1931 } else if(!strcmp(name, "opennewwindow")) {
1932 i->config_opennewwindow = atoi(value);
1933 } else if(!strcmp(name, "storeallcharacters")) {
1934 i->config_storeallcharacters = atoi(value);
1935 } else if(!strcmp(name, "enablezlib")) {
1936 i->config_enablezlib = atoi(value);
1937 } else if(!strcmp(name, "bboxvars")) {
1938 i->config_bboxvars = atoi(value);
1939 } else if(!strcmp(name, "frameresets")) {
1940 i->config_frameresets = atoi(value);
1941 } else if(!strcmp(name, "showclipshapes")) {
1942 i->config_showclipshapes = atoi(value);
1943 } else if(!strcmp(name, "reordertags")) {
1944 i->config_reordertags = atoi(value);
1945 } else if(!strcmp(name, "internallinkfunction")) {
1946 i->config_internallinkfunction = strdup(value);
1947 } else if(!strcmp(name, "externallinkfunction")) {
1948 i->config_externallinkfunction = strdup(value);
1949 } else if(!strcmp(name, "linkfunction")) { //sets both internallinkfunction and externallinkfunction
1950 i->config_internallinkfunction = strdup(value);
1951 i->config_externallinkfunction = strdup(value);
1952 } else if(!strcmp(name, "disable_polygon_conversion")) {
1953 i->config_disable_polygon_conversion = atoi(value);
1954 } else if(!strcmp(name, "normalize_polygon_positions")) {
1955 i->config_normalize_polygon_positions = atoi(value);
1956 } else if(!strcmp(name, "wxwindowparams")) {
1957 i->config_watermark = atoi(value);
1958 } else if(!strcmp(name, "insertstop")) {
1959 i->config_insertstoptag = atoi(value);
1960 } else if(!strcmp(name, "protect")) {
1961 i->config_protect = atoi(value);
1962 if(i->config_protect && i->tag) {
1963 i->tag = swf_InsertTag(i->tag, ST_PROTECT);
1965 } else if(!strcmp(name, "flashversion")) {
1966 i->config_flashversion = atoi(value);
1968 i->swf->fileVersion = i->config_flashversion;
1970 } else if(!strcmp(name, "framerate")) {
1971 i->config_framerate = atof(value);
1973 i->swf->frameRate = i->config_framerate*0x100;
1975 } else if(!strcmp(name, "minlinewidth")) {
1976 i->config_minlinewidth = atof(value);
1977 } else if(!strcmp(name, "caplinewidth")) {
1978 i->config_caplinewidth = atof(value);
1979 } else if(!strcmp(name, "linktarget")) {
1980 i->config_linktarget = strdup(value);
1981 } else if(!strcmp(name, "dumpfonts")) {
1982 i->config_dumpfonts = atoi(value);
1983 } else if(!strcmp(name, "animate")) {
1984 i->config_animate = atoi(value);
1985 } else if(!strcmp(name, "disablelinks")) {
1986 i->config_disablelinks = atoi(value);
1987 } else if(!strcmp(name, "simpleviewer")) {
1988 i->config_simpleviewer = atoi(value);
1989 } else if(!strcmp(name, "next_bitmap_is_jpeg")) {
1991 } else if(!strcmp(name, "jpegquality")) {
1992 int val = atoi(value);
1994 if(val>101) val=101;
1995 i->config_jpegquality = val;
1996 } else if(!strcmp(name, "splinequality")) {
1997 int v = atoi(value);
1998 v = 500-(v*5); // 100% = 0.25 pixel, 0% = 25 pixel
2000 i->config_splinemaxerror = v;
2001 } else if(!strcmp(name, "fontquality")) {
2002 int v = atoi(value);
2003 v = 500-(v*5); // 100% = 0.25 pixel, 0% = 25 pixel
2005 i->config_fontsplinemaxerror = v;
2006 } else if(!strcmp(name, "linkcolor")) {
2007 if(strlen(value)!=8) {
2008 fprintf(stderr, "Unknown format for option 'linkcolor'. (%s <-> RRGGBBAA)\n", value);
2011 # define NIBBLE(s) (((s)>='0' && (s)<='9')?((s)-'0'):((s)&0x0f)+9)
2012 i->config_linkcolor.r = NIBBLE(value[0])<<4 | NIBBLE(value[1]);
2013 i->config_linkcolor.g = NIBBLE(value[2])<<4 | NIBBLE(value[3]);
2014 i->config_linkcolor.b = NIBBLE(value[4])<<4 | NIBBLE(value[5]);
2015 i->config_linkcolor.a = NIBBLE(value[6])<<4 | NIBBLE(value[7]);
2016 } else if(!strcmp(name, "help")) {
2017 printf("\nSWF layer options:\n");
2018 printf("jpegsubpixels=<pixels> resolution adjustment for jpeg images (same as jpegdpi, but in pixels)\n");
2019 printf("ppmsubpixels=<pixels resolution adjustment for lossless images (same as ppmdpi, but in pixels)\n");
2020 printf("subpixels=<pixels> shortcut for setting both jpegsubpixels and ppmsubpixels\n");
2021 printf("drawonlyshapes convert everything to shapes (currently broken)\n");
2022 printf("ignoredraworder allow to perform a few optimizations for creating smaller SWFs\n");
2023 printf("linksopennewwindow make links open a new browser window\n");
2024 printf("linktarget target window name of new links\n");
2025 printf("linkcolor=<color) color of links (format: RRGGBBAA)\n");
2026 printf("linknameurl Link buttons will be named like the URL they refer to (handy for iterating through links with actionscript)\n");
2027 printf("storeallcharacters don't reduce the fonts to used characters in the output file\n");
2028 printf("enablezlib switch on zlib compression (also done if flashversion>=7)\n");
2029 printf("bboxvars store the bounding box of the SWF file in actionscript variables\n");
2030 printf("reordertags=0/1 (default: 1) perform some tag optimizations\n");
2031 printf("internallinkfunction=<name> when the user clicks a internal link (to a different page) in the converted file, this actionscript function is called\n");
2032 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");
2033 printf("disable_polygon_conversion never convert strokes to polygons (will remove capstyles and joint styles)\n");
2034 printf("caplinewidth=<width> the minimum thichness a line needs to have so that capstyles become visible (and are converted)\n");
2035 printf("insertstop put an ActionScript \"STOP\" tag in every frame\n");
2036 printf("protect add a \"protect\" tag to the file, to prevent loading in the Flash editor\n");
2037 printf("flashversion=<version> the SWF fileversion (6)\n");
2038 printf("framerate=<fps> SWF framerate\n");
2039 printf("minlinewidth=<width> convert horizontal/vertical boxes smaller than this width to lines (0.05) \n");
2040 printf("simpleviewer Add next/previous buttons to the SWF\n");
2041 printf("animate insert a showframe tag after each placeobject (animate draw order of PDF files)\n");
2042 printf("jpegquality=<quality> set compression quality of jpeg images\n");
2043 printf("splinequality=<value> Set the quality of spline convertion to value (0-100, default: 100).\n");
2044 printf("disablelinks Disable links.\n");
2051 // --------------------------------------------------------------------
2053 static CXFORM gfxcxform_to_cxform(gfxcxform_t* c)
2056 swf_GetCXForm(0, &cx, 1);
2059 if(c->rg!=0 || c->rb!=0 || c->ra!=0 ||
2060 c->gr!=0 || c->gb!=0 || c->ga!=0 ||
2061 c->br!=0 || c->bg!=0 || c->ba!=0 ||
2062 c->ar!=0 || c->ag!=0 || c->ab!=0)
2063 msg("<warning> CXForm not SWF-compatible");
2065 cx.a0 = (S16)(c->aa*256);
2066 cx.r0 = (S16)(c->rr*256);
2067 cx.g0 = (S16)(c->gg*256);
2068 cx.b0 = (S16)(c->bb*256);
2077 static int imageInCache(gfxdevice_t*dev, void*data, int width, int height)
2081 static void addImageToCache(gfxdevice_t*dev, void*data, int width, int height)
2085 static int add_image(swfoutput_internal*i, gfximage_t*img, int targetwidth, int targetheight, int* newwidth, int* newheight)
2087 gfxdevice_t*dev = i->dev;
2089 RGBA*mem = (RGBA*)img->data;
2091 int sizex = img->width;
2092 int sizey = img->height;
2093 int is_jpeg = i->jpeg;
2096 int newsizex=sizex, newsizey=sizey;
2099 if(is_jpeg && i->config_jpegsubpixels) {
2100 newsizex = (int)(targetwidth*i->config_jpegsubpixels + 0.5);
2101 newsizey = (int)(targetheight*i->config_jpegsubpixels + 0.5);
2102 } else if(!is_jpeg && i->config_ppmsubpixels) {
2103 newsizex = (int)(targetwidth*i->config_ppmsubpixels + 0.5);
2104 newsizey = (int)(targetheight*i->config_ppmsubpixels + 0.5);
2108 if(sizex<=0 || sizey<=0)
2115 /* TODO: cache images */
2117 if(newsizex<sizex || newsizey<sizey) {
2118 msg("<verbose> Scaling %dx%d image to %dx%d", sizex, sizey, newsizex, newsizey);
2119 newpic = swf_ImageScale(mem, sizex, sizey, newsizex, newsizey);
2120 *newwidth = sizex = newsizex;
2121 *newheight = sizey = newsizey;
2124 *newwidth = newsizex = sizex;
2125 *newheight = newsizey = sizey;
2128 int num_colors = swf_ImageGetNumberOfPaletteEntries(mem,sizex,sizey,0);
2129 int has_alpha = swf_ImageHasAlpha(mem,sizex,sizey);
2131 msg("<verbose> Drawing %dx%d %s%simage (id %d) at size %dx%d (%dx%d), %s%d colors",
2133 has_alpha?(has_alpha==2?"semi-transparent ":"transparent "):"",
2134 is_jpeg?"jpeg-":"", i->currentswfid+1,
2136 targetwidth, targetheight,
2137 /*newsizex, newsizey,*/
2138 num_colors>256?">":"", num_colors>256?256:num_colors);
2140 /*RGBA* pal = (RGBA*)rfx_alloc(sizeof(RGBA)*num_colors);
2141 swf_ImageGetNumberOfPaletteEntries(mem,sizex,sizey,pal);
2143 for(t=0;t<num_colors;t++) {
2144 printf("%02x%02x%02x%02x ",
2145 pal[t].r, pal[t].g, pal[t].b, pal[t].a);
2152 int cacheid = imageInCache(dev, mem, sizex, sizey);
2155 bitid = getNewID(dev);
2157 i->tag = swf_AddImage(i->tag, bitid, mem, sizex, sizey, i->config_jpegquality);
2158 addImageToCache(dev, mem, sizex, sizey);
2168 static SRECT gfxline_getSWFbbox(gfxline_t*line)
2170 gfxbbox_t bbox = gfxline_getbbox(line);
2172 r.xmin = (int)(bbox.xmin*20);
2173 r.ymin = (int)(bbox.ymin*20);
2174 r.xmax = (int)(bbox.xmax*20);
2175 r.ymax = (int)(bbox.ymax*20);
2179 int line_is_empty(gfxline_t*line)
2182 if(line->type != gfx_moveTo)
2189 static void swf_fillbitmap(gfxdevice_t*dev, gfxline_t*line, gfximage_t*img, gfxmatrix_t*matrix, gfxcxform_t*cxform)
2191 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
2193 if(line_is_empty(line))
2199 int targetx = (int)(sqrt(matrix->m00*matrix->m00 + matrix->m01*matrix->m01)*img->width);
2200 int targety = (int)(sqrt(matrix->m10*matrix->m10 + matrix->m11*matrix->m11)*img->height);
2202 int newwidth=0,newheight=0;
2203 int bitid = add_image(i, img, targetx, targety, &newwidth, &newheight);
2206 double fx = (double)img->width / (double)newwidth;
2207 double fy = (double)img->height / (double)newheight;
2210 m.sx = (int)(65536*20*matrix->m00*fx); m.r1 = (int)(65536*20*matrix->m10*fy);
2211 m.r0 = (int)(65536*20*matrix->m01*fx); m.sy = (int)(65536*20*matrix->m11*fy);
2212 m.tx = (int)(matrix->tx*20);
2213 m.ty = (int)(matrix->ty*20);
2216 int myshapeid = getNewID(dev);
2217 i->tag = swf_InsertTag(i->tag,ST_DEFINESHAPE);
2219 swf_ShapeNew(&shape);
2220 int fsid = swf_ShapeAddBitmapFillStyle(shape,&m,bitid,1);
2221 swf_SetU16(i->tag, myshapeid);
2222 SRECT r = gfxline_getSWFbbox(line);
2223 r = swf_ClipRect(i->pagebbox, r);
2224 swf_SetRect(i->tag,&r);
2225 swf_SetShapeStyles(i->tag,shape);
2226 swf_ShapeCountBits(shape,NULL,NULL);
2227 swf_SetShapeBits(i->tag,shape);
2228 swf_ShapeSetAll(i->tag,shape,UNDEFINED_COORD,UNDEFINED_COORD,0,fsid,0);
2229 i->swflastx = i->swflasty = UNDEFINED_COORD;
2230 drawgfxline(dev, line, 1);
2231 swf_ShapeSetEnd(i->tag);
2232 swf_ShapeFree(shape);
2234 msg("<trace> Placing image, shape ID %d, bitmap ID %d", myshapeid, bitid);
2235 i->tag = swf_InsertTag(i->tag,ST_PLACEOBJECT2);
2236 CXFORM cxform2 = gfxcxform_to_cxform(cxform);
2237 swf_ObjectPlace(i->tag,myshapeid,getNewDepth(dev),&i->page_matrix,&cxform2,NULL);
2240 static RGBA col_black = {255,0,0,0};
2242 static void drawoutline(gfxdevice_t*dev, gfxline_t*line)
2244 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
2246 int myshapeid = getNewID(dev);
2247 i->tag = swf_InsertTag(i->tag,ST_DEFINESHAPE3);
2250 swf_ShapeNew(&shape);
2251 int lsid = swf_ShapeAddLineStyle(shape,1,&col_black);
2253 swf_SetU16(i->tag,myshapeid);
2254 SRECT r = gfxline_getSWFbbox(line);
2255 r = swf_ClipRect(i->pagebbox, r);
2256 swf_SetRect(i->tag,&r);
2257 swf_SetShapeStyles(i->tag,shape);
2258 swf_ShapeCountBits(shape,NULL,NULL);
2259 swf_SetShapeBits(i->tag,shape);
2260 swf_ShapeSetAll(i->tag,shape,UNDEFINED_COORD,UNDEFINED_COORD,lsid,0,0);
2261 drawgfxline(dev, line, 1);
2262 swf_ShapeSetEnd(i->tag);
2263 swf_ShapeFree(shape);
2265 i->tag = swf_InsertTag(i->tag,ST_PLACEOBJECT2);
2266 swf_ObjectPlace(i->tag, myshapeid, getNewDepth(dev), 0,0,0);
2269 static void swf_startclip(gfxdevice_t*dev, gfxline_t*line)
2271 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
2276 if(i->clippos >= 127)
2278 msg("<warning> Too many clip levels.");
2282 if(i->config_showclipshapes)
2283 drawoutline(dev, line);
2285 int myshapeid = getNewID(dev);
2286 i->tag = swf_InsertTag(i->tag,ST_DEFINESHAPE3);
2288 memset(&col, 0, sizeof(RGBA));
2291 swf_ShapeNew(&shape);
2292 int fsid = swf_ShapeAddSolidFillStyle(shape,&col);
2294 RGBA markcol = {0,i->mark[0],i->mark[1],i->mark[2]};
2295 swf_ShapeAddSolidFillStyle(shape,&markcol);
2297 swf_SetU16(i->tag,myshapeid);
2298 SRECT r = gfxline_getSWFbbox(line);
2299 r = swf_ClipRect(i->pagebbox, r);
2300 swf_SetRect(i->tag,&r);
2301 swf_SetShapeStyles(i->tag,shape);
2302 swf_ShapeCountBits(shape,NULL,NULL);
2303 swf_SetShapeBits(i->tag,shape);
2304 swf_ShapeSetAll(i->tag,shape,UNDEFINED_COORD,UNDEFINED_COORD,0,fsid,0);
2305 i->swflastx = i->swflasty = UNDEFINED_COORD;
2306 i->shapeisempty = 1;
2307 drawgfxline(dev, line, 1);
2308 if(i->shapeisempty) {
2309 /* an empty clip shape is equivalent to a shape with no area */
2310 int x = line?line->x:0;
2311 int y = line?line->y:0;
2312 moveto(dev, i->tag, x,y);
2313 lineto(dev, i->tag, x,y);
2314 lineto(dev, i->tag, x,y);
2316 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)) {
2317 if(i->config_watermark) {
2318 gfxbbox_t r; r.xmin = r.ymin = 0;r.xmax = i->max_x;r.ymax = i->max_y;
2319 draw_watermark(dev, r, 1);
2322 swf_ShapeSetEnd(i->tag);
2323 swf_ShapeFree(shape);
2325 /* TODO: remember the bbox, and check all shapes against it */
2327 msg("<trace> Placing clip ID %d", myshapeid);
2328 i->tag = swf_InsertTag(i->tag,ST_PLACEOBJECT2);
2329 i->cliptags[i->clippos] = i->tag;
2330 i->clipshapes[i->clippos] = myshapeid;
2331 i->clipdepths[i->clippos] = getNewDepth(dev);
2335 static void swf_endclip(gfxdevice_t*dev)
2337 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
2344 msg("<error> Invalid end of clipping region");
2348 /*swf_ObjectPlaceClip(i->cliptags[i->clippos],i->clipshapes[i->clippos],i->clipdepths[i->clippos],&i->page_matrix,NULL,NULL,
2349 / * clip to depth: * / i->depth <= i->clipdepths[i->clippos]? i->depth : i->depth - 1);
2351 swf_ObjectPlaceClip(i->cliptags[i->clippos],i->clipshapes[i->clippos],i->clipdepths[i->clippos],&i->page_matrix,NULL,NULL,i->depth);
2353 static int gfxline_type(gfxline_t*line)
2359 int haszerosegments=0;
2362 if(line->type == gfx_moveTo) {
2365 } else if(line->type == gfx_lineTo) {
2369 } else if(line->type == gfx_splineTo) {
2371 if(tmpsplines>lines)
2379 if(lines==0 && splines==0) return 0;
2380 else if(lines==1 && splines==0) return 1;
2381 else if(lines==0 && splines==1) return 2;
2382 else if(splines==0) return 3;
2386 static int gfxline_has_dots(gfxline_t*line)
2394 if(line->type == gfx_moveTo) {
2395 /* test the length of the preceding line, and assume it is a dot if
2396 it's length is less than 1.0. But *only* if there's a noticable
2397 gap between the previous line and the next moveTo. (I've come
2398 across a PDF where thousands of "dots" were stringed together,
2400 int last_short_gap = short_gap;
2401 if((fabs(line->x - x) + fabs(line->y - y)) < 1.0) {
2406 if(isline && dist < 1 && !short_gap && !last_short_gap) {
2411 } else if(line->type == gfx_lineTo) {
2412 dist += fabs(line->x - x) + fabs(line->y - y);
2414 } else if(line->type == gfx_splineTo) {
2415 dist += fabs(line->sx - x) + fabs(line->sy - y) +
2416 fabs(line->x - line->sx) + fabs(line->y - line->sy);
2423 if(isline && dist < 1 && !short_gap) {
2429 static int gfxline_fix_short_edges(gfxline_t*line)
2433 if(line->type == gfx_lineTo) {
2434 if(fabs(line->x - x) + fabs(line->y - y) < 0.01) {
2437 } else if(line->type == gfx_splineTo) {
2438 if(fabs(line->sx - x) + fabs(line->sy - y) +
2439 fabs(line->x - line->sx) + fabs(line->y - line->sy) < 0.01) {
2450 static char is_inside_page(gfxdevice_t*dev, gfxcoord_t x, gfxcoord_t y)
2452 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
2453 if(x<i->min_x || x>i->max_x) return 0;
2454 if(y<i->min_y || y>i->max_y) return 0;
2458 gfxline_t* gfxline_move(gfxline_t*line, double x, double y)
2460 gfxline_t*l = line = gfxline_clone(line);
2472 //#define NORMALIZE_POLYGON_POSITIONS
2474 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)
2476 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
2477 if(line_is_empty(line))
2479 int type = gfxline_type(line);
2480 int has_dots = gfxline_has_dots(line);
2481 gfxbbox_t r = gfxline_getbbox(line);
2482 int is_outside_page = !is_inside_page(dev, r.xmin, r.ymin) || !is_inside_page(dev, r.xmax, r.ymax);
2484 /* TODO: * split line into segments, and perform this check for all segments */
2486 if(i->config_disable_polygon_conversion || type>=5 ||
2488 (width <= i->config_caplinewidth
2489 || (cap_style == gfx_capRound && joint_style == gfx_joinRound)
2490 || (cap_style == gfx_capRound && type<=2)))) {} else
2492 /* convert line to polygon */
2493 msg("<trace> draw as polygon, type=%d dots=%d", type, has_dots);
2495 gfxline_fix_short_edges(line);
2496 /* we need to convert the line into a polygon */
2497 gfxpoly_t* poly = gfxpoly_strokeToPoly(line, width, cap_style, joint_style, miterLimit);
2498 gfxline_t*gfxline = gfxpoly_to_gfxline(poly);
2499 dev->fill(dev, gfxline, color);
2500 gfxline_free(gfxline);
2505 msg("<trace> draw as stroke, type=%d dots=%d", type, has_dots);
2508 if(i->config_normalize_polygon_positions) {
2510 double startx = 0, starty = 0;
2511 if(line && line->type == gfx_moveTo) {
2515 line = gfxline_move(line, -startx, -starty);
2516 i->shapeposx = (int)(startx*20);
2517 i->shapeposy = (int)(starty*20);
2520 swfoutput_setstrokecolor(dev, color->r, color->g, color->b, color->a);
2521 swfoutput_setlinewidth(dev, width);
2524 drawgfxline(dev, line, 0);
2526 if(i->config_normalize_polygon_positions) {
2527 free(line); //account for _move
2532 static void swf_fill(gfxdevice_t*dev, gfxline_t*line, gfxcolor_t*color)
2534 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
2535 if(line_is_empty(line))
2539 gfxbbox_t r = gfxline_getbbox(line);
2540 int is_outside_page = !is_inside_page(dev, r.xmin, r.ymin) || !is_inside_page(dev, r.xmax, r.ymax);
2542 //if(i->chardatapos && i->chardata[i->chardatapos-1].color.a) {
2545 if(!i->config_ignoredraworder)
2548 if(i->config_normalize_polygon_positions) {
2550 double startx = 0, starty = 0;
2551 if(line && line->type == gfx_moveTo) {
2555 line = gfxline_move(line, -startx, -starty);
2556 i->shapeposx = (int)(startx*20);
2557 i->shapeposy = (int)(starty*20);
2560 swfoutput_setfillcolor(dev, color->r, color->g, color->b, color->a);
2563 drawgfxline(dev, line, 1);
2565 if(i->currentswfid==2 && r.xmin==0 && r.ymin==0 && r.xmax==i->max_x && r.ymax==i->max_y) {
2566 if(i->config_watermark) {
2567 draw_watermark(dev, r, 1);
2571 msg("<trace> end of swf_fill (shapeid=%d)", i->shapeid);
2573 if(i->config_normalize_polygon_positions) {
2574 free(line); //account for _move
2578 static GRADIENT* gfxgradient_to_GRADIENT(gfxgradient_t*gradient)
2581 gfxgradient_t*g = gradient;
2586 GRADIENT* swfgradient = malloc(sizeof(GRADIENT));
2587 swfgradient->num = num;
2588 swfgradient->rgba = malloc(sizeof(swfgradient->rgba[0])*num);
2589 swfgradient->ratios = malloc(sizeof(swfgradient->ratios[0])*num);
2594 swfgradient->ratios[num] = g->pos*255;
2595 swfgradient->rgba[num] = *(RGBA*)&g->color;
2602 static void swf_fillgradient(gfxdevice_t*dev, gfxline_t*line, gfxgradient_t*gradient, gfxgradienttype_t type, gfxmatrix_t*matrix)
2604 if(line_is_empty(line))
2606 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
2608 if(line_is_empty(line))
2611 GRADIENT* swfgradient = gfxgradient_to_GRADIENT(gradient);
2618 double f = type==gfxgradient_radial?4:4;
2620 m.sx = (int)(matrix->m00*20*f); m.r1 = (int)(matrix->m10*20*f);
2621 m.r0 = (int)(matrix->m01*20*f); m.sy = (int)(matrix->m11*20*f);
2622 m.tx = (int)(matrix->tx*20);
2623 m.ty = (int)(matrix->ty*20);
2626 int myshapeid = getNewID(dev);
2627 i->tag = swf_InsertTag(i->tag, ST_DEFINESHAPE2);
2629 swf_ShapeNew(&shape);
2630 int fsid = swf_ShapeAddGradientFillStyle(shape,&m,swfgradient,type==gfxgradient_radial);
2631 swf_SetU16(i->tag, myshapeid);
2632 SRECT r = gfxline_getSWFbbox(line);
2633 r = swf_ClipRect(i->pagebbox, r);
2634 swf_SetRect(i->tag,&r);
2635 swf_SetShapeStyles(i->tag,shape);
2636 swf_ShapeCountBits(shape,NULL,NULL);
2637 swf_SetShapeBits(i->tag,shape);
2638 swf_ShapeSetAll(i->tag,shape,UNDEFINED_COORD,UNDEFINED_COORD,0,fsid,0);
2639 i->swflastx = i->swflasty = UNDEFINED_COORD;
2640 drawgfxline(dev, line, 1);
2641 swf_ShapeSetEnd(i->tag);
2642 swf_ShapeFree(shape);
2644 int depth = getNewDepth(dev);
2645 msg("<trace> Placing gradient, shape ID %d, depth %d", myshapeid, depth);
2646 i->tag = swf_InsertTag(i->tag,ST_PLACEOBJECT2);
2647 swf_ObjectPlace(i->tag,myshapeid,depth,&i->page_matrix,NULL,NULL);
2649 swf_FreeGradient(swfgradient);free(swfgradient);
2652 static SWFFONT* gfxfont_to_swffont(gfxfont_t*font, const char* id)
2654 SWFFONT*swffont = (SWFFONT*)rfx_calloc(sizeof(SWFFONT));
2656 SRECT bounds = {0,0,0,0};
2658 swffont->version = 2;
2659 swffont->name = (U8*)strdup(id);
2660 swffont->layout = (SWFLAYOUT*)rfx_calloc(sizeof(SWFLAYOUT));
2661 swffont->layout->ascent = 0;
2662 swffont->layout->descent = 0;
2663 swffont->layout->leading = 0;
2664 swffont->layout->bounds = (SRECT*)rfx_calloc(sizeof(SRECT)*font->num_glyphs);
2665 swffont->encoding = FONT_ENCODING_UNICODE;
2666 swffont->numchars = font->num_glyphs;
2667 swffont->maxascii = font->max_unicode;
2668 swffont->ascii2glyph = (int*)rfx_calloc(sizeof(int)*swffont->maxascii);
2669 swffont->glyph2ascii = (U16*)rfx_calloc(sizeof(U16)*swffont->numchars);
2670 swffont->glyph = (SWFGLYPH*)rfx_calloc(sizeof(SWFGLYPH)*swffont->numchars);
2671 swffont->glyphnames = (char**)rfx_calloc(sizeof(char*)*swffont->numchars);
2672 for(t=0;t<font->max_unicode;t++) {
2673 swffont->ascii2glyph[t] = font->unicode2glyph[t];
2675 SRECT max = {0,0,0,0};
2676 for(t=0;t<font->num_glyphs;t++) {
2680 swffont->glyph2ascii[t] = font->glyphs[t].unicode;
2681 if(swffont->glyph2ascii[t] == 0xffff || swffont->glyph2ascii[t] == 0x0000) {
2682 /* flash 8 flashtype requires unique unicode IDs for each character.
2683 We use the Unicode private user area to assign characters, hoping that
2684 the font doesn't contain more than 2048 glyphs */
2685 swffont->glyph2ascii[t] = 0xe000 + (t&0x1fff);
2688 if(font->glyphs[t].name) {
2689 swffont->glyphnames[t] = strdup(font->glyphs[t].name);
2691 swffont->glyphnames[t] = 0;
2693 advance = font->glyphs[t].advance;
2695 swf_Shape01DrawerInit(&draw, 0);
2696 line = font->glyphs[t].line;
2699 c.x = line->sx * GLYPH_SCALE; c.y = line->sy * GLYPH_SCALE;
2700 to.x = line->x * GLYPH_SCALE; to.y = line->y * GLYPH_SCALE;
2701 if(line->type == gfx_moveTo) {
2702 draw.moveTo(&draw, &to);
2703 } else if(line->type == gfx_lineTo) {
2704 draw.lineTo(&draw, &to);
2705 } else if(line->type == gfx_splineTo) {
2706 draw.splineTo(&draw, &c, &to);
2711 swffont->glyph[t].shape = swf_ShapeDrawerToShape(&draw);
2713 SRECT bbox = swf_ShapeDrawerGetBBox(&draw);
2714 swf_ExpandRect2(&max, &bbox);
2716 swffont->layout->bounds[t] = bbox;
2718 if(advance<32768.0/20) {
2719 swffont->glyph[t].advance = (int)(advance*20);
2721 //msg("<warning> Advance value overflow in glyph %d", t);
2722 swffont->glyph[t].advance = 32767;
2725 draw.dealloc(&draw);
2727 swf_ExpandRect2(&bounds, &swffont->layout->bounds[t]);
2729 for(t=0;t<font->num_glyphs;t++) {
2730 SRECT bbox = swffont->layout->bounds[t];
2732 /* if the glyph doesn't have a bounding box, use the
2733 combined bounding box (necessary e.g. for space characters) */
2734 if(!(bbox.xmin|bbox.ymin|bbox.xmax|bbox.ymax)) {
2735 swffont->layout->bounds[t] = bbox = max;
2738 /* check that the advance value is reasonable, by comparing it
2739 with the bounding box */
2740 if(bbox.xmax>0 && (bbox.xmax*10 < swffont->glyph[t].advance || !swffont->glyph[t].advance)) {
2741 if(swffont->glyph[t].advance)
2742 msg("<warning> fix bad advance value for char %d: bbox=%.2f, advance=%.2f\n", t, bbox.xmax/20.0, swffont->glyph[t].advance/20.0);
2743 swffont->glyph[t].advance = bbox.xmax;
2745 //swffont->glyph[t].advance = bbox.xmax - bbox.xmin;
2749 /* Flash player will use the advance value from the char, and the ascent/descent values
2750 from the layout for text selection.
2751 ascent will extend the char into negative y direction, from the baseline, while descent
2752 will extend in positive y direction, also from the baseline.
2753 The baseline is defined as the y-position zero
2756 swffont->layout->ascent = -bounds.ymin;
2757 if(swffont->layout->ascent < 0)
2758 swffont->layout->ascent = 0;
2759 swffont->layout->descent = bounds.ymax;
2760 if(swffont->layout->descent < 0)
2761 swffont->layout->descent = 0;
2762 swffont->layout->leading = bounds.ymax - bounds.ymin;
2764 /* if the font has proper ascent/descent values (>0) and those define
2765 greater line spacing that what we estimated from the bounding boxes,
2766 use the font's parameters */
2767 if(font->ascent*20 > swffont->layout->ascent)
2768 swffont->layout->ascent = font->ascent*20;
2769 if(font->descent*20 > swffont->layout->descent)
2770 swffont->layout->descent = font->descent*20;
2775 static void swf_addfont(gfxdevice_t*dev, gfxfont_t*font)
2777 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
2779 if(i->swffont && i->swffont->name && !strcmp((char*)i->swffont->name,font->id))
2780 return; // the requested font is the current font
2782 fontlist_t*last=0,*l = i->fontlist;
2785 if(!strcmp((char*)l->swffont->name, font->id)) {
2786 return; // we already know this font
2790 l = (fontlist_t*)rfx_calloc(sizeof(fontlist_t));
2791 l->swffont = gfxfont_to_swffont(font, font->id);
2798 swf_FontSetID(l->swffont, getNewID(i->dev));
2800 if(getScreenLogLevel() >= LOGLEVEL_DEBUG) {
2802 // print font information
2803 msg("<debug> Font %s",font->id);
2804 msg("<debug> | ID: %d", l->swffont->id);
2805 msg("<debug> | Version: %d", l->swffont->version);
2806 msg("<debug> | Name: %s", l->swffont->name);
2807 msg("<debug> | Numchars: %d", l->swffont->numchars);
2808 msg("<debug> | Maxascii: %d", l->swffont->maxascii);
2809 msg("<debug> | Style: %d", l->swffont->style);
2810 msg("<debug> | Encoding: %d", l->swffont->encoding);
2811 for(iii=0; iii<l->swffont->numchars;iii++) {
2812 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,
2813 l->swffont->layout->bounds[iii].xmin/20.0,
2814 l->swffont->layout->bounds[iii].ymin/20.0,
2815 l->swffont->layout->bounds[iii].xmax/20.0,
2816 l->swffont->layout->bounds[iii].ymax/20.0
2819 for(t=0;t<l->swffont->maxascii;t++) {
2820 if(l->swffont->ascii2glyph[t] == iii)
2821 msg("<debug> | - maps to %d",t);
2827 static void swf_switchfont(gfxdevice_t*dev, const char*fontid)
2829 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
2831 if(i->swffont && i->swffont->name && !strcmp((char*)i->swffont->name,fontid))
2832 return; // the requested font is the current font
2834 fontlist_t*l = i->fontlist;
2836 if(!strcmp((char*)l->swffont->name, fontid)) {
2837 i->swffont = l->swffont;
2842 msg("<error> Unknown font id: %s", fontid);
2846 static void swf_drawchar(gfxdevice_t*dev, gfxfont_t*font, int glyph, gfxcolor_t*color, gfxmatrix_t*matrix)
2848 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
2850 msg("<error> swf_drawchar called (glyph %d) without font", glyph);
2854 if(i->config_drawonlyshapes) {
2855 gfxglyph_t*g = &font->glyphs[glyph];
2856 gfxline_t*line2 = gfxline_clone(g->line);
2857 gfxline_transform(line2, matrix);
2858 dev->fill(dev, line2, color);
2859 gfxline_free(line2);
2863 if(!i->swffont || !i->swffont->name || strcmp((char*)i->swffont->name,font->id)) // not equal to current font
2865 /* TODO: remove the need for this (enhance getcharacterbbox so that it can cope
2866 with multiple fonts */
2868 swf_switchfont(dev, font->id); // set the current font
2871 msg("<warning> swf_drawchar: Font is NULL");
2874 if(glyph<0 || glyph>=i->swffont->numchars) {
2875 msg("<warning> No character %d in font %s (%d chars)", glyph, FIXNULL((char*)i->swffont->name), i->swffont->numchars);
2879 setfontscale(dev, matrix->m00, matrix->m01, matrix->m10, matrix->m11, matrix->tx, matrix->ty, 0);
2881 double det = i->fontmatrix.sx/65536.0 * i->fontmatrix.sy/65536.0 -
2882 i->fontmatrix.r0/65536.0 * i->fontmatrix.r1/65536.0;
2883 if(fabs(det) < 0.0005) {
2884 /* x direction equals y direction- the text is invisible */
2885 msg("<verbose> Not drawing invisible character character %d (det=%f, m=[%f %f;%f %f]\n", glyph,
2887 i->fontmatrix.sx/65536.0, i->fontmatrix.r1/65536.0,
2888 i->fontmatrix.r0/65536.0, i->fontmatrix.sy/65536.0);
2892 /*if(i->swffont->glyph[glyph].shape->bitlen <= 16) {
2893 msg("<warning> Glyph %d in current charset (%s, %d characters) is empty",
2894 glyph, FIXNULL((char*)i->swffont->name), i->swffont->numchars);
2898 /* calculate character position with respect to the current font matrix */
2899 double s = 20 * GLYPH_SCALE / det;
2900 double px = matrix->tx - i->fontmatrix.tx/20.0;
2901 double py = matrix->ty - i->fontmatrix.ty/20.0;
2902 int x = (SCOORD)(( px * i->fontmatrix.sy/65536.0 - py * i->fontmatrix.r1/65536.0)*s);
2903 int y = (SCOORD)((- px * i->fontmatrix.r0/65536.0 + py * i->fontmatrix.sx/65536.0)*s);
2904 if(x>32767 || x<-32768 || y>32767 || y<-32768) {
2905 msg("<verbose> Moving character origin to %f %f\n", matrix->tx, matrix->ty);
2907 setfontscale(dev, matrix->m00, matrix->m01, matrix->m10, matrix->m11, matrix->tx, matrix->ty, 1);
2908 /* since we just moved the char origin to the current char's position,
2909 it now has the relative position (0,0) */
2918 msg("<trace> Drawing char %d in font %d at %d,%d in color %02x%02x%02x%02x",
2919 glyph, i->swffont->id, x, y, color->r, color->g, color->b, color->a);
2921 putcharacter(dev, i->swffont->id, glyph, x, y, i->current_font_size, *(RGBA*)color);
2922 swf_FontUseGlyph(i->swffont, glyph);