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 1024
51 typedef struct _charatposition {
60 typedef struct _chararray {
61 charatposition_t chr[CHARDATAMAX+1];
63 struct _chararray *next;
66 typedef struct _charbuffer {
70 struct _charbuffer *next;
73 typedef struct _fontlist
76 struct _fontlist*next;
79 typedef long int twip;
81 typedef struct _swfmatrix {
82 double m11,m12,m21,m22,m31,m32;
85 typedef struct _swfoutput_internal
87 gfxdevice_t*dev; // the gfxdevice object where this internal struct resides
89 double config_dumpfonts;
90 double config_ppmsubpixels;
91 double config_jpegsubpixels;
93 int config_invisibletexttofront;
95 int config_simpleviewer;
96 int config_opennewwindow;
97 int config_ignoredraworder;
98 int config_drawonlyshapes;
99 int config_frameresets;
100 int config_linknameurl;
101 int config_jpegquality;
102 int config_storeallcharacters;
103 int config_enablezlib;
104 int config_insertstoptag;
105 int config_watermark;
107 int config_flashversion;
108 int config_reordertags;
109 int config_showclipshapes;
110 int config_splinemaxerror;
111 int config_fontsplinemaxerror;
112 int config_filloverlap;
115 int config_disable_polygon_conversion;
116 int config_normalize_polygon_positions;
117 char config_disablelinks;
118 RGBA config_linkcolor;
119 float config_minlinewidth;
120 double config_caplinewidth;
121 char* config_linktarget;
122 char*config_internallinkfunction;
123 char*config_externallinkfunction;
125 double config_framerate;
129 fontlist_t* fontlist;
168 int pic_height[1024];
175 char fillstylechanged;
177 int jpeg; //next image type
184 charbuffer_t* chardata;
185 charbuffer_t* topchardata; //chars supposed to be above everything else
192 int current_font_size;
194 double lastfontm11,lastfontm12,lastfontm21,lastfontm22;
205 } swfoutput_internal;
207 static void swf_fillbitmap(gfxdevice_t*driver, gfxline_t*line, gfximage_t*img, gfxmatrix_t*move, gfxcxform_t*cxform);
208 static int swf_setparameter(gfxdevice_t*driver, const char*key, const char*value);
209 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);
210 static void swf_startclip(gfxdevice_t*dev, gfxline_t*line);
211 static void swf_endclip(gfxdevice_t*dev);
212 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);
213 static void swf_fill(gfxdevice_t*dev, gfxline_t*line, gfxcolor_t*color);
214 static void swf_fillbitmap(gfxdevice_t*dev, gfxline_t*line, gfximage_t*img, gfxmatrix_t*matrix, gfxcxform_t*cxform);
215 static void swf_fillgradient(gfxdevice_t*dev, gfxline_t*line, gfxgradient_t*gradient, gfxgradienttype_t type, gfxmatrix_t*matrix);
216 static void swf_drawchar(gfxdevice_t*dev, gfxfont_t*font, int glyph, gfxcolor_t*color, gfxmatrix_t*matrix);
217 static void swf_addfont(gfxdevice_t*dev, gfxfont_t*font);
218 static void swf_drawlink(gfxdevice_t*dev, gfxline_t*line, const char*action);
219 static void swf_startframe(gfxdevice_t*dev, int width, int height);
220 static void swf_endframe(gfxdevice_t*dev);
221 static gfxresult_t* swf_finish(gfxdevice_t*driver);
223 static swfoutput_internal* init_internal_struct()
225 swfoutput_internal*i = (swfoutput_internal*)malloc(sizeof(swfoutput_internal));
226 memset(i, 0, sizeof(swfoutput_internal));
250 i->fillstylechanged = 0;
257 i->config_disablelinks=0;
258 i->config_dumpfonts=0;
259 i->config_ppmsubpixels=0;
260 i->config_jpegsubpixels=0;
261 i->config_opennewwindow=1;
262 i->config_ignoredraworder=0;
263 i->config_drawonlyshapes=0;
264 i->config_jpegquality=85;
265 i->config_storeallcharacters=0;
267 i->config_enablezlib=0;
268 i->config_insertstoptag=0;
269 i->config_flashversion=6;
270 i->config_framerate=0.25;
271 i->config_splinemaxerror=1;
272 i->config_fontsplinemaxerror=1;
273 i->config_filloverlap=0;
275 i->config_bboxvars=0;
276 i->config_showclipshapes=0;
277 i->config_minlinewidth=0.05;
278 i->config_caplinewidth=1;
279 i->config_linktarget=0;
280 i->config_internallinkfunction=0;
281 i->config_externallinkfunction=0;
282 i->config_reordertags=1;
283 i->config_linknameurl=0;
285 i->config_linkcolor.r = i->config_linkcolor.g = i->config_linkcolor.b = 255;
286 i->config_linkcolor.a = 0x40;
291 static int id_error = 0;
293 static U16 getNewID(gfxdevice_t* dev)
295 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
296 if(i->currentswfid == 65535) {
298 msg("<error> ID Table overflow");
299 msg("<error> This file is too complex to render- SWF only supports 65536 shapes at once");
305 return ++i->currentswfid;
307 static U16 getNewDepth(gfxdevice_t* dev)
309 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
310 if(i->depth == 65520) {
312 msg("<error> Depth Table overflow");
313 msg("<error> This file is too complex to render- SWF only supports 65536 shapes at once");
322 static void startshape(gfxdevice_t* dev);
323 static void starttext(gfxdevice_t* dev);
324 static void endshape(gfxdevice_t* dev);
325 static void endtext(gfxdevice_t* dev);
327 typedef struct _plotxy
332 static inline int twipsnap(double f)
334 /* if(f < -0x40000000/20.0) {
335 fprintf(stderr, "Warning: Coordinate underflow (%f)\n", f);
336 f = -0x40000000/20.0;
337 } else if(f>0x3fffffff/20.0) {
338 fprintf(stderr, "Warning: Coordinate overflow (%f)\n", f);
342 /* clamp coordinates to a rectangle with the property that we
343 can represent a line from the upper left corner to the upper
344 right corner using no more than 64 strokes */
345 const double min = -(1<<(18+4))/20.0;
346 const double max = ((1<<(18+4))-1)/20.0;
348 fprintf(stderr, "Warning: Coordinate underflow (%f)\n", f);
351 fprintf(stderr, "Warning: Coordinate overflow (%f)\n", f);
358 // write a move-to command into the swf
359 static int movetoxy(gfxdevice_t*dev, TAG*tag, plotxy_t p0)
361 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
362 int rx = twipsnap(p0.x);
363 int ry = twipsnap(p0.y);
364 if(rx!=i->swflastx || ry!=i->swflasty || i->fillstylechanged) {
365 swf_ShapeSetMove (tag, i->shape, rx,ry);
366 i->fillstylechanged = 0;
373 static int moveto(gfxdevice_t*dev, TAG*tag, double x, double y)
375 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
379 return movetoxy(dev, tag, p);
381 static void addPointToBBox(gfxdevice_t*dev, int px, int py)
383 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
389 swf_ExpandRect(&i->bboxrect, p);
391 swf_ExpandRect3(&i->bboxrect, p, i->linewidth*3/2);
395 /*static void plot(gfxdevice_t*dev, int x, int y, TAG*tag)
397 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
398 int width = i->linewidth/4;
402 //swf_ShapeSetLine(tag, i->shape,-width,-width);
403 //swf_ShapeSetLine(tag, i->shape,width*2,0);
404 //swf_ShapeSetLine(tag, i->shape,0,width*2);
405 //swf_ShapeSetLine(tag, i->shape,-width*2,0);
406 //swf_ShapeSetLine(tag, i->shape,0,-width*2);
407 //swf_ShapeSetLine(tag, i->shape,width,width);
410 swf_ShapeSetLine(tag, i->shape,-width,0);
411 swf_ShapeSetLine(tag, i->shape,width,-width);
412 swf_ShapeSetLine(tag, i->shape,width,width);
413 swf_ShapeSetLine(tag, i->shape,-width,width);
414 swf_ShapeSetLine(tag, i->shape,-width,-width);
415 swf_ShapeSetLine(tag, i->shape,width,0);
417 addPointToBBox(dev, x-width ,y-width);
418 addPointToBBox(dev, x+width ,y+width);
421 // write a line-to command into the swf
422 static void linetoxy(gfxdevice_t*dev, TAG*tag, plotxy_t p0)
424 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
425 int px = twipsnap(p0.x);
426 int py = twipsnap(p0.y);
427 int rx = (px-i->swflastx);
428 int ry = (py-i->swflasty);
430 swf_ShapeSetLine (tag, i->shape, rx,ry);
431 addPointToBBox(dev, i->swflastx,i->swflasty);
432 addPointToBBox(dev, px,py);
433 } /* this is a nice idea, but doesn't work with current flash
434 players (the pixel will be invisible if they're not
435 precisely on a pixel boundary)
436 Besides, we should only do this if this lineto itself
437 is again followed by a "move".
438 else if(!i->fill && i->config_dots) {
439 // treat lines of length 0 as plots, making them
440 // at least 1 twip wide so Flash will display them
441 //plot(dev, i->swflastx, i->swflasty, tag);
442 swf_ShapeSetLine (tag, i->shape, rx+1,ry);
449 static void lineto(gfxdevice_t*dev, TAG*tag, double x, double y)
454 linetoxy(dev,tag, p);
457 // write a spline-to command into the swf
458 static void splineto(gfxdevice_t*dev, TAG*tag, plotxy_t control,plotxy_t end)
460 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
461 int lastlastx = i->swflastx;
462 int lastlasty = i->swflasty;
464 int cx = (twipsnap(control.x)-i->swflastx);
465 int cy = (twipsnap(control.y)-i->swflasty);
468 int ex = (twipsnap(end.x)-i->swflastx);
469 int ey = (twipsnap(end.y)-i->swflasty);
473 if((cx || cy) && (ex || ey)) {
474 swf_ShapeSetCurve(tag, i->shape, cx,cy,ex,ey);
475 addPointToBBox(dev, lastlastx ,lastlasty );
476 addPointToBBox(dev, lastlastx+cx,lastlasty+cy);
477 addPointToBBox(dev, lastlastx+cx+ex,lastlasty+cy+ey);
478 } else if(cx || cy || ex || ey) {
479 swf_ShapeSetLine(tag, i->shape, cx+ex,cy+ey);
480 addPointToBBox(dev, lastlastx ,lastlasty );
481 addPointToBBox(dev, lastlastx+cx,lastlasty+cy);
482 addPointToBBox(dev, lastlastx+cx+ex,lastlasty+cy+ey);
488 /* write a line, given two points and the transformation
490 /*static void line(gfxdevice_t*dev, TAG*tag, plotxy_t p0, plotxy_t p1)
492 moveto(dev, tag, p0);
493 lineto(dev, tag, p1);
496 void resetdrawer(gfxdevice_t*dev)
498 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
503 static void stopFill(gfxdevice_t*dev)
505 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
506 if(i->lastwasfill!=0)
508 swf_ShapeSetStyle(i->tag,i->shape,i->linestyleid,0x8000,0);
509 i->fillstylechanged = 1;
513 static void startFill(gfxdevice_t*dev)
515 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
516 if(i->lastwasfill!=1)
518 swf_ShapeSetStyle(i->tag,i->shape,0x8000,i->fillstyleid,0);
519 i->fillstylechanged = 1;
524 static inline int colorcompare(RGBA*a,RGBA*b)
536 static SRECT getcharacterbbox(chararray_t*chardata, MATRIX* m)
540 memset(&r, 0, sizeof(r));
543 if(debug) printf("\n");
546 for(t=0;t<chardata->pos;t++) {
547 charatposition_t*chr = &chardata->chr[t];
548 SRECT b = chr->font->layout->bounds[chr->charid];
554 /* divide by 1024, rounding xmax/ymax up */
555 b.xmax += 1023; b.ymax += 1023; b.xmin /= 1024; b.ymin /= 1024; b.xmax /= 1024; b.ymax /= 1024;
562 /* until we solve the INTERNAL_SCALING problem (see below)
563 make sure the bounding box is big enough */
569 b = swf_TurnRect(b, m);
571 if(debug) printf("(%f,%f,%f,%f) -> (%f,%f,%f,%f) [font %d, char %d]\n",
572 chr->font->layout->bounds[chr->charid].xmin/20.0,
573 chr->font->layout->bounds[chr->charid].ymin/20.0,
574 chr->font->layout->bounds[chr->charid].xmax/20.0,
575 chr->font->layout->bounds[chr->charid].ymax/20.0,
582 swf_ExpandRect2(&r, &b);
584 chardata = chardata->next;
586 if(debug) printf("-----> (%f,%f,%f,%f)\n",
594 static chararray_t*chararray_reverse(chararray_t*buf)
596 chararray_t*prev = 0;
598 chararray_t*next = buf->next;
606 static void chararray_writetotag(chararray_t*_chardata, TAG*tag)
610 color.r = _chardata?_chardata->chr[0].color.r^255:0;
619 int charadvance[128];
622 int glyphbits=1; //TODO: can this be zero?
625 if(tag->id != ST_DEFINETEXT &&
626 tag->id != ST_DEFINETEXT2) {
627 msg("<error> internal error: charbuffer_put needs an text tag, not %d\n",tag->id);
631 msg("<warning> charbuffer_put called with zero characters");
634 for(pass = 0; pass < 2; pass++)
644 advancebits++; // add sign bit
645 swf_SetU8(tag, glyphbits);
646 swf_SetU8(tag, advancebits);
649 chararray_t*chardata = _chardata;
654 assert(!chardata->next || chardata->pos == CHARDATAMAX);
655 assert(chardata->pos);
657 int to = chardata->next?chardata->pos-1:chardata->pos;
661 char islast = t==chardata->pos;
663 charatposition_t*chr = &chardata->chr[t];
665 if(lastfont != chardata->chr[t].font ||
666 lastx!=chardata->chr[t].x ||
667 lasty!=chardata->chr[t].y ||
668 !colorcompare(&color, &chardata->chr[t].color) ||
670 lastsize != chardata->chr[t].size ||
673 if(charstorepos && pass==0)
676 for(s=0;s<charstorepos;s++)
678 while(charids[s]>=(1<<glyphbits))
680 while(charadvance[s]>=(1<<advancebits))
684 if(charstorepos && pass==1)
686 tag->writeBit = 0; // Q&D
687 swf_SetBits(tag, 0, 1); // GLYPH Record
688 swf_SetBits(tag, charstorepos, 7); // number of glyphs
690 for(s=0;s<charstorepos;s++)
692 swf_SetBits(tag, charids[s], glyphbits);
693 swf_SetBits(tag, charadvance[s], advancebits);
698 if(pass == 1 && !islast)
704 if(lastx != chr->x ||
714 if(!colorcompare(&color, &chr->color))
719 font.id = chr->font->id;
720 if(lastfont != chr->font || lastsize != chr->size)
723 tag->writeBit = 0; // Q&D
724 swf_TextSetInfoRecord(tag, newfont, chr->size, newcolor, newx,newy);
727 lastfont = chr->font;
730 lastsize = chr->size;
737 if(t<chardata->pos-1) nextx = chardata->chr[t+1].x;
738 if(t==chardata->pos-1 && chardata->next) nextx = chardata->next->chr[0].x;
739 int dx = nextx-chr->x;
742 if(dx>=0 && (dx<(1<<(advancebits-1)) || pass==0)) {
749 charids[charstorepos] = chr->charid;
750 charadvance[charstorepos] = advance;
753 chardata = chardata->next;
758 static void chararray_destroy(chararray_t*chr)
761 chararray_t*next = chr->next;
768 static inline int matrix_diff(MATRIX*m1, MATRIX*m2)
770 return memcmp(m1,m2,sizeof(MATRIX));
772 static charbuffer_t*charbuffer_append(charbuffer_t*buf, SWFFONT*font, int charid, int x,int y, int size, RGBA color, MATRIX*m)
774 if(!buf || matrix_diff(&buf->matrix,m)) {
775 charbuffer_t*n = rfx_calloc(sizeof(charbuffer_t));
780 if(!buf->last || buf->last->pos == CHARDATAMAX) {
781 chararray_t*n = rfx_calloc(sizeof(chararray_t));
783 buf->array = buf->last = n;
789 chararray_t*a = buf->last;
790 a->chr[a->pos].font = font;
791 a->chr[a->pos].charid = charid;
792 a->chr[a->pos].x = x;
793 a->chr[a->pos].y = y;
794 a->chr[a->pos].color = color;
795 a->chr[a->pos].size = size;
800 /* Notice: we can only put chars in the range -1639,1638 (-32768/20,32768/20).
801 So if we set this value to high, the char coordinates will overflow.
802 If we set it to low, however, the char positions will be inaccurate */
803 #define GLYPH_SCALE 1
805 static void chararray_writetodev(gfxdevice_t*dev, chararray_t*array, MATRIX*matrix, char invisible)
807 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
809 int textid = getNewID(dev);
810 i->tag = swf_InsertTag(i->tag,ST_DEFINETEXT2);
811 swf_SetU16(i->tag, textid);
813 r = getcharacterbbox(array, matrix);
814 r = swf_ClipRect(i->pagebbox, r);
815 swf_SetRect(i->tag,&r);
816 swf_SetMatrix(i->tag, matrix);
817 msg("<trace> Placing text as ID %d", textid);
818 chararray_writetotag(array, i->tag);
823 if(i->swf->fileVersion >= 8) {
824 i->tag = swf_InsertTag(i->tag, ST_CSMTEXTSETTINGS);
825 swf_SetU16(i->tag, textid);
827 //swf_SetU8(i->tag, /*subpixel grid*/(2<<3)|/*flashtype*/0x40);
828 swf_SetU8(i->tag, /*grid*/(1<<3)|/*flashtype*/0x40);
829 //swf_SetU8(i->tag, /*no grid*/(0<<3)|/*flashtype*/0x40);
831 swf_SetU32(i->tag, 0);//thickness
832 swf_SetU32(i->tag, 0);//sharpness
833 swf_SetU8(i->tag, 0);//reserved
835 i->tag = swf_InsertTag(i->tag,ST_PLACEOBJECT2);
836 if(invisible && i->config_flashversion>=8) {
837 swf_ObjectPlaceBlend(i->tag,textid,getNewDepth(dev),&i->page_matrix,NULL,NULL,BLENDMODE_MULTIPLY);
839 swf_ObjectPlace(i->tag,textid,getNewDepth(dev),&i->page_matrix,NULL,NULL);
843 static void charbuffer_writetodevandfree(gfxdevice_t*dev, charbuffer_t*buf, char invisible)
846 charbuffer_t*next = buf->next;buf->next = 0;
847 chararray_writetodev(dev, buf->array, &buf->matrix, invisible);
848 chararray_destroy(buf->array);
854 static void endtext(gfxdevice_t*dev)
856 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
859 charbuffer_writetodevandfree(dev, i->chardata, 0);i->chardata = 0;
863 /* sets the matrix which is to be applied to characters drawn by swfoutput_drawchar() */
864 static void setfontscale(gfxdevice_t*dev,double m11,double m12, double m21,double m22,double x, double y, char force)
870 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
871 if(i->lastfontm11 == m11 &&
872 i->lastfontm12 == m12 &&
873 i->lastfontm21 == m21 &&
874 i->lastfontm22 == m22 && !force)
879 i->lastfontm11 = m11;
880 i->lastfontm12 = m12;
881 i->lastfontm21 = m21;
882 i->lastfontm22 = m22;
884 double xsize = sqrt(m11*m11 + m12*m12);
885 double ysize = sqrt(m21*m21 + m22*m22);
886 i->current_font_size = (xsize>ysize?xsize:ysize)*1;
887 if(i->current_font_size < 1)
888 i->current_font_size = 1;
889 double ifs = 1.0 / (i->current_font_size*GLYPH_SCALE);
892 m.sx = (S32)((m11*ifs)*65536); m.r1 = (S32)((m21*ifs)*65536);
893 m.r0 = (S32)((m12*ifs)*65536); m.sy = (S32)((m22*ifs)*65536);
894 /* this is the position of the first char to set a new fontmatrix-
895 we hope that it's close enough to all other characters using the
896 font, so we use its position as origin for the matrix */
902 static int watermark2_width=47;
903 static int watermark2_height=11;
904 static int watermark2[47] = {95,1989,71,0,2015,337,1678,0,2015,5,1921,320,1938,25,2006,1024,
905 1042,21,13,960,1039,976,8,2000,1359,1088,31,1989,321,1728,0,1152,
906 1344,832,0,1984,0,896,1088,1088,896,0,1984,128,256,512,1984};
908 static void draw_watermark(gfxdevice_t*dev, gfxbbox_t r, char drawall)
910 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
911 double wx = r.xmax / 5.0;
912 double tx = r.xmax*4.0 / 5.0;
913 double ty = r.ymax-wx*watermark2_height/watermark2_width;
914 double sx = (r.xmax - tx) / watermark2_width;
915 double sy = (r.ymax - ty) / watermark2_height;
918 if(ty > 0 && px > 1.0 && py > 1.0) {
920 for(y=0;y<watermark2_height;y++)
921 for(x=0;x<watermark2_width;x++) {
922 if(((watermark2[x]>>y)&1)) {
923 if(!drawall && rand()%5)
925 unsigned int b = rand();
926 moveto(dev, i->tag, x*sx+tx+((b>>1)&1)/20.0, y*sy+ty+((b>>3)&1)/20.0);
927 lineto(dev, i->tag, x*sx+px+tx+((b>>2)&1)/20.0, y*sy+ty+((b>>3)&1)/20.0);
928 lineto(dev, i->tag, x*sx+px+tx+((b>>2)&1)/20.0, y*sy+py+ty+((b>>4)&1)/20.0);
929 lineto(dev, i->tag, x*sx+tx+((b>>1)&1)/20.0, y*sy+py+ty+((b>>4)&1)/20.0);
930 lineto(dev, i->tag, x*sx+tx+((b>>1)&1)/20.0, y*sy+ty+((b>>3)&1)/20.0);
936 static void swfoutput_setfillcolor(gfxdevice_t* dev, U8 r, U8 g, U8 b, U8 a)
938 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
939 if(i->fillrgb.r == r &&
942 i->fillrgb.a == a) return;
951 static void insert_watermark(gfxdevice_t*dev, char drawall)
953 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
954 if(!drawall && i->watermarks>20)
960 swfoutput_setfillcolor(dev, 0,0,255,192);
962 swfoutput_setfillcolor(dev, rand(),rand(),rand(),(rand()&127)+128);
967 gfxbbox_t r; r.xmin = r.ymin = 0;
970 draw_watermark(dev, r, drawall);
976 static void endpage(gfxdevice_t*dev)
978 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
986 charbuffer_writetodevandfree(dev, i->topchardata, 1);
993 if(i->config_watermark) {
994 insert_watermark(dev, 1);
1000 static void addViewer(gfxdevice_t* dev)
1002 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1005 RGBA button_colors[3]= {{0xbf,0x00,0x00,0x80},{0xbf,0x20,0x20,0xc0}, {0xbf,0xc0,0xc0,0xff}};
1007 int button_sizex = 20;
1008 int button_sizey = 20;
1010 RGBA black = {255,0,0,0};
1012 i->tag = swf_InsertTag(i->tag,ST_DEFINESHAPE3);
1014 int ls1 = swf_ShapeAddLineStyle(s,40,&black);
1015 int fs1 = swf_ShapeAddSolidFillStyle(s,&button_colors[t/2]);
1016 int shapeid = ids[t] = getNewID(dev);
1017 swf_SetU16(i->tag,shapeid);
1019 r.xmin = -20*button_sizex;
1020 r.xmax = 20*button_sizex;
1022 r.ymax = 40*button_sizey;
1023 swf_SetRect(i->tag,&r); // set shape bounds
1024 swf_SetShapeHeader(i->tag,s); // write all styles to tag
1025 swf_ShapeSetAll(i->tag,s,0*button_sizex,0,ls1,fs1,0);
1026 swf_ShapeSetLine(i->tag,s,(1-(t&1)*2)*20*button_sizex,20*button_sizey);
1027 swf_ShapeSetLine(i->tag,s,-(1-(t&1)*2)*20*button_sizex,20*button_sizey);
1028 swf_ShapeSetLine(i->tag,s,0,-40*button_sizey);
1029 swf_ShapeSetEnd(i->tag); // finish drawing
1030 swf_ShapeFree(s); // clean shape structure (which isn't needed anymore after writing the tag)
1032 ActionTAG*a1=0,*a2=0,*a3=0;
1033 a1 = action_NextFrame(a1);
1034 a1 = action_Stop(a1);
1035 a1 = action_End(a1);
1037 a2 = action_PreviousFrame(a2);
1038 a2 = action_Stop(a2);
1039 a2 = action_End(a2);
1041 a3 = action_Stop(a3);
1042 a3 = action_End(a3);
1044 i->tag = swf_InsertTag(i->tag, ST_DOACTION);
1045 swf_ActionSet(i->tag,a3);
1047 i->tag = swf_InsertTag(i->tag,ST_DEFINEBUTTON);
1048 int buttonid1 = getNewID(dev);
1049 swf_SetU16(i->tag, buttonid1);
1050 swf_ButtonSetRecord(i->tag,BS_UP|BS_HIT,ids[0],1,NULL,NULL);
1051 swf_ButtonSetRecord(i->tag,BS_OVER,ids[2],1,NULL,NULL);
1052 swf_ButtonSetRecord(i->tag,BS_DOWN,ids[4],1,NULL,NULL);
1053 swf_SetU8(i->tag,0); // end of button records
1054 swf_ActionSet(i->tag,a1);
1056 i->tag = swf_InsertTag(i->tag,ST_DEFINEBUTTON);
1057 int buttonid2 = getNewID(dev);
1058 swf_SetU16(i->tag, buttonid2);
1059 swf_ButtonSetRecord(i->tag,BS_UP|BS_HIT,ids[1],1,NULL,NULL);
1060 swf_ButtonSetRecord(i->tag,BS_OVER,ids[3],1,NULL,NULL);
1061 swf_ButtonSetRecord(i->tag,BS_DOWN,ids[5],1,NULL,NULL);
1062 swf_SetU8(i->tag,0); // end of button records
1063 swf_ActionSet(i->tag,a2);
1065 i->tag = swf_InsertTag(i->tag,ST_PLACEOBJECT2);
1067 swf_GetMatrix(0, &m);
1068 m.tx = button_sizex*20+200;
1069 swf_ObjectPlace(i->tag, buttonid2, 65534,&m,0,0);
1070 i->tag = swf_InsertTag(i->tag,ST_PLACEOBJECT2);
1071 m.tx = button_sizex*20+200+200;
1072 swf_ObjectPlace(i->tag, buttonid1, 65535,&m,0,0);
1076 void swf_startframe(gfxdevice_t*dev, int width, int height)
1078 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1080 if(i->config_protect) {
1081 i->tag = swf_InsertTag(i->tag, ST_PROTECT);
1082 i->config_protect = 0;
1084 if(i->config_simpleviewer) {
1089 if(!i->firstpage && !i->pagefinished)
1092 msg("<verbose> Starting new SWF page of size %dx%d", width, height);
1094 swf_GetMatrix(0, &i->page_matrix);
1095 i->page_matrix.tx = 0;
1096 i->page_matrix.ty = 0;
1103 /* create a bbox structure with the page size. This is used
1104 for clipping shape and text bounding boxes. As we don't want to
1105 generate bounding boxes which extend beyond the movie size (in
1106 order to not confuse Flash), we clip everything against i->pagebbox */
1107 i->pagebbox.xmin = 0;
1108 i->pagebbox.ymin = 0;
1109 i->pagebbox.xmax = width*20;
1110 i->pagebbox.ymax = height*20;
1112 /* increase SWF's bounding box */
1113 swf_ExpandRect2(&i->swf->movieSize, &i->pagebbox);
1115 i->lastframeno = i->frameno;
1117 i->pagefinished = 0;
1121 void swf_endframe(gfxdevice_t*dev)
1123 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1125 if(!i->pagefinished)
1128 if( (i->swf->fileVersion <= 8) && (i->config_insertstoptag) ) {
1130 atag = action_Stop(atag);
1131 atag = action_End(atag);
1132 i->tag = swf_InsertTag(i->tag,ST_DOACTION);
1133 swf_ActionSet(i->tag,atag);
1135 i->tag = swf_InsertTag(i->tag,ST_SHOWFRAME);
1138 for(i->depth;i->depth>i->startdepth;i->depth--) {
1139 i->tag = swf_InsertTag(i->tag,ST_REMOVEOBJECT2);
1140 swf_SetU16(i->tag,i->depth);
1142 i->depth = i->startdepth;
1144 if(i->config_frameresets) {
1145 for(i->currentswfid;i->currentswfid>i->startids;i->currentswfid--) {
1146 i->tag = swf_InsertTag(i->tag,ST_FREECHARACTER);
1147 swf_SetU16(i->tag,i->currentswfid);
1149 i->currentswfid = i->startids;
1153 static void setBackground(gfxdevice_t*dev, int x1, int y1, int x2, int y2)
1155 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1157 rgb.a = rgb.r = rgb.g = rgb.b = 0xff;
1161 int shapeid = getNewID(dev);
1166 i->tag = swf_InsertTag(i->tag, ST_DEFINESHAPE);
1168 fs1 = swf_ShapeAddSolidFillStyle(s, &rgb);
1169 swf_SetU16(i->tag,shapeid);
1170 swf_SetRect(i->tag,&r);
1171 swf_SetShapeHeader(i->tag,s);
1172 swf_ShapeSetAll(i->tag,s,x1,y1,ls1,fs1,0);
1173 swf_ShapeSetLine(i->tag,s,(x2-x1),0);
1174 swf_ShapeSetLine(i->tag,s,0,(y2-y1));
1175 swf_ShapeSetLine(i->tag,s,(x1-x2),0);
1176 swf_ShapeSetLine(i->tag,s,0,(y1-y2));
1177 swf_ShapeSetEnd(i->tag);
1179 i->tag = swf_InsertTag(i->tag, ST_PLACEOBJECT2);
1180 swf_ObjectPlace(i->tag,shapeid,getNewDepth(dev),0,0,0);
1181 i->tag = swf_InsertTag(i->tag, ST_PLACEOBJECT2);
1182 swf_ObjectPlaceClip(i->tag,shapeid,getNewDepth(dev),0,0,0,65535);
1185 /* initialize the swf writer */
1186 void gfxdevice_swf_init(gfxdevice_t* dev)
1188 memset(dev, 0, sizeof(gfxdevice_t));
1192 dev->internal = init_internal_struct(); // set config to default values
1194 dev->startpage = swf_startframe;
1195 dev->endpage = swf_endframe;
1196 dev->finish = swf_finish;
1197 dev->fillbitmap = swf_fillbitmap;
1198 dev->setparameter = swf_setparameter;
1199 dev->stroke = swf_stroke;
1200 dev->startclip = swf_startclip;
1201 dev->endclip = swf_endclip;
1202 dev->fill = swf_fill;
1203 dev->fillbitmap = swf_fillbitmap;
1204 dev->fillgradient = swf_fillgradient;
1205 dev->addfont = swf_addfont;
1206 dev->drawchar = swf_drawchar;
1207 dev->drawlink = swf_drawlink;
1209 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1212 msg("<verbose> initializing swf output\n", i->max_x,i->max_y);
1216 i->swf = (SWF*)rfx_calloc(sizeof(SWF));
1217 i->swf->fileVersion = 0;
1218 i->swf->frameRate = 0x80;
1219 i->swf->movieSize.xmin = 0;
1220 i->swf->movieSize.ymin = 0;
1221 i->swf->movieSize.xmax = 0;
1222 i->swf->movieSize.ymax = 0;
1223 i->swf->fileAttributes = 9; // as3, local-with-network
1225 i->swf->firstTag = swf_InsertTag(NULL,ST_SETBACKGROUNDCOLOR);
1226 i->tag = i->swf->firstTag;
1228 rgb.a = rgb.r = rgb.g = rgb.b = 0xff;
1230 swf_SetRGB(i->tag,&rgb);
1232 i->startdepth = i->depth = 0;
1233 i->startids = i->currentswfid = 0;
1236 static void startshape(gfxdevice_t*dev)
1238 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1243 //if(i->chardatapos && i->chardata[i->chardatapos-1].color.a)
1246 i->tag = swf_InsertTag(i->tag,ST_DEFINESHAPE3);
1248 swf_ShapeNew(&i->shape);
1249 i->linestyleid = swf_ShapeAddLineStyle(i->shape,i->linewidth,&i->strokergb);
1250 i->fillstyleid = swf_ShapeAddSolidFillStyle(i->shape,&i->fillrgb);
1252 RGBA markcol = {0,i->mark[0],i->mark[1],i->mark[2]};
1253 swf_ShapeAddSolidFillStyle(i->shape,&markcol);
1256 i->shapeid = getNewID(dev);
1258 msg("<debug> Using shape id %d", i->shapeid);
1260 swf_SetU16(i->tag,i->shapeid); // ID
1262 i->bboxrectpos = i->tag->len;
1264 swf_SetRect(i->tag,&i->pagebbox);
1266 memset(&i->bboxrect, 0, sizeof(i->bboxrect));
1268 swf_SetShapeStyles(i->tag,i->shape);
1269 swf_ShapeCountBits(i->shape,NULL,NULL);
1270 swf_SetShapeBits(i->tag,i->shape);
1272 /* TODO: do we really need this? */
1273 //swf_ShapeSetAll(i->tag,i->shape,/*x*/0,/*y*/0,i->linestyleid,0,0);
1274 //swf_ShapeSetAll(i->tag,i->shape,/*x*/UNDEFINED_COORD,/*y*/UNDEFINED_COORD,i->linestyleid,0,0);
1275 i->swflastx=i->swflasty=UNDEFINED_COORD;
1276 i->lastwasfill = -1;
1277 i->shapeisempty = 1;
1280 static void starttext(gfxdevice_t*dev)
1282 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1286 if(i->config_watermark) {
1287 insert_watermark(dev, 0);
1290 i->swflastx=i->swflasty=0;
1294 /* TODO: move to ../lib/rfxswf */
1295 void changeRect(gfxdevice_t*dev, TAG*tag, int pos, SRECT*newrect)
1297 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1298 /* determine length of old rect */
1302 swf_GetRect(tag, &old);
1303 swf_ResetReadBits(tag);
1304 int pos_end = tag->pos;
1306 int len = tag->len - pos_end;
1307 U8*data = (U8*)malloc(len);
1308 memcpy(data, &tag->data[pos_end], len);
1311 swf_SetRect(tag, newrect);
1312 swf_SetBlock(tag, data, len);
1314 tag->pos = tag->readBit = 0;
1317 void cancelshape(gfxdevice_t*dev)
1319 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1320 /* delete old shape tag */
1322 i->tag = i->tag->prev;
1323 swf_DeleteTag(0, todel);
1324 if(i->shape) {swf_ShapeFree(i->shape);i->shape=0;}
1326 i->bboxrectpos = -1;
1328 // i->currentswfid--; // doesn't work, for some reason
1331 void fixAreas(gfxdevice_t*dev)
1333 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1334 if(!i->shapeisempty && i->fill &&
1335 (i->bboxrect.xmin == i->bboxrect.xmax ||
1336 i->bboxrect.ymin == i->bboxrect.ymax) &&
1337 i->config_minlinewidth >= 0.001
1339 msg("<debug> Shape has size 0: width=%.2f height=%.2f",
1340 (i->bboxrect.xmax-i->bboxrect.xmin)/20.0,
1341 (i->bboxrect.ymax-i->bboxrect.ymin)/20.0
1344 SRECT r = i->bboxrect;
1346 if(r.xmin == r.xmax && r.ymin == r.ymax) {
1347 /* this thing comes down to a single dot- nothing to fix here */
1353 RGBA save_col = i->strokergb;
1354 int save_width = i->linewidth;
1356 i->strokergb = i->fillrgb;
1357 i->linewidth = (int)(i->config_minlinewidth*20);
1358 if(i->linewidth==0) i->linewidth = 1;
1363 moveto(dev, i->tag, r.xmin/20.0,r.ymin/20.0);
1364 lineto(dev, i->tag, r.xmax/20.0,r.ymax/20.0);
1366 i->strokergb = save_col;
1367 i->linewidth = save_width;
1372 static void endshape_noput(gfxdevice_t*dev)
1374 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1377 //changeRect(dev, i->tag, i->bboxrectpos, &i->bboxrect);
1380 swf_ShapeFree(i->shape);
1388 static void endshape(gfxdevice_t*dev)
1390 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1396 if(i->shapeisempty ||
1398 (i->bboxrect.xmin == i->bboxrect.xmax &&
1399 i->bboxrect.ymin == i->bboxrect.ymax))
1401 // delete the shape again, we didn't do anything
1402 msg("<debug> cancelling shape: bbox is (%f,%f,%f,%f)",
1403 i->bboxrect.xmin /20.0,
1404 i->bboxrect.ymin /20.0,
1405 i->bboxrect.xmax /20.0,
1406 i->bboxrect.ymax /20.0
1412 swf_ShapeSetEnd(i->tag);
1414 SRECT r = swf_ClipRect(i->pagebbox, i->bboxrect);
1415 changeRect(dev, i->tag, i->bboxrectpos, &r);
1417 msg("<trace> Placing shape ID %d", i->shapeid);
1419 i->tag = swf_InsertTag(i->tag,ST_PLACEOBJECT2);
1420 MATRIX m = i->page_matrix;
1421 m.tx += i->shapeposx;
1422 m.ty += i->shapeposy;
1423 swf_ObjectPlace(i->tag,i->shapeid,getNewDepth(dev),&m,NULL,NULL);
1425 if(i->config_animate) {
1426 i->tag = swf_InsertTag(i->tag,ST_SHOWFRAME);
1429 swf_ShapeFree(i->shape);
1432 i->bboxrectpos = -1;
1439 void wipeSWF(SWF*swf)
1441 TAG*tag = swf->firstTag;
1443 TAG*next = tag->next;
1444 if(tag->id != ST_SETBACKGROUNDCOLOR &&
1445 tag->id != ST_END &&
1446 tag->id != ST_DOACTION &&
1447 tag->id != ST_SHOWFRAME) {
1448 swf_DeleteTag(swf, tag);
1454 void swfoutput_finalize(gfxdevice_t*dev)
1456 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1458 if(i->tag && i->tag->id == ST_END)
1459 return; //already done
1461 i->swf->fileVersion = i->config_flashversion;
1462 i->swf->frameRate = i->config_framerate*0x100;
1464 if(i->config_bboxvars) {
1465 TAG* tag = swf_InsertTag(i->swf->firstTag, ST_DOACTION);
1467 a = action_PushString(a, "xmin");
1468 a = action_PushFloat(a, i->swf->movieSize.xmin / 20.0);
1469 a = action_SetVariable(a);
1470 a = action_PushString(a, "ymin");
1471 a = action_PushFloat(a, i->swf->movieSize.ymin / 20.0);
1472 a = action_SetVariable(a);
1473 a = action_PushString(a, "xmax");
1474 a = action_PushFloat(a, i->swf->movieSize.xmax / 20.0);
1475 a = action_SetVariable(a);
1476 a = action_PushString(a, "ymax");
1477 a = action_PushFloat(a, i->swf->movieSize.ymax / 20.0);
1478 a = action_SetVariable(a);
1479 a = action_PushString(a, "width");
1480 a = action_PushFloat(a, (i->swf->movieSize.xmax - i->swf->movieSize.xmin) / 20.0);
1481 a = action_SetVariable(a);
1482 a = action_PushString(a, "height");
1483 a = action_PushFloat(a, (i->swf->movieSize.ymax - i->swf->movieSize.ymin) / 20.0);
1484 a = action_SetVariable(a);
1486 swf_ActionSet(tag, a);
1491 free(i->mark);i->mark = 0;
1495 fontlist_t *iterator = i->fontlist;
1497 TAG*mtag = i->swf->firstTag;
1498 if(iterator->swffont) {
1499 if(!i->config_storeallcharacters) {
1500 msg("<debug> Reducing font %s", iterator->swffont->name);
1501 swf_FontReduce(iterator->swffont);
1503 int used = iterator->swffont->use && iterator->swffont->use->used_glyphs;
1505 mtag = swf_InsertTag(mtag, ST_DEFINEFONT2);
1506 swf_FontSetDefine2(mtag, iterator->swffont);
1510 iterator = iterator->next;
1513 i->tag = swf_InsertTag(i->tag,ST_END);
1514 TAG* tag = i->tag->prev;
1516 /* remove the removeobject2 tags between the last ST_SHOWFRAME
1517 and the ST_END- they confuse the flash player */
1518 while(tag->id == ST_REMOVEOBJECT2) {
1519 TAG* prev = tag->prev;
1520 swf_DeleteTag(i->swf, tag);
1527 if(i->config_enablezlib || i->config_flashversion>=6) {
1528 i->swf->compressed = 1;
1531 /* Add AVM2 actionscript */
1532 if(i->config_flashversion>=9 &&
1533 (i->config_insertstoptag || i->hasbuttons)) {
1534 swf_AddButtonLinks(i->swf, i->config_insertstoptag,
1535 i->config_internallinkfunction||i->config_externallinkfunction);
1537 // if(i->config_reordertags)
1538 // swf_Optimize(i->swf);
1541 int swfresult_save(gfxresult_t*gfx, const char*filename)
1543 SWF*swf = (SWF*)gfx->internal;
1546 fi = open(filename, O_BINARY|O_CREAT|O_TRUNC|O_WRONLY, 0777);
1551 msg("<fatal> Could not create \"%s\". ", FIXNULL(filename));
1555 if FAILED(swf_WriteSWF(fi,swf))
1556 msg("<error> WriteSWF() failed.\n");
1562 void* swfresult_get(gfxresult_t*gfx, const char*name)
1564 SWF*swf = (SWF*)gfx->internal;
1565 if(!strcmp(name, "swf")) {
1566 return (void*)swf_CopySWF(swf);
1567 } else if(!strcmp(name, "xmin")) {
1568 return (void*)(ptroff_t)(swf->movieSize.xmin/20);
1569 } else if(!strcmp(name, "ymin")) {
1570 return (void*)(ptroff_t)(swf->movieSize.ymin/20);
1571 } else if(!strcmp(name, "xmax")) {
1572 return (void*)(ptroff_t)(swf->movieSize.xmax/20);
1573 } else if(!strcmp(name, "ymax")) {
1574 return (void*)(ptroff_t)(swf->movieSize.ymax/20);
1575 } else if(!strcmp(name, "width")) {
1576 return (void*)(ptroff_t)((swf->movieSize.xmax - swf->movieSize.xmin)/20);
1577 } else if(!strcmp(name, "height")) {
1578 return (void*)(ptroff_t)((swf->movieSize.ymax - swf->movieSize.ymin)/20);
1582 void swfresult_destroy(gfxresult_t*gfx)
1585 swf_FreeTags((SWF*)gfx->internal);
1586 free(gfx->internal);
1589 memset(gfx, 0, sizeof(gfxresult_t));
1593 static void swfoutput_destroy(gfxdevice_t* dev);
1595 gfxresult_t* swf_finish(gfxdevice_t* dev)
1597 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1600 if(i->config_linktarget) {
1601 free(i->config_linktarget);
1602 i->config_linktarget = 0;
1605 swfoutput_finalize(dev);
1606 SWF* swf = i->swf;i->swf = 0;
1607 swfoutput_destroy(dev);
1609 result = (gfxresult_t*)rfx_calloc(sizeof(gfxresult_t));
1610 result->internal = swf;
1611 result->save = swfresult_save;
1613 result->get = swfresult_get;
1614 result->destroy = swfresult_destroy;
1618 /* Perform cleaning up */
1619 static void swfoutput_destroy(gfxdevice_t* dev)
1621 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1623 /* not initialized yet- nothing to destroy */
1627 fontlist_t *tmp,*iterator = i->fontlist;
1629 if(iterator->swffont) {
1630 swf_FontFree(iterator->swffont);iterator->swffont=0;
1633 iterator = iterator->next;
1636 if(i->swf) {swf_FreeTags(i->swf);free(i->swf);i->swf = 0;}
1639 memset(dev, 0, sizeof(gfxdevice_t));
1642 static void swfoutput_setstrokecolor(gfxdevice_t* dev, U8 r, U8 g, U8 b, U8 a)
1644 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1645 if(i->strokergb.r == r &&
1646 i->strokergb.g == g &&
1647 i->strokergb.b == b &&
1648 i->strokergb.a == a) return;
1658 //#define ROUND_UP 19
1659 //#define ROUND_UP 10
1661 static void swfoutput_setlinewidth(gfxdevice_t*dev, double _linewidth)
1663 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1664 if(i->linewidth == (U16)(_linewidth*20+19.0/20.0))
1668 i->linewidth = (U16)(_linewidth*20+19.0/20.0);
1672 static void drawlink(gfxdevice_t*dev, ActionTAG*,ActionTAG*, gfxline_t*points, char mouseover, const char*url);
1673 static void swfoutput_namedlink(gfxdevice_t*dev, char*name, gfxline_t*points);
1674 static void swfoutput_linktopage(gfxdevice_t*dev, int page, gfxline_t*points);
1675 static void swfoutput_linktourl(gfxdevice_t*dev, const char*url, gfxline_t*points);
1677 /*void swfoutput_drawlink(gfxdevice_t*dev, char*url, gfxline_t*points)
1679 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1680 dev->drawlink(dev, points, url);
1683 void swf_drawlink(gfxdevice_t*dev, gfxline_t*points, const char*url)
1685 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1687 if(i->config_disablelinks)
1690 if(!strncmp("http://pdf2swf:", url, 15)) {
1691 char*tmp = strdup(url);
1692 int l = strlen(tmp);
1695 swfoutput_namedlink(dev, tmp+15, points);
1698 } else if(!strncmp("page", url, 4)) {
1701 if(url[t]<'0' || url[t]>'9')
1704 int page = atoi(&url[4]);
1705 if(page<0) page = 0;
1706 swfoutput_linktopage(dev, page, points);
1709 swfoutput_linktourl(dev, url, points);
1712 void swfoutput_linktourl(gfxdevice_t*dev, const char*url, gfxline_t*points)
1714 ActionTAG* actions = 0;
1715 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1721 /* TODO: escape special characters in url */
1723 if(i->config_externallinkfunction && i->config_flashversion<=8) {
1724 actions = action_PushString(actions, url); //parameter
1725 actions = action_PushInt(actions, 1); //number of parameters (1)
1726 actions = action_PushString(actions, i->config_externallinkfunction); //function name
1727 actions = action_CallFunction(actions);
1728 } else if(!i->config_linktarget) {
1729 if(!i->config_opennewwindow)
1730 actions = action_GetUrl(actions, url, "_parent");
1732 actions = action_GetUrl(actions, url, "_this");
1734 actions = action_GetUrl(actions, url, i->config_linktarget);
1736 actions = action_End(actions);
1738 drawlink(dev, actions, 0, points, 0, url);
1740 swf_ActionFree(actions);
1742 void swfoutput_linktopage(gfxdevice_t*dev, int page, gfxline_t*points)
1744 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1745 ActionTAG* actions = 0;
1752 if(!i->config_internallinkfunction || i->config_flashversion>=9) {
1753 actions = action_GotoFrame(actions, page-1);
1754 actions = action_End(actions);
1756 actions = action_PushInt(actions, page); //parameter
1757 actions = action_PushInt(actions, 1); //number of parameters (1)
1758 actions = action_PushString(actions, i->config_internallinkfunction); //function name
1759 actions = action_CallFunction(actions);
1760 actions = action_End(actions);
1764 sprintf(name, "page%d", page);
1766 drawlink(dev, actions, 0, points, 0, name);
1768 swf_ActionFree(actions);
1771 /* Named Links (a.k.a. Acrobatmenu) are used to implement various gadgets
1772 of the viewer objects, like subtitles, index elements etc.
1774 void swfoutput_namedlink(gfxdevice_t*dev, char*name, gfxline_t*points)
1776 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1777 ActionTAG *actions1,*actions2;
1778 char*tmp = strdup(name);
1786 if(!strncmp(tmp, "call:", 5))
1788 char*x = strchr(&tmp[5], ':');
1790 actions1 = action_PushInt(0, 0); //number of parameters (0)
1791 actions1 = action_PushString(actions1, &tmp[5]); //function name
1792 actions1 = action_CallFunction(actions1);
1793 actions1 = action_End(actions1);
1796 actions1 = action_PushString(0, x+1); //parameter
1797 actions1 = action_PushInt(actions1, 1); //number of parameters (1)
1798 actions1 = action_PushString(actions1, &tmp[5]); //function name
1799 actions1 = action_CallFunction(actions1);
1800 actions1 = action_End(actions1);
1802 actions2 = action_End(0);
1807 actions1 = action_PushString(0, "/:subtitle");
1808 actions1 = action_PushString(actions1, name);
1809 actions1 = action_SetVariable(actions1);
1810 actions1 = action_End(actions1);
1812 actions2 = action_PushString(0, "/:subtitle");
1813 actions2 = action_PushString(actions2, "");
1814 actions2 = action_SetVariable(actions2);
1815 actions2 = action_End(actions2);
1818 drawlink(dev, actions1, actions2, points, mouseover, name);
1820 swf_ActionFree(actions1);
1821 swf_ActionFree(actions2);
1825 static void drawgfxline(gfxdevice_t*dev, gfxline_t*line, int fill)
1827 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1828 gfxcoord_t lastx=0,lasty=0,px=0,py=0;
1830 int lines= 0, splines=0;
1837 /* check whether the next segment is zero */
1838 if(line->type == gfx_moveTo) {
1839 moveto(dev, i->tag, line->x, line->y);
1840 px = lastx = line->x;
1841 py = lasty = line->y;
1843 } if(line->type == gfx_lineTo) {
1844 lineto(dev, i->tag, line->x, line->y);
1849 } else if(line->type == gfx_splineTo) {
1851 s.x = line->sx;p.x = line->x;
1852 s.y = line->sy;p.y = line->y;
1853 splineto(dev, i->tag, s, p);
1861 msg("<trace> drawgfxline, %d lines, %d splines", lines, splines);
1865 static void drawlink(gfxdevice_t*dev, ActionTAG*actions1, ActionTAG*actions2, gfxline_t*points, char mouseover, const char*url)
1867 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1876 int buttonid = getNewID(dev);
1877 gfxbbox_t bbox = gfxline_getbbox(points);
1882 myshapeid = getNewID(dev);
1883 i->tag = swf_InsertTag(i->tag,ST_DEFINESHAPE3);
1884 swf_ShapeNew(&i->shape);
1885 rgb.r = rgb.b = rgb.a = rgb.g = 0;
1886 fsid = swf_ShapeAddSolidFillStyle(i->shape,&rgb);
1887 swf_SetU16(i->tag, myshapeid);
1888 r.xmin = (int)(bbox.xmin*20);
1889 r.ymin = (int)(bbox.ymin*20);
1890 r.xmax = (int)(bbox.xmax*20);
1891 r.ymax = (int)(bbox.ymax*20);
1892 r = swf_ClipRect(i->pagebbox, r);
1893 swf_SetRect(i->tag,&r);
1894 swf_SetShapeStyles(i->tag,i->shape);
1895 swf_ShapeCountBits(i->shape,NULL,NULL);
1896 swf_SetShapeBits(i->tag,i->shape);
1897 swf_ShapeSetAll(i->tag,i->shape,/*x*/0,/*y*/0,0,fsid,0);
1898 i->swflastx = i->swflasty = 0;
1899 drawgfxline(dev, points, 1);
1900 swf_ShapeSetEnd(i->tag);
1901 swf_ShapeFree(i->shape);
1904 myshapeid2 = getNewID(dev);
1905 i->tag = swf_InsertTag(i->tag,ST_DEFINESHAPE3);
1906 swf_ShapeNew(&i->shape);
1908 rgb = i->config_linkcolor;
1910 fsid = swf_ShapeAddSolidFillStyle(i->shape,&rgb);
1911 swf_SetU16(i->tag, myshapeid2);
1912 r.xmin = (int)(bbox.xmin*20);
1913 r.ymin = (int)(bbox.ymin*20);
1914 r.xmax = (int)(bbox.xmax*20);
1915 r.ymax = (int)(bbox.ymax*20);
1916 r = swf_ClipRect(i->pagebbox, r);
1917 swf_SetRect(i->tag,&r);
1918 swf_SetShapeStyles(i->tag,i->shape);
1919 swf_ShapeCountBits(i->shape,NULL,NULL);
1920 swf_SetShapeBits(i->tag,i->shape);
1921 swf_ShapeSetAll(i->tag,i->shape,/*x*/0,/*y*/0,0,fsid,0);
1922 i->swflastx = i->swflasty = 0;
1923 drawgfxline(dev, points, 1);
1924 swf_ShapeSetEnd(i->tag);
1925 swf_ShapeFree(i->shape);
1929 i->tag = swf_InsertTag(i->tag,ST_DEFINEBUTTON);
1930 swf_SetU16(i->tag,buttonid); //id
1931 swf_ButtonSetFlags(i->tag, 0); //menu=no
1932 swf_ButtonSetRecord(i->tag,0x01,myshapeid,i->depth,0,0);
1933 swf_ButtonSetRecord(i->tag,0x02,myshapeid2,i->depth,0,0);
1934 swf_ButtonSetRecord(i->tag,0x04,myshapeid2,i->depth,0,0);
1935 swf_ButtonSetRecord(i->tag,0x08,myshapeid,i->depth,0,0);
1936 swf_SetU8(i->tag,0);
1937 swf_ActionSet(i->tag,actions1);
1938 swf_SetU8(i->tag,0);
1942 i->tag = swf_InsertTag(i->tag,ST_DEFINEBUTTON2);
1943 swf_SetU16(i->tag,buttonid); //id
1944 swf_ButtonSetFlags(i->tag, 0); //menu=no
1945 swf_ButtonSetRecord(i->tag,0x01,myshapeid,i->depth,0,0);
1946 swf_ButtonSetRecord(i->tag,0x02,myshapeid2,i->depth,0,0);
1947 swf_ButtonSetRecord(i->tag,0x04,myshapeid2,i->depth,0,0);
1948 swf_ButtonSetRecord(i->tag,0x08,myshapeid,i->depth,0,0);
1949 swf_SetU8(i->tag,0); // end of button records
1950 swf_ButtonSetCondition(i->tag, BC_IDLE_OVERUP);
1951 swf_ActionSet(i->tag,actions1);
1953 swf_ButtonSetCondition(i->tag, BC_OVERUP_IDLE);
1954 swf_ActionSet(i->tag,actions2);
1955 swf_SetU8(i->tag,0);
1956 swf_ButtonPostProcess(i->tag, 2);
1958 swf_SetU8(i->tag,0);
1959 swf_ButtonPostProcess(i->tag, 1);
1963 const char* name = 0;
1964 if(i->config_linknameurl) {
1968 sprintf(buf, "button%d", buttonid);
1971 msg("<trace> Placing link ID %d", buttonid);
1972 i->tag = swf_InsertTag(i->tag,ST_PLACEOBJECT2);
1974 if(posx!=0 || posy!=0) {
1976 p.x = (int)(posx*20);
1977 p.y = (int)(posy*20);
1978 p = swf_TurnPoint(p, &i->page_matrix);
1983 swf_ObjectPlace(i->tag, buttonid, getNewDepth(dev),&m,0,(U8*)name);
1985 swf_ObjectPlace(i->tag, buttonid, getNewDepth(dev),&i->page_matrix,0,(U8*)name);
1992 for(t=0;t<picpos;t++)
1994 if(pic_xids[t] == xid &&
1995 pic_yids[t] == yid) {
1996 width = pic_width[t];
1997 height = pic_height[t];
2001 pic_ids[picpos] = swfoutput_drawimagelosslessN(&output, pic, pal, width, height, x1,y1,x2,y2,x3,y3,x4,y4, numpalette);
2002 pic_xids[picpos] = xid;
2003 pic_yids[picpos] = yid;
2004 pic_width[picpos] = width;
2005 pic_height[picpos] = height;
2008 pic[width*y+x] = buf[0];
2012 xid += pal[1].r*3 + pal[1].g*11 + pal[1].b*17;
2013 yid += pal[1].r*7 + pal[1].g*5 + pal[1].b*23;
2017 xid += x*r+x*b*3+x*g*7+x*a*11;
2018 yid += y*r*3+y*b*17+y*g*19+y*a*11;
2020 for(t=0;t<picpos;t++)
2022 if(pic_xids[t] == xid &&
2023 pic_yids[t] == yid) {
2032 int swf_setparameter(gfxdevice_t*dev, const char*name, const char*value)
2034 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
2036 msg("<trace> swfdevice: %s=%s", name, value);
2037 if(!strcmp(name, "jpegsubpixels")) {
2038 i->config_jpegsubpixels = atof(value);
2039 } else if(!strcmp(name, "ppmsubpixels")) {
2040 i->config_ppmsubpixels = atof(value);
2041 } else if(!strcmp(name, "subpixels")) {
2042 i->config_ppmsubpixels = i->config_jpegsubpixels = atof(value);
2043 } else if(!strcmp(name, "drawonlyshapes")) {
2044 i->config_drawonlyshapes = atoi(value);
2045 } else if(!strcmp(name, "ignoredraworder")) {
2046 i->config_ignoredraworder = atoi(value);
2047 } else if(!strcmp(name, "mark")) {
2048 if(!value || !value[0]) {
2049 if(i->mark) free(i->mark);
2053 i->mark = strdup("...");
2054 for(t=0;t<3;t++) if(value[t]) i->mark[t] = value[t];
2056 } else if(!strcmp(name, "filloverlap")) {
2057 i->config_filloverlap = atoi(value);
2058 } else if(!strcmp(name, "linksopennewwindow")) {
2059 i->config_opennewwindow = atoi(value);
2060 } else if(!strcmp(name, "opennewwindow")) {
2061 i->config_opennewwindow = atoi(value);
2062 } else if(!strcmp(name, "storeallcharacters")) {
2063 i->config_storeallcharacters = atoi(value);
2064 } else if(!strcmp(name, "enablezlib")) {
2065 i->config_enablezlib = atoi(value);
2066 } else if(!strcmp(name, "bboxvars")) {
2067 i->config_bboxvars = atoi(value);
2068 } else if(!strcmp(name, "dots")) {
2069 i->config_dots = atoi(value);
2070 } else if(!strcmp(name, "frameresets")) {
2071 i->config_frameresets = atoi(value);
2072 } else if(!strcmp(name, "showclipshapes")) {
2073 i->config_showclipshapes = atoi(value);
2074 } else if(!strcmp(name, "reordertags")) {
2075 i->config_reordertags = atoi(value);
2076 } else if(!strcmp(name, "internallinkfunction")) {
2077 i->config_internallinkfunction = strdup(value);
2078 } else if(!strcmp(name, "externallinkfunction")) {
2079 i->config_externallinkfunction = strdup(value);
2080 } else if(!strcmp(name, "linkfunction")) { //sets both internallinkfunction and externallinkfunction
2081 i->config_internallinkfunction = strdup(value);
2082 i->config_externallinkfunction = strdup(value);
2083 } else if(!strcmp(name, "disable_polygon_conversion")) {
2084 i->config_disable_polygon_conversion = atoi(value);
2085 } else if(!strcmp(name, "normalize_polygon_positions")) {
2086 i->config_normalize_polygon_positions = atoi(value);
2087 } else if(!strcmp(name, "wxwindowparams")) {
2088 i->config_watermark = atoi(value);
2089 } else if(!strcmp(name, "insertstop")) {
2090 i->config_insertstoptag = atoi(value);
2091 } else if(!strcmp(name, "protect")) {
2092 i->config_protect = atoi(value);
2093 if(i->config_protect && i->tag) {
2094 i->tag = swf_InsertTag(i->tag, ST_PROTECT);
2096 } else if(!strcmp(name, "flashversion")) {
2097 i->config_flashversion = atoi(value);
2099 i->swf->fileVersion = i->config_flashversion;
2101 } else if(!strcmp(name, "framerate")) {
2102 i->config_framerate = atof(value);
2104 i->swf->frameRate = i->config_framerate*0x100;
2106 } else if(!strcmp(name, "minlinewidth")) {
2107 i->config_minlinewidth = atof(value);
2108 } else if(!strcmp(name, "caplinewidth")) {
2109 i->config_caplinewidth = atof(value);
2110 } else if(!strcmp(name, "linktarget")) {
2111 i->config_linktarget = strdup(value);
2112 } else if(!strcmp(name, "invisibletexttofront")) {
2113 i->config_invisibletexttofront = atoi(value);
2114 } else if(!strcmp(name, "noclips")) {
2115 i->config_noclips = atoi(value);
2116 } else if(!strcmp(name, "dumpfonts")) {
2117 i->config_dumpfonts = atoi(value);
2118 } else if(!strcmp(name, "animate")) {
2119 i->config_animate = atoi(value);
2120 } else if(!strcmp(name, "disablelinks")) {
2121 i->config_disablelinks = atoi(value);
2122 } else if(!strcmp(name, "simpleviewer")) {
2123 i->config_simpleviewer = atoi(value);
2124 } else if(!strcmp(name, "next_bitmap_is_jpeg")) {
2126 } else if(!strcmp(name, "jpegquality")) {
2127 int val = atoi(value);
2129 if(val>101) val=101;
2130 i->config_jpegquality = val;
2131 } else if(!strcmp(name, "splinequality")) {
2132 int v = atoi(value);
2133 v = 500-(v*5); // 100% = 0.25 pixel, 0% = 25 pixel
2135 i->config_splinemaxerror = v;
2136 } else if(!strcmp(name, "fontquality")) {
2137 int v = atoi(value);
2138 v = 500-(v*5); // 100% = 0.25 pixel, 0% = 25 pixel
2140 i->config_fontsplinemaxerror = v;
2141 } else if(!strcmp(name, "linkcolor")) {
2142 if(strlen(value)!=8) {
2143 fprintf(stderr, "Unknown format for option 'linkcolor'. (%s <-> RRGGBBAA)\n", value);
2146 # define NIBBLE(s) (((s)>='0' && (s)<='9')?((s)-'0'):((s)&0x0f)+9)
2147 i->config_linkcolor.r = NIBBLE(value[0])<<4 | NIBBLE(value[1]);
2148 i->config_linkcolor.g = NIBBLE(value[2])<<4 | NIBBLE(value[3]);
2149 i->config_linkcolor.b = NIBBLE(value[4])<<4 | NIBBLE(value[5]);
2150 i->config_linkcolor.a = NIBBLE(value[6])<<4 | NIBBLE(value[7]);
2151 } else if(!strcmp(name, "help")) {
2152 printf("\nSWF layer options:\n");
2153 printf("jpegsubpixels=<pixels> resolution adjustment for jpeg images (same as jpegdpi, but in pixels)\n");
2154 printf("ppmsubpixels=<pixels resolution adjustment for lossless images (same as ppmdpi, but in pixels)\n");
2155 printf("subpixels=<pixels> shortcut for setting both jpegsubpixels and ppmsubpixels\n");
2156 printf("drawonlyshapes convert everything to shapes (currently broken)\n");
2157 printf("ignoredraworder allow to perform a few optimizations for creating smaller SWFs\n");
2158 printf("linksopennewwindow make links open a new browser window\n");
2159 printf("linktarget target window name of new links\n");
2160 printf("linkcolor=<color) color of links (format: RRGGBBAA)\n");
2161 printf("linknameurl Link buttons will be named like the URL they refer to (handy for iterating through links with actionscript)\n");
2162 printf("storeallcharacters don't reduce the fonts to used characters in the output file\n");
2163 printf("enablezlib switch on zlib compression (also done if flashversion>=7)\n");
2164 printf("bboxvars store the bounding box of the SWF file in actionscript variables\n");
2165 printf("dots Take care to handle dots correctly\n");
2166 printf("reordertags=0/1 (default: 1) perform some tag optimizations\n");
2167 printf("internallinkfunction=<name> when the user clicks a internal link (to a different page) in the converted file, this actionscript function is called\n");
2168 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");
2169 printf("disable_polygon_conversion never convert strokes to polygons (will remove capstyles and joint styles)\n");
2170 printf("caplinewidth=<width> the minimum thichness a line needs to have so that capstyles become visible (and are converted)\n");
2171 printf("insertstop put an ActionScript \"STOP\" tag in every frame\n");
2172 printf("protect add a \"protect\" tag to the file, to prevent loading in the Flash editor\n");
2173 printf("flashversion=<version> the SWF fileversion (6)\n");
2174 printf("framerate=<fps> SWF framerate\n");
2175 printf("minlinewidth=<width> convert horizontal/vertical boxes smaller than this width to lines (0.05) \n");
2176 printf("simpleviewer Add next/previous buttons to the SWF\n");
2177 printf("animate insert a showframe tag after each placeobject (animate draw order of PDF files)\n");
2178 printf("jpegquality=<quality> set compression quality of jpeg images\n");
2179 printf("splinequality=<value> Set the quality of spline convertion to value (0-100, default: 100).\n");
2180 printf("disablelinks Disable links.\n");
2187 // --------------------------------------------------------------------
2189 static CXFORM gfxcxform_to_cxform(gfxcxform_t* c)
2192 swf_GetCXForm(0, &cx, 1);
2195 if(c->rg!=0 || c->rb!=0 || c->ra!=0 ||
2196 c->gr!=0 || c->gb!=0 || c->ga!=0 ||
2197 c->br!=0 || c->bg!=0 || c->ba!=0 ||
2198 c->ar!=0 || c->ag!=0 || c->ab!=0)
2199 msg("<warning> CXForm not SWF-compatible");
2201 cx.a0 = (S16)(c->aa*256);
2202 cx.r0 = (S16)(c->rr*256);
2203 cx.g0 = (S16)(c->gg*256);
2204 cx.b0 = (S16)(c->bb*256);
2213 static int imageInCache(gfxdevice_t*dev, void*data, int width, int height)
2217 static void addImageToCache(gfxdevice_t*dev, void*data, int width, int height)
2221 static int add_image(swfoutput_internal*i, gfximage_t*img, int targetwidth, int targetheight, int* newwidth, int* newheight)
2223 gfxdevice_t*dev = i->dev;
2225 RGBA*mem = (RGBA*)img->data;
2227 int sizex = img->width;
2228 int sizey = img->height;
2229 int is_jpeg = i->jpeg;
2232 int newsizex=sizex, newsizey=sizey;
2235 if(is_jpeg && i->config_jpegsubpixels) {
2236 newsizex = (int)(targetwidth*i->config_jpegsubpixels + 0.5);
2237 newsizey = (int)(targetheight*i->config_jpegsubpixels + 0.5);
2238 } else if(!is_jpeg && i->config_ppmsubpixels) {
2239 newsizex = (int)(targetwidth*i->config_ppmsubpixels + 0.5);
2240 newsizey = (int)(targetheight*i->config_ppmsubpixels + 0.5);
2244 if(sizex<=0 || sizey<=0)
2251 /* TODO: cache images */
2253 if(newsizex<sizex || newsizey<sizey) {
2254 msg("<verbose> Scaling %dx%d image to %dx%d", sizex, sizey, newsizex, newsizey);
2255 newpic = swf_ImageScale(mem, sizex, sizey, newsizex, newsizey);
2256 *newwidth = sizex = newsizex;
2257 *newheight = sizey = newsizey;
2260 *newwidth = newsizex = sizex;
2261 *newheight = newsizey = sizey;
2264 int num_colors = swf_ImageGetNumberOfPaletteEntries(mem,sizex,sizey,0);
2265 int has_alpha = swf_ImageHasAlpha(mem,sizex,sizey);
2267 msg("<verbose> Drawing %dx%d %s%simage (id %d) at size %dx%d (%dx%d), %s%d colors",
2269 has_alpha?(has_alpha==2?"semi-transparent ":"transparent "):"",
2270 is_jpeg?"jpeg-":"", i->currentswfid+1,
2272 targetwidth, targetheight,
2273 /*newsizex, newsizey,*/
2274 num_colors>256?">":"", num_colors>256?256:num_colors);
2276 /*RGBA* pal = (RGBA*)rfx_alloc(sizeof(RGBA)*num_colors);
2277 swf_ImageGetNumberOfPaletteEntries(mem,sizex,sizey,pal);
2279 for(t=0;t<num_colors;t++) {
2280 printf("%02x%02x%02x%02x ",
2281 pal[t].r, pal[t].g, pal[t].b, pal[t].a);
2288 int cacheid = imageInCache(dev, mem, sizex, sizey);
2291 bitid = getNewID(dev);
2293 i->tag = swf_AddImage(i->tag, bitid, mem, sizex, sizey, i->config_jpegquality);
2294 addImageToCache(dev, mem, sizex, sizey);
2304 static SRECT gfxline_getSWFbbox(gfxline_t*line)
2306 gfxbbox_t bbox = gfxline_getbbox(line);
2308 r.xmin = (int)(bbox.xmin*20);
2309 r.ymin = (int)(bbox.ymin*20);
2310 r.xmax = (int)(bbox.xmax*20);
2311 r.ymax = (int)(bbox.ymax*20);
2315 int line_is_empty(gfxline_t*line)
2318 if(line->type != gfx_moveTo)
2325 static void swf_fillbitmap(gfxdevice_t*dev, gfxline_t*line, gfximage_t*img, gfxmatrix_t*matrix, gfxcxform_t*cxform)
2327 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
2329 if(line_is_empty(line))
2335 int targetx = (int)(sqrt(matrix->m00*matrix->m00 + matrix->m01*matrix->m01)*img->width);
2336 int targety = (int)(sqrt(matrix->m10*matrix->m10 + matrix->m11*matrix->m11)*img->height);
2338 int newwidth=0,newheight=0;
2339 int bitid = add_image(i, img, targetx, targety, &newwidth, &newheight);
2342 double fx = (double)img->width / (double)newwidth;
2343 double fy = (double)img->height / (double)newheight;
2346 m.sx = (int)(65536*20*matrix->m00*fx); m.r1 = (int)(65536*20*matrix->m10*fy);
2347 m.r0 = (int)(65536*20*matrix->m01*fx); m.sy = (int)(65536*20*matrix->m11*fy);
2348 m.tx = (int)(matrix->tx*20);
2349 m.ty = (int)(matrix->ty*20);
2352 int myshapeid = getNewID(dev);
2353 i->tag = swf_InsertTag(i->tag,ST_DEFINESHAPE);
2355 swf_ShapeNew(&shape);
2356 int fsid = swf_ShapeAddBitmapFillStyle(shape,&m,bitid,1);
2357 swf_SetU16(i->tag, myshapeid);
2358 SRECT r = gfxline_getSWFbbox(line);
2359 r = swf_ClipRect(i->pagebbox, r);
2360 swf_SetRect(i->tag,&r);
2361 swf_SetShapeStyles(i->tag,shape);
2362 swf_ShapeCountBits(shape,NULL,NULL);
2363 swf_SetShapeBits(i->tag,shape);
2364 swf_ShapeSetAll(i->tag,shape,UNDEFINED_COORD,UNDEFINED_COORD,0,fsid,0);
2365 i->swflastx = i->swflasty = UNDEFINED_COORD;
2366 drawgfxline(dev, line, 1);
2367 swf_ShapeSetEnd(i->tag);
2368 swf_ShapeFree(shape);
2370 msg("<trace> Placing image, shape ID %d, bitmap ID %d", myshapeid, bitid);
2371 i->tag = swf_InsertTag(i->tag,ST_PLACEOBJECT2);
2372 CXFORM cxform2 = gfxcxform_to_cxform(cxform);
2373 swf_ObjectPlace(i->tag,myshapeid,getNewDepth(dev),&i->page_matrix,&cxform2,NULL);
2376 static RGBA col_black = {255,0,0,0};
2378 static void drawoutline(gfxdevice_t*dev, gfxline_t*line)
2380 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
2382 int myshapeid = getNewID(dev);
2383 i->tag = swf_InsertTag(i->tag,ST_DEFINESHAPE3);
2386 swf_ShapeNew(&shape);
2387 int lsid = swf_ShapeAddLineStyle(shape,1,&col_black);
2389 swf_SetU16(i->tag,myshapeid);
2390 SRECT r = gfxline_getSWFbbox(line);
2391 r = swf_ClipRect(i->pagebbox, r);
2392 swf_SetRect(i->tag,&r);
2393 swf_SetShapeStyles(i->tag,shape);
2394 swf_ShapeCountBits(shape,NULL,NULL);
2395 swf_SetShapeBits(i->tag,shape);
2396 swf_ShapeSetAll(i->tag,shape,UNDEFINED_COORD,UNDEFINED_COORD,lsid,0,0);
2397 drawgfxline(dev, line, 1);
2398 swf_ShapeSetEnd(i->tag);
2399 swf_ShapeFree(shape);
2401 i->tag = swf_InsertTag(i->tag,ST_PLACEOBJECT2);
2402 swf_ObjectPlace(i->tag, myshapeid, getNewDepth(dev), 0,0,0);
2405 static void swf_startclip(gfxdevice_t*dev, gfxline_t*line)
2407 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
2408 if(i->config_noclips)
2414 if(i->clippos >= 127)
2416 msg("<warning> Too many clip levels.");
2420 if(i->config_showclipshapes)
2421 drawoutline(dev, line);
2423 int myshapeid = getNewID(dev);
2424 i->tag = swf_InsertTag(i->tag,ST_DEFINESHAPE3);
2426 memset(&col, 0, sizeof(RGBA));
2429 swf_ShapeNew(&shape);
2430 int fsid = swf_ShapeAddSolidFillStyle(shape,&col);
2432 RGBA markcol = {0,i->mark[0],i->mark[1],i->mark[2]};
2433 swf_ShapeAddSolidFillStyle(shape,&markcol);
2435 swf_SetU16(i->tag,myshapeid);
2436 SRECT r = gfxline_getSWFbbox(line);
2437 r = swf_ClipRect(i->pagebbox, r);
2438 swf_SetRect(i->tag,&r);
2439 swf_SetShapeStyles(i->tag,shape);
2440 swf_ShapeCountBits(shape,NULL,NULL);
2441 swf_SetShapeBits(i->tag,shape);
2442 swf_ShapeSetAll(i->tag,shape,UNDEFINED_COORD,UNDEFINED_COORD,0,fsid,0);
2443 i->swflastx = i->swflasty = UNDEFINED_COORD;
2444 i->shapeisempty = 1;
2445 drawgfxline(dev, line, 1);
2446 if(i->shapeisempty) {
2447 /* an empty clip shape is equivalent to a shape with no area */
2448 int x = line?line->x:0;
2449 int y = line?line->y:0;
2450 moveto(dev, i->tag, x,y);
2451 lineto(dev, i->tag, x,y);
2452 lineto(dev, i->tag, x,y);
2454 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)) {
2455 if(i->config_watermark) {
2456 gfxbbox_t r; r.xmin = r.ymin = 0;r.xmax = i->max_x;r.ymax = i->max_y;
2457 draw_watermark(dev, r, 1);
2460 swf_ShapeSetEnd(i->tag);
2461 swf_ShapeFree(shape);
2463 /* TODO: remember the bbox, and check all shapes against it */
2465 msg("<trace> Placing clip ID %d", myshapeid);
2466 i->tag = swf_InsertTag(i->tag,ST_PLACEOBJECT2);
2467 i->cliptags[i->clippos] = i->tag;
2468 i->clipshapes[i->clippos] = myshapeid;
2469 i->clipdepths[i->clippos] = getNewDepth(dev);
2473 static void swf_endclip(gfxdevice_t*dev)
2475 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
2476 if(i->config_noclips)
2484 msg("<error> Invalid end of clipping region");
2488 /*swf_ObjectPlaceClip(i->cliptags[i->clippos],i->clipshapes[i->clippos],i->clipdepths[i->clippos],&i->page_matrix,NULL,NULL,
2489 / * clip to depth: * / i->depth <= i->clipdepths[i->clippos]? i->depth : i->depth - 1);
2491 swf_ObjectPlaceClip(i->cliptags[i->clippos],i->clipshapes[i->clippos],i->clipdepths[i->clippos],&i->page_matrix,NULL,NULL,i->depth);
2493 static int gfxline_type(gfxline_t*line)
2499 int haszerosegments=0;
2502 if(line->type == gfx_moveTo) {
2505 } else if(line->type == gfx_lineTo) {
2509 } else if(line->type == gfx_splineTo) {
2511 if(tmpsplines>lines)
2519 if(lines==0 && splines==0) return 0;
2520 else if(lines==1 && splines==0) return 1;
2521 else if(lines==0 && splines==1) return 2;
2522 else if(splines==0) return 3;
2526 static int gfxline_has_dots(gfxline_t*line)
2534 if(line->type == gfx_moveTo) {
2535 /* test the length of the preceding line, and assume it is a dot if
2536 it's length is less than 1.0. But *only* if there's a noticable
2537 gap between the previous line and the next moveTo. (I've come
2538 across a PDF where thousands of "dots" were stringed together,
2540 int last_short_gap = short_gap;
2541 if((fabs(line->x - x) + fabs(line->y - y)) < 1.0) {
2546 if(isline && dist < 1 && !short_gap && !last_short_gap) {
2551 } else if(line->type == gfx_lineTo) {
2552 dist += fabs(line->x - x) + fabs(line->y - y);
2554 } else if(line->type == gfx_splineTo) {
2555 dist += fabs(line->sx - x) + fabs(line->sy - y) +
2556 fabs(line->x - line->sx) + fabs(line->y - line->sy);
2563 if(isline && dist < 1 && !short_gap) {
2569 static int gfxline_fix_short_edges(gfxline_t*line)
2573 if(line->type == gfx_lineTo) {
2574 if(fabs(line->x - x) + fabs(line->y - y) < 0.01) {
2577 } else if(line->type == gfx_splineTo) {
2578 if(fabs(line->sx - x) + fabs(line->sy - y) +
2579 fabs(line->x - line->sx) + fabs(line->y - line->sy) < 0.01) {
2590 static char is_inside_page(gfxdevice_t*dev, gfxcoord_t x, gfxcoord_t y)
2592 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
2593 if(x<i->min_x || x>i->max_x) return 0;
2594 if(y<i->min_y || y>i->max_y) return 0;
2598 gfxline_t* gfxline_move(gfxline_t*line, double x, double y)
2600 gfxline_t*l = line = gfxline_clone(line);
2612 //#define NORMALIZE_POLYGON_POSITIONS
2614 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)
2616 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
2617 if(line_is_empty(line))
2619 int type = gfxline_type(line);
2620 int has_dots = gfxline_has_dots(line);
2621 gfxbbox_t r = gfxline_getbbox(line);
2622 int is_outside_page = !is_inside_page(dev, r.xmin, r.ymin) || !is_inside_page(dev, r.xmax, r.ymax);
2624 /* TODO: * split line into segments, and perform this check for all segments */
2626 if(i->config_disable_polygon_conversion || /*type>=5 ||*/
2628 (width <= i->config_caplinewidth
2629 || (cap_style == gfx_capRound && joint_style == gfx_joinRound)
2630 || (cap_style == gfx_capRound && type<=2))))
2634 /* convert line to polygon */
2635 msg("<trace> draw as polygon, type=%d dots=%d", type, has_dots);
2637 gfxline_fix_short_edges(line);
2638 /* we need to convert the line into a polygon */
2639 gfxpoly_t* poly = gfxpoly_from_stroke(line, width, cap_style, joint_style, miterLimit, DEFAULT_GRID);
2640 gfxline_t*gfxline = gfxline_from_gfxpoly(poly);
2641 dev->fill(dev, gfxline, color);
2642 gfxline_free(gfxline);
2643 gfxpoly_destroy(poly);
2647 msg("<trace> draw as stroke, type=%d dots=%d", type, has_dots);
2650 if(i->config_normalize_polygon_positions) {
2652 double startx = 0, starty = 0;
2653 if(line && line->type == gfx_moveTo) {
2657 line = gfxline_move(line, -startx, -starty);
2658 i->shapeposx = (int)(startx*20);
2659 i->shapeposy = (int)(starty*20);
2662 swfoutput_setstrokecolor(dev, color->r, color->g, color->b, color->a);
2663 swfoutput_setlinewidth(dev, width);
2666 drawgfxline(dev, line, 0);
2668 if(i->config_normalize_polygon_positions) {
2669 free(line); //account for _move
2674 static void swf_fill(gfxdevice_t*dev, gfxline_t*line, gfxcolor_t*color)
2676 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
2677 if(line_is_empty(line))
2681 gfxbbox_t r = gfxline_getbbox(line);
2682 int is_outside_page = !is_inside_page(dev, r.xmin, r.ymin) || !is_inside_page(dev, r.xmax, r.ymax);
2686 if(!i->config_ignoredraworder)
2689 if(i->config_normalize_polygon_positions) {
2691 double startx = 0, starty = 0;
2692 if(line && line->type == gfx_moveTo) {
2696 line = gfxline_move(line, -startx, -starty);
2697 i->shapeposx = (int)(startx*20);
2698 i->shapeposy = (int)(starty*20);
2701 swfoutput_setfillcolor(dev, color->r, color->g, color->b, color->a);
2704 drawgfxline(dev, line, 1);
2706 if(i->currentswfid==2 && r.xmin==0 && r.ymin==0 && r.xmax==i->max_x && r.ymax==i->max_y) {
2707 if(i->config_watermark) {
2708 draw_watermark(dev, r, 1);
2712 msg("<trace> end of swf_fill (shapeid=%d)", i->shapeid);
2714 if(i->config_normalize_polygon_positions) {
2715 free(line); //account for _move
2719 static GRADIENT* gfxgradient_to_GRADIENT(gfxgradient_t*gradient)
2722 gfxgradient_t*g = gradient;
2727 GRADIENT* swfgradient = malloc(sizeof(GRADIENT));
2728 swfgradient->num = num;
2729 swfgradient->rgba = malloc(sizeof(swfgradient->rgba[0])*num);
2730 swfgradient->ratios = malloc(sizeof(swfgradient->ratios[0])*num);
2735 swfgradient->ratios[num] = g->pos*255;
2736 swfgradient->rgba[num] = *(RGBA*)&g->color;
2743 static void swf_fillgradient(gfxdevice_t*dev, gfxline_t*line, gfxgradient_t*gradient, gfxgradienttype_t type, gfxmatrix_t*matrix)
2745 if(line_is_empty(line))
2747 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
2749 if(line_is_empty(line))
2752 GRADIENT* swfgradient = gfxgradient_to_GRADIENT(gradient);
2759 double f = type==gfxgradient_radial?4:4;
2761 m.sx = (int)(matrix->m00*20*f); m.r1 = (int)(matrix->m10*20*f);
2762 m.r0 = (int)(matrix->m01*20*f); m.sy = (int)(matrix->m11*20*f);
2763 m.tx = (int)(matrix->tx*20);
2764 m.ty = (int)(matrix->ty*20);
2767 int myshapeid = getNewID(dev);
2768 i->tag = swf_InsertTag(i->tag, ST_DEFINESHAPE2);
2770 swf_ShapeNew(&shape);
2771 int fsid = swf_ShapeAddGradientFillStyle(shape,&m,swfgradient,type==gfxgradient_radial);
2772 swf_SetU16(i->tag, myshapeid);
2773 SRECT r = gfxline_getSWFbbox(line);
2774 r = swf_ClipRect(i->pagebbox, r);
2775 swf_SetRect(i->tag,&r);
2776 swf_SetShapeStyles(i->tag,shape);
2777 swf_ShapeCountBits(shape,NULL,NULL);
2778 swf_SetShapeBits(i->tag,shape);
2779 swf_ShapeSetAll(i->tag,shape,UNDEFINED_COORD,UNDEFINED_COORD,0,fsid,0);
2780 i->swflastx = i->swflasty = UNDEFINED_COORD;
2781 drawgfxline(dev, line, 1);
2782 swf_ShapeSetEnd(i->tag);
2783 swf_ShapeFree(shape);
2785 int depth = getNewDepth(dev);
2786 msg("<trace> Placing gradient, shape ID %d, depth %d", myshapeid, depth);
2787 i->tag = swf_InsertTag(i->tag,ST_PLACEOBJECT2);
2788 swf_ObjectPlace(i->tag,myshapeid,depth,&i->page_matrix,NULL,NULL);
2790 swf_FreeGradient(swfgradient);free(swfgradient);
2793 static SWFFONT* gfxfont_to_swffont(gfxfont_t*font, const char* id)
2795 SWFFONT*swffont = (SWFFONT*)rfx_calloc(sizeof(SWFFONT));
2797 SRECT bounds = {0,0,0,0};
2799 swffont->version = 2;
2800 swffont->name = (U8*)strdup(id);
2801 swffont->layout = (SWFLAYOUT*)rfx_calloc(sizeof(SWFLAYOUT));
2802 swffont->layout->ascent = 0;
2803 swffont->layout->descent = 0;
2804 swffont->layout->leading = 0;
2805 swffont->layout->bounds = (SRECT*)rfx_calloc(sizeof(SRECT)*font->num_glyphs);
2806 swffont->encoding = FONT_ENCODING_UNICODE;
2807 swffont->numchars = font->num_glyphs;
2808 swffont->maxascii = font->max_unicode;
2809 swffont->ascii2glyph = (int*)rfx_calloc(sizeof(int)*swffont->maxascii);
2810 swffont->glyph2ascii = (U16*)rfx_calloc(sizeof(U16)*swffont->numchars);
2811 swffont->glyph = (SWFGLYPH*)rfx_calloc(sizeof(SWFGLYPH)*swffont->numchars);
2812 swffont->glyphnames = (char**)rfx_calloc(sizeof(char*)*swffont->numchars);
2813 for(t=0;t<font->max_unicode;t++) {
2814 swffont->ascii2glyph[t] = font->unicode2glyph[t];
2816 SRECT max = {0,0,0,0};
2817 for(t=0;t<font->num_glyphs;t++) {
2821 swffont->glyph2ascii[t] = font->glyphs[t].unicode;
2822 if(swffont->glyph2ascii[t] == 0xffff || swffont->glyph2ascii[t] == 0x0000) {
2823 /* flash 8 flashtype requires unique unicode IDs for each character.
2824 We use the Unicode private user area to assign characters, hoping that
2825 the font doesn't contain more than 2048 glyphs */
2826 swffont->glyph2ascii[t] = 0xe000 + (t&0x1fff);
2829 if(font->glyphs[t].name) {
2830 swffont->glyphnames[t] = strdup(font->glyphs[t].name);
2832 swffont->glyphnames[t] = 0;
2834 advance = font->glyphs[t].advance;
2836 swf_Shape01DrawerInit(&draw, 0);
2837 line = font->glyphs[t].line;
2841 c.x = line->sx * GLYPH_SCALE; c.y = line->sy * GLYPH_SCALE;
2842 to.x = line->x * GLYPH_SCALE; to.y = line->y * GLYPH_SCALE;
2843 if(line->type == gfx_moveTo) {
2844 draw.moveTo(&draw, &to);
2845 } else if(line->type == gfx_lineTo) {
2846 draw.lineTo(&draw, &to);
2847 } else if(line->type == gfx_splineTo) {
2848 draw.splineTo(&draw, &c, &to);
2853 swffont->glyph[t].shape = swf_ShapeDrawerToShape(&draw);
2855 SRECT bbox = swf_ShapeDrawerGetBBox(&draw);
2856 swf_ExpandRect2(&max, &bbox);
2858 swffont->layout->bounds[t] = bbox;
2860 if(advance<32768.0/20) {
2861 swffont->glyph[t].advance = (int)(advance*20);
2863 //msg("<warning> Advance value overflow in glyph %d", t);
2864 swffont->glyph[t].advance = 32767;
2867 draw.dealloc(&draw);
2869 swf_ExpandRect2(&bounds, &swffont->layout->bounds[t]);
2871 for(t=0;t<font->num_glyphs;t++) {
2872 SRECT bbox = swffont->layout->bounds[t];
2874 /* if the glyph doesn't have a bounding box, use the
2875 combined bounding box (necessary e.g. for space characters) */
2876 if(!(bbox.xmin|bbox.ymin|bbox.xmax|bbox.ymax)) {
2877 swffont->layout->bounds[t] = bbox = max;
2880 /* check that the advance value is reasonable, by comparing it
2881 with the bounding box */
2882 if(bbox.xmax>0 && (bbox.xmax*10 < swffont->glyph[t].advance || !swffont->glyph[t].advance)) {
2883 if(swffont->glyph[t].advance)
2884 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);
2885 swffont->glyph[t].advance = bbox.xmax;
2887 //swffont->glyph[t].advance = bbox.xmax - bbox.xmin;
2891 /* Flash player will use the advance value from the char, and the ascent/descent values
2892 from the layout for text selection.
2893 ascent will extend the char into negative y direction, from the baseline, while descent
2894 will extend in positive y direction, also from the baseline.
2895 The baseline is defined as the y-position zero
2898 swffont->layout->ascent = -bounds.ymin;
2899 if(swffont->layout->ascent < 0)
2900 swffont->layout->ascent = 0;
2901 swffont->layout->descent = bounds.ymax;
2902 if(swffont->layout->descent < 0)
2903 swffont->layout->descent = 0;
2904 swffont->layout->leading = bounds.ymax - bounds.ymin;
2906 /* if the font has proper ascent/descent values (>0) and those define
2907 greater line spacing that what we estimated from the bounding boxes,
2908 use the font's parameters */
2909 if(font->ascent*20 > swffont->layout->ascent)
2910 swffont->layout->ascent = font->ascent*20;
2911 if(font->descent*20 > swffont->layout->descent)
2912 swffont->layout->descent = font->descent*20;
2917 static void swf_addfont(gfxdevice_t*dev, gfxfont_t*font)
2919 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
2921 if(i->swffont && i->swffont->name && !strcmp((char*)i->swffont->name,font->id))
2922 return; // the requested font is the current font
2924 fontlist_t*last=0,*l = i->fontlist;
2927 if(!strcmp((char*)l->swffont->name, font->id)) {
2928 return; // we already know this font
2932 l = (fontlist_t*)rfx_calloc(sizeof(fontlist_t));
2933 l->swffont = gfxfont_to_swffont(font, font->id);
2940 swf_FontSetID(l->swffont, getNewID(i->dev));
2942 if(getScreenLogLevel() >= LOGLEVEL_DEBUG) {
2944 // print font information
2945 msg("<debug> Font %s",font->id);
2946 msg("<debug> | ID: %d", l->swffont->id);
2947 msg("<debug> | Version: %d", l->swffont->version);
2948 msg("<debug> | Name: %s", l->swffont->name);
2949 msg("<debug> | Numchars: %d", l->swffont->numchars);
2950 msg("<debug> | Maxascii: %d", l->swffont->maxascii);
2951 msg("<debug> | Style: %d", l->swffont->style);
2952 msg("<debug> | Encoding: %d", l->swffont->encoding);
2953 for(iii=0; iii<l->swffont->numchars;iii++) {
2954 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,
2955 l->swffont->layout->bounds[iii].xmin/20.0,
2956 l->swffont->layout->bounds[iii].ymin/20.0,
2957 l->swffont->layout->bounds[iii].xmax/20.0,
2958 l->swffont->layout->bounds[iii].ymax/20.0
2961 for(t=0;t<l->swffont->maxascii;t++) {
2962 if(l->swffont->ascii2glyph[t] == iii)
2963 msg("<debug> | - maps to %d",t);
2969 static void swf_switchfont(gfxdevice_t*dev, const char*fontid)
2971 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
2973 if(i->swffont && i->swffont->name && !strcmp((char*)i->swffont->name,fontid))
2974 return; // the requested font is the current font
2976 fontlist_t*l = i->fontlist;
2978 if(!strcmp((char*)l->swffont->name, fontid)) {
2979 i->swffont = l->swffont;
2984 msg("<error> Unknown font id: %s", fontid);
2988 static void swf_drawchar(gfxdevice_t*dev, gfxfont_t*font, int glyph, gfxcolor_t*color, gfxmatrix_t*matrix)
2990 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
2992 msg("<error> swf_drawchar called (glyph %d) without font", glyph);
2996 if(i->config_drawonlyshapes) {
2997 gfxglyph_t*g = &font->glyphs[glyph];
2998 gfxline_t*line2 = gfxline_clone(g->line);
2999 gfxline_transform(line2, matrix);
3000 dev->fill(dev, line2, color);
3001 gfxline_free(line2);
3005 if(!i->swffont || !i->swffont->name || strcmp((char*)i->swffont->name,font->id)) // not equal to current font
3007 swf_switchfont(dev, font->id); // set the current font
3011 msg("<warning> swf_drawchar: Font is NULL");
3014 if(glyph<0 || glyph>=i->swffont->numchars) {
3015 msg("<warning> No character %d in font %s (%d chars)", glyph, FIXNULL((char*)i->swffont->name), i->swffont->numchars);
3019 setfontscale(dev, matrix->m00, matrix->m01, matrix->m10, matrix->m11, matrix->tx, matrix->ty, 0);
3021 double det = i->fontmatrix.sx/65536.0 * i->fontmatrix.sy/65536.0 -
3022 i->fontmatrix.r0/65536.0 * i->fontmatrix.r1/65536.0;
3023 if(fabs(det) < 0.0005) {
3024 /* x direction equals y direction- the text is invisible */
3025 msg("<verbose> Not drawing invisible character %d (det=%f, m=[%f %f;%f %f]\n", glyph,
3027 i->fontmatrix.sx/65536.0, i->fontmatrix.r1/65536.0,
3028 i->fontmatrix.r0/65536.0, i->fontmatrix.sy/65536.0);
3032 /*if(i->swffont->glyph[glyph].shape->bitlen <= 16) {
3033 msg("<warning> Glyph %d in current charset (%s, %d characters) is empty",
3034 glyph, FIXNULL((char*)i->swffont->name), i->swffont->numchars);
3038 /* calculate character position with respect to the current font matrix */
3039 double s = 20 * GLYPH_SCALE / det;
3040 double px = matrix->tx - i->fontmatrix.tx/20.0;
3041 double py = matrix->ty - i->fontmatrix.ty/20.0;
3042 int x = (SCOORD)(( px * i->fontmatrix.sy/65536.0 - py * i->fontmatrix.r1/65536.0)*s);
3043 int y = (SCOORD)((- px * i->fontmatrix.r0/65536.0 + py * i->fontmatrix.sx/65536.0)*s);
3044 if(x>32767 || x<-32768 || y>32767 || y<-32768) {
3045 msg("<verbose> Moving character origin to %f %f\n", matrix->tx, matrix->ty);
3047 setfontscale(dev, matrix->m00, matrix->m01, matrix->m10, matrix->m11, matrix->tx, matrix->ty, 1);
3048 /* since we just moved the char origin to the current char's position,
3049 it now has the relative position (0,0) */
3058 msg("<trace> Drawing char %d in font %d at %d,%d in color %02x%02x%02x%02x",
3059 glyph, i->swffont->id, x, y, color->r, color->g, color->b, color->a);
3061 if(color->a == 0 && i->config_invisibletexttofront) {
3062 if(i->config_flashversion>=8) {
3063 // use "multiply" blend mode
3064 color->a = color->r = color->g = color->b = 255;
3066 i->topchardata = charbuffer_append(i->topchardata, i->swffont, glyph, x, y, i->current_font_size, *(RGBA*)color, &i->fontmatrix);
3068 i->chardata = charbuffer_append(i->chardata, i->swffont, glyph, x, y, i->current_font_size, *(RGBA*)color, &i->fontmatrix);
3070 swf_FontUseGlyph(i->swffont, glyph);