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 if(invisible && i->config_flashversion>=8) {
836 i->tag = swf_InsertTag(i->tag,ST_PLACEOBJECT3);
837 swf_ObjectPlaceBlend(i->tag,textid,getNewDepth(dev),&i->page_matrix,NULL,NULL,BLENDMODE_MULTIPLY);
839 i->tag = swf_InsertTag(i->tag,ST_PLACEOBJECT2);
840 swf_ObjectPlace(i->tag,textid,getNewDepth(dev),&i->page_matrix,NULL,NULL);
844 static void charbuffer_writetodevandfree(gfxdevice_t*dev, charbuffer_t*buf, char invisible)
847 charbuffer_t*next = buf->next;buf->next = 0;
848 chararray_writetodev(dev, buf->array, &buf->matrix, invisible);
849 chararray_destroy(buf->array);
855 static void endtext(gfxdevice_t*dev)
857 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
860 charbuffer_writetodevandfree(dev, i->chardata, 0);i->chardata = 0;
864 /* sets the matrix which is to be applied to characters drawn by swfoutput_drawchar() */
865 static void setfontscale(gfxdevice_t*dev,double m11,double m12, double m21,double m22,double x, double y, char force)
871 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
872 if(i->lastfontm11 == m11 &&
873 i->lastfontm12 == m12 &&
874 i->lastfontm21 == m21 &&
875 i->lastfontm22 == m22 && !force)
880 i->lastfontm11 = m11;
881 i->lastfontm12 = m12;
882 i->lastfontm21 = m21;
883 i->lastfontm22 = m22;
885 double xsize = sqrt(m11*m11 + m12*m12);
886 double ysize = sqrt(m21*m21 + m22*m22);
887 i->current_font_size = (xsize>ysize?xsize:ysize)*1;
888 if(i->current_font_size < 1)
889 i->current_font_size = 1;
890 double ifs = 1.0 / (i->current_font_size*GLYPH_SCALE);
893 m.sx = (S32)((m11*ifs)*65536); m.r1 = (S32)((m21*ifs)*65536);
894 m.r0 = (S32)((m12*ifs)*65536); m.sy = (S32)((m22*ifs)*65536);
895 /* this is the position of the first char to set a new fontmatrix-
896 we hope that it's close enough to all other characters using the
897 font, so we use its position as origin for the matrix */
903 static int watermark2_width=47;
904 static int watermark2_height=11;
905 static int watermark2[47] = {95,1989,71,0,2015,337,1678,0,2015,5,1921,320,1938,25,2006,1024,
906 1042,21,13,960,1039,976,8,2000,1359,1088,31,1989,321,1728,0,1152,
907 1344,832,0,1984,0,896,1088,1088,896,0,1984,128,256,512,1984};
909 static void draw_watermark(gfxdevice_t*dev, gfxbbox_t r, char drawall)
911 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
912 double wx = r.xmax / 5.0;
913 double tx = r.xmax*4.0 / 5.0;
914 double ty = r.ymax-wx*watermark2_height/watermark2_width;
915 double sx = (r.xmax - tx) / watermark2_width;
916 double sy = (r.ymax - ty) / watermark2_height;
919 if(ty > 0 && px > 1.0 && py > 1.0) {
921 for(y=0;y<watermark2_height;y++)
922 for(x=0;x<watermark2_width;x++) {
923 if(((watermark2[x]>>y)&1)) {
924 if(!drawall && rand()%5)
926 unsigned int b = rand();
927 moveto(dev, i->tag, x*sx+tx+((b>>1)&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+ty+((b>>3)&1)/20.0);
929 lineto(dev, i->tag, x*sx+px+tx+((b>>2)&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+py+ty+((b>>4)&1)/20.0);
931 lineto(dev, i->tag, x*sx+tx+((b>>1)&1)/20.0, y*sy+ty+((b>>3)&1)/20.0);
937 static void swfoutput_setfillcolor(gfxdevice_t* dev, U8 r, U8 g, U8 b, U8 a)
939 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
940 if(i->fillrgb.r == r &&
943 i->fillrgb.a == a) return;
952 static void insert_watermark(gfxdevice_t*dev, char drawall)
954 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
955 if(!drawall && i->watermarks>20)
961 swfoutput_setfillcolor(dev, 0,0,255,192);
963 swfoutput_setfillcolor(dev, rand(),rand(),rand(),(rand()&127)+128);
968 gfxbbox_t r; r.xmin = r.ymin = 0;
971 draw_watermark(dev, r, drawall);
977 static void endpage(gfxdevice_t*dev)
979 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
987 charbuffer_writetodevandfree(dev, i->topchardata, 1);
994 if(i->config_watermark) {
995 insert_watermark(dev, 1);
1001 static void addViewer(gfxdevice_t* dev)
1003 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1006 RGBA button_colors[3]= {{0xbf,0x00,0x00,0x80},{0xbf,0x20,0x20,0xc0}, {0xbf,0xc0,0xc0,0xff}};
1008 int button_sizex = 20;
1009 int button_sizey = 20;
1011 RGBA black = {255,0,0,0};
1013 i->tag = swf_InsertTag(i->tag,ST_DEFINESHAPE3);
1015 int ls1 = swf_ShapeAddLineStyle(s,40,&black);
1016 int fs1 = swf_ShapeAddSolidFillStyle(s,&button_colors[t/2]);
1017 int shapeid = ids[t] = getNewID(dev);
1018 swf_SetU16(i->tag,shapeid);
1020 r.xmin = -20*button_sizex;
1021 r.xmax = 20*button_sizex;
1023 r.ymax = 40*button_sizey;
1024 swf_SetRect(i->tag,&r); // set shape bounds
1025 swf_SetShapeHeader(i->tag,s); // write all styles to tag
1026 swf_ShapeSetAll(i->tag,s,0*button_sizex,0,ls1,fs1,0);
1027 swf_ShapeSetLine(i->tag,s,(1-(t&1)*2)*20*button_sizex,20*button_sizey);
1028 swf_ShapeSetLine(i->tag,s,-(1-(t&1)*2)*20*button_sizex,20*button_sizey);
1029 swf_ShapeSetLine(i->tag,s,0,-40*button_sizey);
1030 swf_ShapeSetEnd(i->tag); // finish drawing
1031 swf_ShapeFree(s); // clean shape structure (which isn't needed anymore after writing the tag)
1033 ActionTAG*a1=0,*a2=0,*a3=0;
1034 a1 = action_NextFrame(a1);
1035 a1 = action_Stop(a1);
1036 a1 = action_End(a1);
1038 a2 = action_PreviousFrame(a2);
1039 a2 = action_Stop(a2);
1040 a2 = action_End(a2);
1042 a3 = action_Stop(a3);
1043 a3 = action_End(a3);
1045 i->tag = swf_InsertTag(i->tag, ST_DOACTION);
1046 swf_ActionSet(i->tag,a3);
1048 i->tag = swf_InsertTag(i->tag,ST_DEFINEBUTTON);
1049 int buttonid1 = getNewID(dev);
1050 swf_SetU16(i->tag, buttonid1);
1051 swf_ButtonSetRecord(i->tag,BS_UP|BS_HIT,ids[0],1,NULL,NULL);
1052 swf_ButtonSetRecord(i->tag,BS_OVER,ids[2],1,NULL,NULL);
1053 swf_ButtonSetRecord(i->tag,BS_DOWN,ids[4],1,NULL,NULL);
1054 swf_SetU8(i->tag,0); // end of button records
1055 swf_ActionSet(i->tag,a1);
1057 i->tag = swf_InsertTag(i->tag,ST_DEFINEBUTTON);
1058 int buttonid2 = getNewID(dev);
1059 swf_SetU16(i->tag, buttonid2);
1060 swf_ButtonSetRecord(i->tag,BS_UP|BS_HIT,ids[1],1,NULL,NULL);
1061 swf_ButtonSetRecord(i->tag,BS_OVER,ids[3],1,NULL,NULL);
1062 swf_ButtonSetRecord(i->tag,BS_DOWN,ids[5],1,NULL,NULL);
1063 swf_SetU8(i->tag,0); // end of button records
1064 swf_ActionSet(i->tag,a2);
1066 i->tag = swf_InsertTag(i->tag,ST_PLACEOBJECT2);
1068 swf_GetMatrix(0, &m);
1069 m.tx = button_sizex*20+200;
1070 swf_ObjectPlace(i->tag, buttonid2, 65534,&m,0,0);
1071 i->tag = swf_InsertTag(i->tag,ST_PLACEOBJECT2);
1072 m.tx = button_sizex*20+200+200;
1073 swf_ObjectPlace(i->tag, buttonid1, 65535,&m,0,0);
1077 void swf_startframe(gfxdevice_t*dev, int width, int height)
1079 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1081 if(i->config_protect) {
1082 i->tag = swf_InsertTag(i->tag, ST_PROTECT);
1083 i->config_protect = 0;
1085 if(i->config_simpleviewer) {
1090 if(!i->firstpage && !i->pagefinished)
1093 msg("<verbose> Starting new SWF page of size %dx%d", width, height);
1095 swf_GetMatrix(0, &i->page_matrix);
1096 i->page_matrix.tx = 0;
1097 i->page_matrix.ty = 0;
1104 /* create a bbox structure with the page size. This is used
1105 for clipping shape and text bounding boxes. As we don't want to
1106 generate bounding boxes which extend beyond the movie size (in
1107 order to not confuse Flash), we clip everything against i->pagebbox */
1108 i->pagebbox.xmin = 0;
1109 i->pagebbox.ymin = 0;
1110 i->pagebbox.xmax = width*20;
1111 i->pagebbox.ymax = height*20;
1113 /* increase SWF's bounding box */
1114 swf_ExpandRect2(&i->swf->movieSize, &i->pagebbox);
1116 i->lastframeno = i->frameno;
1118 i->pagefinished = 0;
1122 void swf_endframe(gfxdevice_t*dev)
1124 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1126 if(!i->pagefinished)
1129 if( (i->swf->fileVersion <= 8) && (i->config_insertstoptag) ) {
1131 atag = action_Stop(atag);
1132 atag = action_End(atag);
1133 i->tag = swf_InsertTag(i->tag,ST_DOACTION);
1134 swf_ActionSet(i->tag,atag);
1136 i->tag = swf_InsertTag(i->tag,ST_SHOWFRAME);
1139 for(i->depth;i->depth>i->startdepth;i->depth--) {
1140 i->tag = swf_InsertTag(i->tag,ST_REMOVEOBJECT2);
1141 swf_SetU16(i->tag,i->depth);
1143 i->depth = i->startdepth;
1145 if(i->config_frameresets) {
1146 for(i->currentswfid;i->currentswfid>i->startids;i->currentswfid--) {
1147 i->tag = swf_InsertTag(i->tag,ST_FREECHARACTER);
1148 swf_SetU16(i->tag,i->currentswfid);
1150 i->currentswfid = i->startids;
1154 static void setBackground(gfxdevice_t*dev, int x1, int y1, int x2, int y2)
1156 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1158 rgb.a = rgb.r = rgb.g = rgb.b = 0xff;
1162 int shapeid = getNewID(dev);
1167 i->tag = swf_InsertTag(i->tag, ST_DEFINESHAPE);
1169 fs1 = swf_ShapeAddSolidFillStyle(s, &rgb);
1170 swf_SetU16(i->tag,shapeid);
1171 swf_SetRect(i->tag,&r);
1172 swf_SetShapeHeader(i->tag,s);
1173 swf_ShapeSetAll(i->tag,s,x1,y1,ls1,fs1,0);
1174 swf_ShapeSetLine(i->tag,s,(x2-x1),0);
1175 swf_ShapeSetLine(i->tag,s,0,(y2-y1));
1176 swf_ShapeSetLine(i->tag,s,(x1-x2),0);
1177 swf_ShapeSetLine(i->tag,s,0,(y1-y2));
1178 swf_ShapeSetEnd(i->tag);
1180 i->tag = swf_InsertTag(i->tag, ST_PLACEOBJECT2);
1181 swf_ObjectPlace(i->tag,shapeid,getNewDepth(dev),0,0,0);
1182 i->tag = swf_InsertTag(i->tag, ST_PLACEOBJECT2);
1183 swf_ObjectPlaceClip(i->tag,shapeid,getNewDepth(dev),0,0,0,65535);
1186 /* initialize the swf writer */
1187 void gfxdevice_swf_init(gfxdevice_t* dev)
1189 memset(dev, 0, sizeof(gfxdevice_t));
1193 dev->internal = init_internal_struct(); // set config to default values
1195 dev->startpage = swf_startframe;
1196 dev->endpage = swf_endframe;
1197 dev->finish = swf_finish;
1198 dev->fillbitmap = swf_fillbitmap;
1199 dev->setparameter = swf_setparameter;
1200 dev->stroke = swf_stroke;
1201 dev->startclip = swf_startclip;
1202 dev->endclip = swf_endclip;
1203 dev->fill = swf_fill;
1204 dev->fillbitmap = swf_fillbitmap;
1205 dev->fillgradient = swf_fillgradient;
1206 dev->addfont = swf_addfont;
1207 dev->drawchar = swf_drawchar;
1208 dev->drawlink = swf_drawlink;
1210 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1213 msg("<verbose> initializing swf output\n", i->max_x,i->max_y);
1217 i->swf = (SWF*)rfx_calloc(sizeof(SWF));
1218 i->swf->fileVersion = 0;
1219 i->swf->frameRate = 0x80;
1220 i->swf->movieSize.xmin = 0;
1221 i->swf->movieSize.ymin = 0;
1222 i->swf->movieSize.xmax = 0;
1223 i->swf->movieSize.ymax = 0;
1224 i->swf->fileAttributes = 9; // as3, local-with-network
1226 i->swf->firstTag = swf_InsertTag(NULL,ST_SETBACKGROUNDCOLOR);
1227 i->tag = i->swf->firstTag;
1229 rgb.a = rgb.r = rgb.g = rgb.b = 0xff;
1231 swf_SetRGB(i->tag,&rgb);
1233 i->startdepth = i->depth = 0;
1234 i->startids = i->currentswfid = 0;
1237 static void startshape(gfxdevice_t*dev)
1239 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1244 //if(i->chardatapos && i->chardata[i->chardatapos-1].color.a)
1247 i->tag = swf_InsertTag(i->tag,ST_DEFINESHAPE3);
1249 swf_ShapeNew(&i->shape);
1250 i->linestyleid = swf_ShapeAddLineStyle(i->shape,i->linewidth,&i->strokergb);
1251 i->fillstyleid = swf_ShapeAddSolidFillStyle(i->shape,&i->fillrgb);
1253 RGBA markcol = {0,i->mark[0],i->mark[1],i->mark[2]};
1254 swf_ShapeAddSolidFillStyle(i->shape,&markcol);
1257 i->shapeid = getNewID(dev);
1259 msg("<debug> Using shape id %d", i->shapeid);
1261 swf_SetU16(i->tag,i->shapeid); // ID
1263 i->bboxrectpos = i->tag->len;
1265 swf_SetRect(i->tag,&i->pagebbox);
1267 memset(&i->bboxrect, 0, sizeof(i->bboxrect));
1269 swf_SetShapeStyles(i->tag,i->shape);
1270 swf_ShapeCountBits(i->shape,NULL,NULL);
1271 swf_SetShapeBits(i->tag,i->shape);
1273 /* TODO: do we really need this? */
1274 //swf_ShapeSetAll(i->tag,i->shape,/*x*/0,/*y*/0,i->linestyleid,0,0);
1275 //swf_ShapeSetAll(i->tag,i->shape,/*x*/UNDEFINED_COORD,/*y*/UNDEFINED_COORD,i->linestyleid,0,0);
1276 i->swflastx=i->swflasty=UNDEFINED_COORD;
1277 i->lastwasfill = -1;
1278 i->shapeisempty = 1;
1281 static void starttext(gfxdevice_t*dev)
1283 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1287 if(i->config_watermark) {
1288 insert_watermark(dev, 0);
1291 i->swflastx=i->swflasty=0;
1295 /* TODO: move to ../lib/rfxswf */
1296 void changeRect(gfxdevice_t*dev, TAG*tag, int pos, SRECT*newrect)
1298 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1299 /* determine length of old rect */
1303 swf_GetRect(tag, &old);
1304 swf_ResetReadBits(tag);
1305 int pos_end = tag->pos;
1307 int len = tag->len - pos_end;
1308 U8*data = (U8*)malloc(len);
1309 memcpy(data, &tag->data[pos_end], len);
1312 swf_SetRect(tag, newrect);
1313 swf_SetBlock(tag, data, len);
1315 tag->pos = tag->readBit = 0;
1318 void cancelshape(gfxdevice_t*dev)
1320 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1321 /* delete old shape tag */
1323 i->tag = i->tag->prev;
1324 swf_DeleteTag(0, todel);
1325 if(i->shape) {swf_ShapeFree(i->shape);i->shape=0;}
1327 i->bboxrectpos = -1;
1329 // i->currentswfid--; // doesn't work, for some reason
1332 void fixAreas(gfxdevice_t*dev)
1334 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1335 if(!i->shapeisempty && i->fill &&
1336 (i->bboxrect.xmin == i->bboxrect.xmax ||
1337 i->bboxrect.ymin == i->bboxrect.ymax) &&
1338 i->config_minlinewidth >= 0.001
1340 msg("<debug> Shape has size 0: width=%.2f height=%.2f",
1341 (i->bboxrect.xmax-i->bboxrect.xmin)/20.0,
1342 (i->bboxrect.ymax-i->bboxrect.ymin)/20.0
1345 SRECT r = i->bboxrect;
1347 if(r.xmin == r.xmax && r.ymin == r.ymax) {
1348 /* this thing comes down to a single dot- nothing to fix here */
1354 RGBA save_col = i->strokergb;
1355 int save_width = i->linewidth;
1357 i->strokergb = i->fillrgb;
1358 i->linewidth = (int)(i->config_minlinewidth*20);
1359 if(i->linewidth==0) i->linewidth = 1;
1364 moveto(dev, i->tag, r.xmin/20.0,r.ymin/20.0);
1365 lineto(dev, i->tag, r.xmax/20.0,r.ymax/20.0);
1367 i->strokergb = save_col;
1368 i->linewidth = save_width;
1373 static void endshape_noput(gfxdevice_t*dev)
1375 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1378 //changeRect(dev, i->tag, i->bboxrectpos, &i->bboxrect);
1381 swf_ShapeFree(i->shape);
1389 static void endshape(gfxdevice_t*dev)
1391 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1397 if(i->shapeisempty ||
1399 (i->bboxrect.xmin == i->bboxrect.xmax &&
1400 i->bboxrect.ymin == i->bboxrect.ymax))
1402 // delete the shape again, we didn't do anything
1403 msg("<debug> cancelling shape: bbox is (%f,%f,%f,%f)",
1404 i->bboxrect.xmin /20.0,
1405 i->bboxrect.ymin /20.0,
1406 i->bboxrect.xmax /20.0,
1407 i->bboxrect.ymax /20.0
1413 swf_ShapeSetEnd(i->tag);
1415 SRECT r = swf_ClipRect(i->pagebbox, i->bboxrect);
1416 changeRect(dev, i->tag, i->bboxrectpos, &r);
1418 msg("<trace> Placing shape ID %d", i->shapeid);
1420 i->tag = swf_InsertTag(i->tag,ST_PLACEOBJECT2);
1421 MATRIX m = i->page_matrix;
1422 m.tx += i->shapeposx;
1423 m.ty += i->shapeposy;
1424 swf_ObjectPlace(i->tag,i->shapeid,getNewDepth(dev),&m,NULL,NULL);
1426 if(i->config_animate) {
1427 i->tag = swf_InsertTag(i->tag,ST_SHOWFRAME);
1430 swf_ShapeFree(i->shape);
1433 i->bboxrectpos = -1;
1440 void wipeSWF(SWF*swf)
1442 TAG*tag = swf->firstTag;
1444 TAG*next = tag->next;
1445 if(tag->id != ST_SETBACKGROUNDCOLOR &&
1446 tag->id != ST_END &&
1447 tag->id != ST_DOACTION &&
1448 tag->id != ST_SHOWFRAME) {
1449 swf_DeleteTag(swf, tag);
1455 void swfoutput_finalize(gfxdevice_t*dev)
1457 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1459 if(i->tag && i->tag->id == ST_END)
1460 return; //already done
1462 i->swf->fileVersion = i->config_flashversion;
1463 i->swf->frameRate = i->config_framerate*0x100;
1465 if(i->config_bboxvars) {
1466 TAG* tag = swf_InsertTag(i->swf->firstTag, ST_DOACTION);
1468 a = action_PushString(a, "xmin");
1469 a = action_PushFloat(a, i->swf->movieSize.xmin / 20.0);
1470 a = action_SetVariable(a);
1471 a = action_PushString(a, "ymin");
1472 a = action_PushFloat(a, i->swf->movieSize.ymin / 20.0);
1473 a = action_SetVariable(a);
1474 a = action_PushString(a, "xmax");
1475 a = action_PushFloat(a, i->swf->movieSize.xmax / 20.0);
1476 a = action_SetVariable(a);
1477 a = action_PushString(a, "ymax");
1478 a = action_PushFloat(a, i->swf->movieSize.ymax / 20.0);
1479 a = action_SetVariable(a);
1480 a = action_PushString(a, "width");
1481 a = action_PushFloat(a, (i->swf->movieSize.xmax - i->swf->movieSize.xmin) / 20.0);
1482 a = action_SetVariable(a);
1483 a = action_PushString(a, "height");
1484 a = action_PushFloat(a, (i->swf->movieSize.ymax - i->swf->movieSize.ymin) / 20.0);
1485 a = action_SetVariable(a);
1487 swf_ActionSet(tag, a);
1492 free(i->mark);i->mark = 0;
1496 fontlist_t *iterator = i->fontlist;
1498 TAG*mtag = i->swf->firstTag;
1499 if(iterator->swffont) {
1500 if(!i->config_storeallcharacters) {
1501 msg("<debug> Reducing font %s", iterator->swffont->name);
1502 swf_FontReduce(iterator->swffont);
1504 int used = iterator->swffont->use && iterator->swffont->use->used_glyphs;
1506 mtag = swf_InsertTag(mtag, ST_DEFINEFONT2);
1507 swf_FontSetDefine2(mtag, iterator->swffont);
1511 iterator = iterator->next;
1514 i->tag = swf_InsertTag(i->tag,ST_END);
1515 TAG* tag = i->tag->prev;
1517 /* remove the removeobject2 tags between the last ST_SHOWFRAME
1518 and the ST_END- they confuse the flash player */
1519 while(tag->id == ST_REMOVEOBJECT2) {
1520 TAG* prev = tag->prev;
1521 swf_DeleteTag(i->swf, tag);
1528 if(i->config_enablezlib || i->config_flashversion>=6) {
1529 i->swf->compressed = 1;
1532 /* Add AVM2 actionscript */
1533 if(i->config_flashversion>=9 &&
1534 (i->config_insertstoptag || i->hasbuttons)) {
1535 swf_AddButtonLinks(i->swf, i->config_insertstoptag,
1536 i->config_internallinkfunction||i->config_externallinkfunction);
1538 // if(i->config_reordertags)
1539 // swf_Optimize(i->swf);
1542 int swfresult_save(gfxresult_t*gfx, const char*filename)
1544 SWF*swf = (SWF*)gfx->internal;
1547 fi = open(filename, O_BINARY|O_CREAT|O_TRUNC|O_WRONLY, 0777);
1552 msg("<fatal> Could not create \"%s\". ", FIXNULL(filename));
1556 if FAILED(swf_WriteSWF(fi,swf))
1557 msg("<error> WriteSWF() failed.\n");
1563 void* swfresult_get(gfxresult_t*gfx, const char*name)
1565 SWF*swf = (SWF*)gfx->internal;
1566 if(!strcmp(name, "swf")) {
1567 return (void*)swf_CopySWF(swf);
1568 } else if(!strcmp(name, "xmin")) {
1569 return (void*)(ptroff_t)(swf->movieSize.xmin/20);
1570 } else if(!strcmp(name, "ymin")) {
1571 return (void*)(ptroff_t)(swf->movieSize.ymin/20);
1572 } else if(!strcmp(name, "xmax")) {
1573 return (void*)(ptroff_t)(swf->movieSize.xmax/20);
1574 } else if(!strcmp(name, "ymax")) {
1575 return (void*)(ptroff_t)(swf->movieSize.ymax/20);
1576 } else if(!strcmp(name, "width")) {
1577 return (void*)(ptroff_t)((swf->movieSize.xmax - swf->movieSize.xmin)/20);
1578 } else if(!strcmp(name, "height")) {
1579 return (void*)(ptroff_t)((swf->movieSize.ymax - swf->movieSize.ymin)/20);
1583 void swfresult_destroy(gfxresult_t*gfx)
1586 swf_FreeTags((SWF*)gfx->internal);
1587 free(gfx->internal);
1590 memset(gfx, 0, sizeof(gfxresult_t));
1594 static void swfoutput_destroy(gfxdevice_t* dev);
1596 gfxresult_t* swf_finish(gfxdevice_t* dev)
1598 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1601 if(i->config_linktarget) {
1602 free(i->config_linktarget);
1603 i->config_linktarget = 0;
1606 swfoutput_finalize(dev);
1607 SWF* swf = i->swf;i->swf = 0;
1608 swfoutput_destroy(dev);
1610 result = (gfxresult_t*)rfx_calloc(sizeof(gfxresult_t));
1611 result->internal = swf;
1612 result->save = swfresult_save;
1614 result->get = swfresult_get;
1615 result->destroy = swfresult_destroy;
1619 /* Perform cleaning up */
1620 static void swfoutput_destroy(gfxdevice_t* dev)
1622 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1624 /* not initialized yet- nothing to destroy */
1628 fontlist_t *tmp,*iterator = i->fontlist;
1630 if(iterator->swffont) {
1631 swf_FontFree(iterator->swffont);iterator->swffont=0;
1634 iterator = iterator->next;
1637 if(i->swf) {swf_FreeTags(i->swf);free(i->swf);i->swf = 0;}
1640 memset(dev, 0, sizeof(gfxdevice_t));
1643 static void swfoutput_setstrokecolor(gfxdevice_t* dev, U8 r, U8 g, U8 b, U8 a)
1645 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1646 if(i->strokergb.r == r &&
1647 i->strokergb.g == g &&
1648 i->strokergb.b == b &&
1649 i->strokergb.a == a) return;
1659 //#define ROUND_UP 19
1660 //#define ROUND_UP 10
1662 static void swfoutput_setlinewidth(gfxdevice_t*dev, double _linewidth)
1664 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1665 if(i->linewidth == (U16)(_linewidth*20+19.0/20.0))
1669 i->linewidth = (U16)(_linewidth*20+19.0/20.0);
1673 static void drawlink(gfxdevice_t*dev, ActionTAG*,ActionTAG*, gfxline_t*points, char mouseover, const char*url);
1674 static void swfoutput_namedlink(gfxdevice_t*dev, char*name, gfxline_t*points);
1675 static void swfoutput_linktopage(gfxdevice_t*dev, int page, gfxline_t*points);
1676 static void swfoutput_linktourl(gfxdevice_t*dev, const char*url, gfxline_t*points);
1678 /*void swfoutput_drawlink(gfxdevice_t*dev, char*url, gfxline_t*points)
1680 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1681 dev->drawlink(dev, points, url);
1684 void swf_drawlink(gfxdevice_t*dev, gfxline_t*points, const char*url)
1686 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1688 if(i->config_disablelinks)
1691 if(!strncmp("http://pdf2swf:", url, 15)) {
1692 char*tmp = strdup(url);
1693 int l = strlen(tmp);
1696 swfoutput_namedlink(dev, tmp+15, points);
1699 } else if(!strncmp("page", url, 4)) {
1702 if(url[t]<'0' || url[t]>'9')
1705 int page = atoi(&url[4]);
1706 if(page<0) page = 0;
1707 swfoutput_linktopage(dev, page, points);
1710 swfoutput_linktourl(dev, url, points);
1713 void swfoutput_linktourl(gfxdevice_t*dev, const char*url, gfxline_t*points)
1715 ActionTAG* actions = 0;
1716 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1722 /* TODO: escape special characters in url */
1724 if(i->config_externallinkfunction && i->config_flashversion<=8) {
1725 actions = action_PushString(actions, url); //parameter
1726 actions = action_PushInt(actions, 1); //number of parameters (1)
1727 actions = action_PushString(actions, i->config_externallinkfunction); //function name
1728 actions = action_CallFunction(actions);
1729 } else if(!i->config_linktarget) {
1730 if(!i->config_opennewwindow)
1731 actions = action_GetUrl(actions, url, "_parent");
1733 actions = action_GetUrl(actions, url, "_this");
1735 actions = action_GetUrl(actions, url, i->config_linktarget);
1737 actions = action_End(actions);
1739 drawlink(dev, actions, 0, points, 0, url);
1741 swf_ActionFree(actions);
1743 void swfoutput_linktopage(gfxdevice_t*dev, int page, gfxline_t*points)
1745 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1746 ActionTAG* actions = 0;
1753 if(!i->config_internallinkfunction || i->config_flashversion>=9) {
1754 actions = action_GotoFrame(actions, page-1);
1755 actions = action_End(actions);
1757 actions = action_PushInt(actions, page); //parameter
1758 actions = action_PushInt(actions, 1); //number of parameters (1)
1759 actions = action_PushString(actions, i->config_internallinkfunction); //function name
1760 actions = action_CallFunction(actions);
1761 actions = action_End(actions);
1765 sprintf(name, "page%d", page);
1767 drawlink(dev, actions, 0, points, 0, name);
1769 swf_ActionFree(actions);
1772 /* Named Links (a.k.a. Acrobatmenu) are used to implement various gadgets
1773 of the viewer objects, like subtitles, index elements etc.
1775 void swfoutput_namedlink(gfxdevice_t*dev, char*name, gfxline_t*points)
1777 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1778 ActionTAG *actions1,*actions2;
1779 char*tmp = strdup(name);
1787 if(!strncmp(tmp, "call:", 5))
1789 char*x = strchr(&tmp[5], ':');
1791 actions1 = action_PushInt(0, 0); //number of parameters (0)
1792 actions1 = action_PushString(actions1, &tmp[5]); //function name
1793 actions1 = action_CallFunction(actions1);
1794 actions1 = action_End(actions1);
1797 actions1 = action_PushString(0, x+1); //parameter
1798 actions1 = action_PushInt(actions1, 1); //number of parameters (1)
1799 actions1 = action_PushString(actions1, &tmp[5]); //function name
1800 actions1 = action_CallFunction(actions1);
1801 actions1 = action_End(actions1);
1803 actions2 = action_End(0);
1808 actions1 = action_PushString(0, "/:subtitle");
1809 actions1 = action_PushString(actions1, name);
1810 actions1 = action_SetVariable(actions1);
1811 actions1 = action_End(actions1);
1813 actions2 = action_PushString(0, "/:subtitle");
1814 actions2 = action_PushString(actions2, "");
1815 actions2 = action_SetVariable(actions2);
1816 actions2 = action_End(actions2);
1819 drawlink(dev, actions1, actions2, points, mouseover, name);
1821 swf_ActionFree(actions1);
1822 swf_ActionFree(actions2);
1826 static void drawgfxline(gfxdevice_t*dev, gfxline_t*line, int fill)
1828 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1829 gfxcoord_t lastx=0,lasty=0,px=0,py=0;
1831 int lines= 0, splines=0;
1838 /* check whether the next segment is zero */
1839 if(line->type == gfx_moveTo) {
1840 moveto(dev, i->tag, line->x, line->y);
1841 px = lastx = line->x;
1842 py = lasty = line->y;
1844 } if(line->type == gfx_lineTo) {
1845 lineto(dev, i->tag, line->x, line->y);
1850 } else if(line->type == gfx_splineTo) {
1852 s.x = line->sx;p.x = line->x;
1853 s.y = line->sy;p.y = line->y;
1854 splineto(dev, i->tag, s, p);
1862 msg("<trace> drawgfxline, %d lines, %d splines", lines, splines);
1866 static void drawlink(gfxdevice_t*dev, ActionTAG*actions1, ActionTAG*actions2, gfxline_t*points, char mouseover, const char*url)
1868 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1877 int buttonid = getNewID(dev);
1878 gfxbbox_t bbox = gfxline_getbbox(points);
1883 myshapeid = getNewID(dev);
1884 i->tag = swf_InsertTag(i->tag,ST_DEFINESHAPE3);
1885 swf_ShapeNew(&i->shape);
1886 rgb.r = rgb.b = rgb.a = rgb.g = 0;
1887 fsid = swf_ShapeAddSolidFillStyle(i->shape,&rgb);
1888 swf_SetU16(i->tag, myshapeid);
1889 r.xmin = (int)(bbox.xmin*20);
1890 r.ymin = (int)(bbox.ymin*20);
1891 r.xmax = (int)(bbox.xmax*20);
1892 r.ymax = (int)(bbox.ymax*20);
1893 r = swf_ClipRect(i->pagebbox, r);
1894 swf_SetRect(i->tag,&r);
1895 swf_SetShapeStyles(i->tag,i->shape);
1896 swf_ShapeCountBits(i->shape,NULL,NULL);
1897 swf_SetShapeBits(i->tag,i->shape);
1898 swf_ShapeSetAll(i->tag,i->shape,/*x*/0,/*y*/0,0,fsid,0);
1899 i->swflastx = i->swflasty = 0;
1900 drawgfxline(dev, points, 1);
1901 swf_ShapeSetEnd(i->tag);
1902 swf_ShapeFree(i->shape);
1905 myshapeid2 = getNewID(dev);
1906 i->tag = swf_InsertTag(i->tag,ST_DEFINESHAPE3);
1907 swf_ShapeNew(&i->shape);
1909 rgb = i->config_linkcolor;
1911 fsid = swf_ShapeAddSolidFillStyle(i->shape,&rgb);
1912 swf_SetU16(i->tag, myshapeid2);
1913 r.xmin = (int)(bbox.xmin*20);
1914 r.ymin = (int)(bbox.ymin*20);
1915 r.xmax = (int)(bbox.xmax*20);
1916 r.ymax = (int)(bbox.ymax*20);
1917 r = swf_ClipRect(i->pagebbox, r);
1918 swf_SetRect(i->tag,&r);
1919 swf_SetShapeStyles(i->tag,i->shape);
1920 swf_ShapeCountBits(i->shape,NULL,NULL);
1921 swf_SetShapeBits(i->tag,i->shape);
1922 swf_ShapeSetAll(i->tag,i->shape,/*x*/0,/*y*/0,0,fsid,0);
1923 i->swflastx = i->swflasty = 0;
1924 drawgfxline(dev, points, 1);
1925 swf_ShapeSetEnd(i->tag);
1926 swf_ShapeFree(i->shape);
1930 i->tag = swf_InsertTag(i->tag,ST_DEFINEBUTTON);
1931 swf_SetU16(i->tag,buttonid); //id
1932 swf_ButtonSetFlags(i->tag, 0); //menu=no
1933 swf_ButtonSetRecord(i->tag,0x01,myshapeid,i->depth,0,0);
1934 swf_ButtonSetRecord(i->tag,0x02,myshapeid2,i->depth,0,0);
1935 swf_ButtonSetRecord(i->tag,0x04,myshapeid2,i->depth,0,0);
1936 swf_ButtonSetRecord(i->tag,0x08,myshapeid,i->depth,0,0);
1937 swf_SetU8(i->tag,0);
1938 swf_ActionSet(i->tag,actions1);
1939 swf_SetU8(i->tag,0);
1943 i->tag = swf_InsertTag(i->tag,ST_DEFINEBUTTON2);
1944 swf_SetU16(i->tag,buttonid); //id
1945 swf_ButtonSetFlags(i->tag, 0); //menu=no
1946 swf_ButtonSetRecord(i->tag,0x01,myshapeid,i->depth,0,0);
1947 swf_ButtonSetRecord(i->tag,0x02,myshapeid2,i->depth,0,0);
1948 swf_ButtonSetRecord(i->tag,0x04,myshapeid2,i->depth,0,0);
1949 swf_ButtonSetRecord(i->tag,0x08,myshapeid,i->depth,0,0);
1950 swf_SetU8(i->tag,0); // end of button records
1951 swf_ButtonSetCondition(i->tag, BC_IDLE_OVERUP);
1952 swf_ActionSet(i->tag,actions1);
1954 swf_ButtonSetCondition(i->tag, BC_OVERUP_IDLE);
1955 swf_ActionSet(i->tag,actions2);
1956 swf_SetU8(i->tag,0);
1957 swf_ButtonPostProcess(i->tag, 2);
1959 swf_SetU8(i->tag,0);
1960 swf_ButtonPostProcess(i->tag, 1);
1964 const char* name = 0;
1965 if(i->config_linknameurl) {
1969 sprintf(buf, "button%d", buttonid);
1972 msg("<trace> Placing link ID %d", buttonid);
1973 i->tag = swf_InsertTag(i->tag,ST_PLACEOBJECT2);
1975 if(posx!=0 || posy!=0) {
1977 p.x = (int)(posx*20);
1978 p.y = (int)(posy*20);
1979 p = swf_TurnPoint(p, &i->page_matrix);
1984 swf_ObjectPlace(i->tag, buttonid, getNewDepth(dev),&m,0,(U8*)name);
1986 swf_ObjectPlace(i->tag, buttonid, getNewDepth(dev),&i->page_matrix,0,(U8*)name);
1993 for(t=0;t<picpos;t++)
1995 if(pic_xids[t] == xid &&
1996 pic_yids[t] == yid) {
1997 width = pic_width[t];
1998 height = pic_height[t];
2002 pic_ids[picpos] = swfoutput_drawimagelosslessN(&output, pic, pal, width, height, x1,y1,x2,y2,x3,y3,x4,y4, numpalette);
2003 pic_xids[picpos] = xid;
2004 pic_yids[picpos] = yid;
2005 pic_width[picpos] = width;
2006 pic_height[picpos] = height;
2009 pic[width*y+x] = buf[0];
2013 xid += pal[1].r*3 + pal[1].g*11 + pal[1].b*17;
2014 yid += pal[1].r*7 + pal[1].g*5 + pal[1].b*23;
2018 xid += x*r+x*b*3+x*g*7+x*a*11;
2019 yid += y*r*3+y*b*17+y*g*19+y*a*11;
2021 for(t=0;t<picpos;t++)
2023 if(pic_xids[t] == xid &&
2024 pic_yids[t] == yid) {
2033 int swf_setparameter(gfxdevice_t*dev, const char*name, const char*value)
2035 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
2037 msg("<trace> swfdevice: %s=%s", name, value);
2038 if(!strcmp(name, "jpegsubpixels")) {
2039 i->config_jpegsubpixels = atof(value);
2040 } else if(!strcmp(name, "ppmsubpixels")) {
2041 i->config_ppmsubpixels = atof(value);
2042 } else if(!strcmp(name, "subpixels")) {
2043 i->config_ppmsubpixels = i->config_jpegsubpixels = atof(value);
2044 } else if(!strcmp(name, "drawonlyshapes")) {
2045 i->config_drawonlyshapes = atoi(value);
2046 } else if(!strcmp(name, "ignoredraworder")) {
2047 i->config_ignoredraworder = atoi(value);
2048 } else if(!strcmp(name, "mark")) {
2049 if(!value || !value[0]) {
2050 if(i->mark) free(i->mark);
2054 i->mark = strdup("...");
2055 for(t=0;t<3;t++) if(value[t]) i->mark[t] = value[t];
2057 } else if(!strcmp(name, "filloverlap")) {
2058 i->config_filloverlap = atoi(value);
2059 } else if(!strcmp(name, "linksopennewwindow")) {
2060 i->config_opennewwindow = atoi(value);
2061 } else if(!strcmp(name, "opennewwindow")) {
2062 i->config_opennewwindow = atoi(value);
2063 } else if(!strcmp(name, "storeallcharacters")) {
2064 i->config_storeallcharacters = atoi(value);
2065 } else if(!strcmp(name, "enablezlib")) {
2066 i->config_enablezlib = atoi(value);
2067 } else if(!strcmp(name, "bboxvars")) {
2068 i->config_bboxvars = atoi(value);
2069 } else if(!strcmp(name, "dots")) {
2070 i->config_dots = atoi(value);
2071 } else if(!strcmp(name, "frameresets")) {
2072 i->config_frameresets = atoi(value);
2073 } else if(!strcmp(name, "showclipshapes")) {
2074 i->config_showclipshapes = atoi(value);
2075 } else if(!strcmp(name, "reordertags")) {
2076 i->config_reordertags = atoi(value);
2077 } else if(!strcmp(name, "internallinkfunction")) {
2078 i->config_internallinkfunction = strdup(value);
2079 } else if(!strcmp(name, "externallinkfunction")) {
2080 i->config_externallinkfunction = strdup(value);
2081 } else if(!strcmp(name, "linkfunction")) { //sets both internallinkfunction and externallinkfunction
2082 i->config_internallinkfunction = strdup(value);
2083 i->config_externallinkfunction = strdup(value);
2084 } else if(!strcmp(name, "disable_polygon_conversion")) {
2085 i->config_disable_polygon_conversion = atoi(value);
2086 } else if(!strcmp(name, "normalize_polygon_positions")) {
2087 i->config_normalize_polygon_positions = atoi(value);
2088 } else if(!strcmp(name, "wxwindowparams")) {
2089 i->config_watermark = atoi(value);
2090 } else if(!strcmp(name, "insertstop")) {
2091 i->config_insertstoptag = atoi(value);
2092 } else if(!strcmp(name, "protect")) {
2093 i->config_protect = atoi(value);
2094 if(i->config_protect && i->tag) {
2095 i->tag = swf_InsertTag(i->tag, ST_PROTECT);
2097 } else if(!strcmp(name, "flashversion")) {
2098 i->config_flashversion = atoi(value);
2100 i->swf->fileVersion = i->config_flashversion;
2102 } else if(!strcmp(name, "framerate")) {
2103 i->config_framerate = atof(value);
2105 i->swf->frameRate = i->config_framerate*0x100;
2107 } else if(!strcmp(name, "minlinewidth")) {
2108 i->config_minlinewidth = atof(value);
2109 } else if(!strcmp(name, "caplinewidth")) {
2110 i->config_caplinewidth = atof(value);
2111 } else if(!strcmp(name, "linktarget")) {
2112 i->config_linktarget = strdup(value);
2113 } else if(!strcmp(name, "invisibletexttofront")) {
2114 i->config_invisibletexttofront = atoi(value);
2115 } else if(!strcmp(name, "noclips")) {
2116 i->config_noclips = atoi(value);
2117 } else if(!strcmp(name, "dumpfonts")) {
2118 i->config_dumpfonts = atoi(value);
2119 } else if(!strcmp(name, "animate")) {
2120 i->config_animate = atoi(value);
2121 } else if(!strcmp(name, "disablelinks")) {
2122 i->config_disablelinks = atoi(value);
2123 } else if(!strcmp(name, "simpleviewer")) {
2124 i->config_simpleviewer = atoi(value);
2125 } else if(!strcmp(name, "next_bitmap_is_jpeg")) {
2127 } else if(!strcmp(name, "jpegquality")) {
2128 int val = atoi(value);
2130 if(val>101) val=101;
2131 i->config_jpegquality = val;
2132 } else if(!strcmp(name, "splinequality")) {
2133 int v = atoi(value);
2134 v = 500-(v*5); // 100% = 0.25 pixel, 0% = 25 pixel
2136 i->config_splinemaxerror = v;
2137 } else if(!strcmp(name, "fontquality")) {
2138 int v = atoi(value);
2139 v = 500-(v*5); // 100% = 0.25 pixel, 0% = 25 pixel
2141 i->config_fontsplinemaxerror = v;
2142 } else if(!strcmp(name, "linkcolor")) {
2143 if(strlen(value)!=8) {
2144 fprintf(stderr, "Unknown format for option 'linkcolor'. (%s <-> RRGGBBAA)\n", value);
2147 # define NIBBLE(s) (((s)>='0' && (s)<='9')?((s)-'0'):((s)&0x0f)+9)
2148 i->config_linkcolor.r = NIBBLE(value[0])<<4 | NIBBLE(value[1]);
2149 i->config_linkcolor.g = NIBBLE(value[2])<<4 | NIBBLE(value[3]);
2150 i->config_linkcolor.b = NIBBLE(value[4])<<4 | NIBBLE(value[5]);
2151 i->config_linkcolor.a = NIBBLE(value[6])<<4 | NIBBLE(value[7]);
2152 } else if(!strcmp(name, "help")) {
2153 printf("\nSWF layer options:\n");
2154 printf("jpegsubpixels=<pixels> resolution adjustment for jpeg images (same as jpegdpi, but in pixels)\n");
2155 printf("ppmsubpixels=<pixels resolution adjustment for lossless images (same as ppmdpi, but in pixels)\n");
2156 printf("subpixels=<pixels> shortcut for setting both jpegsubpixels and ppmsubpixels\n");
2157 printf("drawonlyshapes convert everything to shapes (currently broken)\n");
2158 printf("ignoredraworder allow to perform a few optimizations for creating smaller SWFs\n");
2159 printf("linksopennewwindow make links open a new browser window\n");
2160 printf("linktarget target window name of new links\n");
2161 printf("linkcolor=<color) color of links (format: RRGGBBAA)\n");
2162 printf("linknameurl Link buttons will be named like the URL they refer to (handy for iterating through links with actionscript)\n");
2163 printf("storeallcharacters don't reduce the fonts to used characters in the output file\n");
2164 printf("enablezlib switch on zlib compression (also done if flashversion>=7)\n");
2165 printf("bboxvars store the bounding box of the SWF file in actionscript variables\n");
2166 printf("dots Take care to handle dots correctly\n");
2167 printf("reordertags=0/1 (default: 1) perform some tag optimizations\n");
2168 printf("internallinkfunction=<name> when the user clicks a internal link (to a different page) in the converted file, this actionscript function is called\n");
2169 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");
2170 printf("disable_polygon_conversion never convert strokes to polygons (will remove capstyles and joint styles)\n");
2171 printf("caplinewidth=<width> the minimum thichness a line needs to have so that capstyles become visible (and are converted)\n");
2172 printf("insertstop put an ActionScript \"STOP\" tag in every frame\n");
2173 printf("protect add a \"protect\" tag to the file, to prevent loading in the Flash editor\n");
2174 printf("flashversion=<version> the SWF fileversion (6)\n");
2175 printf("framerate=<fps> SWF framerate\n");
2176 printf("minlinewidth=<width> convert horizontal/vertical boxes smaller than this width to lines (0.05) \n");
2177 printf("simpleviewer Add next/previous buttons to the SWF\n");
2178 printf("animate insert a showframe tag after each placeobject (animate draw order of PDF files)\n");
2179 printf("jpegquality=<quality> set compression quality of jpeg images\n");
2180 printf("splinequality=<value> Set the quality of spline convertion to value (0-100, default: 100).\n");
2181 printf("disablelinks Disable links.\n");
2188 // --------------------------------------------------------------------
2190 static CXFORM gfxcxform_to_cxform(gfxcxform_t* c)
2193 swf_GetCXForm(0, &cx, 1);
2196 if(c->rg!=0 || c->rb!=0 || c->ra!=0 ||
2197 c->gr!=0 || c->gb!=0 || c->ga!=0 ||
2198 c->br!=0 || c->bg!=0 || c->ba!=0 ||
2199 c->ar!=0 || c->ag!=0 || c->ab!=0)
2200 msg("<warning> CXForm not SWF-compatible");
2202 cx.a0 = (S16)(c->aa*256);
2203 cx.r0 = (S16)(c->rr*256);
2204 cx.g0 = (S16)(c->gg*256);
2205 cx.b0 = (S16)(c->bb*256);
2214 static int imageInCache(gfxdevice_t*dev, void*data, int width, int height)
2218 static void addImageToCache(gfxdevice_t*dev, void*data, int width, int height)
2222 static int add_image(swfoutput_internal*i, gfximage_t*img, int targetwidth, int targetheight, int* newwidth, int* newheight)
2224 gfxdevice_t*dev = i->dev;
2226 RGBA*mem = (RGBA*)img->data;
2228 int sizex = img->width;
2229 int sizey = img->height;
2230 int is_jpeg = i->jpeg;
2233 int newsizex=sizex, newsizey=sizey;
2236 if(is_jpeg && i->config_jpegsubpixels) {
2237 newsizex = (int)(targetwidth*i->config_jpegsubpixels + 0.5);
2238 newsizey = (int)(targetheight*i->config_jpegsubpixels + 0.5);
2239 } else if(!is_jpeg && i->config_ppmsubpixels) {
2240 newsizex = (int)(targetwidth*i->config_ppmsubpixels + 0.5);
2241 newsizey = (int)(targetheight*i->config_ppmsubpixels + 0.5);
2245 if(sizex<=0 || sizey<=0)
2252 /* TODO: cache images */
2254 if(newsizex<sizex || newsizey<sizey) {
2255 msg("<verbose> Scaling %dx%d image to %dx%d", sizex, sizey, newsizex, newsizey);
2256 newpic = swf_ImageScale(mem, sizex, sizey, newsizex, newsizey);
2257 *newwidth = sizex = newsizex;
2258 *newheight = sizey = newsizey;
2261 *newwidth = newsizex = sizex;
2262 *newheight = newsizey = sizey;
2265 int num_colors = swf_ImageGetNumberOfPaletteEntries(mem,sizex,sizey,0);
2266 int has_alpha = swf_ImageHasAlpha(mem,sizex,sizey);
2268 msg("<verbose> Drawing %dx%d %s%simage (id %d) at size %dx%d (%dx%d), %s%d colors",
2270 has_alpha?(has_alpha==2?"semi-transparent ":"transparent "):"",
2271 is_jpeg?"jpeg-":"", i->currentswfid+1,
2273 targetwidth, targetheight,
2274 /*newsizex, newsizey,*/
2275 num_colors>256?">":"", num_colors>256?256:num_colors);
2277 /*RGBA* pal = (RGBA*)rfx_alloc(sizeof(RGBA)*num_colors);
2278 swf_ImageGetNumberOfPaletteEntries(mem,sizex,sizey,pal);
2280 for(t=0;t<num_colors;t++) {
2281 printf("%02x%02x%02x%02x ",
2282 pal[t].r, pal[t].g, pal[t].b, pal[t].a);
2289 int cacheid = imageInCache(dev, mem, sizex, sizey);
2292 bitid = getNewID(dev);
2294 i->tag = swf_AddImage(i->tag, bitid, mem, sizex, sizey, i->config_jpegquality);
2295 addImageToCache(dev, mem, sizex, sizey);
2305 static SRECT gfxline_getSWFbbox(gfxline_t*line)
2307 gfxbbox_t bbox = gfxline_getbbox(line);
2309 r.xmin = (int)(bbox.xmin*20);
2310 r.ymin = (int)(bbox.ymin*20);
2311 r.xmax = (int)(bbox.xmax*20);
2312 r.ymax = (int)(bbox.ymax*20);
2316 int line_is_empty(gfxline_t*line)
2319 if(line->type != gfx_moveTo)
2326 static void swf_fillbitmap(gfxdevice_t*dev, gfxline_t*line, gfximage_t*img, gfxmatrix_t*matrix, gfxcxform_t*cxform)
2328 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
2330 if(line_is_empty(line))
2336 int targetx = (int)(sqrt(matrix->m00*matrix->m00 + matrix->m01*matrix->m01)*img->width);
2337 int targety = (int)(sqrt(matrix->m10*matrix->m10 + matrix->m11*matrix->m11)*img->height);
2339 int newwidth=0,newheight=0;
2340 int bitid = add_image(i, img, targetx, targety, &newwidth, &newheight);
2343 double fx = (double)img->width / (double)newwidth;
2344 double fy = (double)img->height / (double)newheight;
2347 m.sx = (int)(65536*20*matrix->m00*fx); m.r1 = (int)(65536*20*matrix->m10*fy);
2348 m.r0 = (int)(65536*20*matrix->m01*fx); m.sy = (int)(65536*20*matrix->m11*fy);
2349 m.tx = (int)(matrix->tx*20);
2350 m.ty = (int)(matrix->ty*20);
2353 int myshapeid = getNewID(dev);
2354 i->tag = swf_InsertTag(i->tag,ST_DEFINESHAPE);
2356 swf_ShapeNew(&shape);
2357 int fsid = swf_ShapeAddBitmapFillStyle(shape,&m,bitid,1);
2358 swf_SetU16(i->tag, myshapeid);
2359 SRECT r = gfxline_getSWFbbox(line);
2360 r = swf_ClipRect(i->pagebbox, r);
2361 swf_SetRect(i->tag,&r);
2362 swf_SetShapeStyles(i->tag,shape);
2363 swf_ShapeCountBits(shape,NULL,NULL);
2364 swf_SetShapeBits(i->tag,shape);
2365 swf_ShapeSetAll(i->tag,shape,UNDEFINED_COORD,UNDEFINED_COORD,0,fsid,0);
2366 i->swflastx = i->swflasty = UNDEFINED_COORD;
2367 drawgfxline(dev, line, 1);
2368 swf_ShapeSetEnd(i->tag);
2369 swf_ShapeFree(shape);
2371 msg("<trace> Placing image, shape ID %d, bitmap ID %d", myshapeid, bitid);
2372 i->tag = swf_InsertTag(i->tag,ST_PLACEOBJECT2);
2373 CXFORM cxform2 = gfxcxform_to_cxform(cxform);
2374 swf_ObjectPlace(i->tag,myshapeid,getNewDepth(dev),&i->page_matrix,&cxform2,NULL);
2377 static RGBA col_black = {255,0,0,0};
2379 static void drawoutline(gfxdevice_t*dev, gfxline_t*line)
2381 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
2383 int myshapeid = getNewID(dev);
2384 i->tag = swf_InsertTag(i->tag,ST_DEFINESHAPE3);
2387 swf_ShapeNew(&shape);
2388 int lsid = swf_ShapeAddLineStyle(shape,1,&col_black);
2390 swf_SetU16(i->tag,myshapeid);
2391 SRECT r = gfxline_getSWFbbox(line);
2392 r = swf_ClipRect(i->pagebbox, r);
2393 swf_SetRect(i->tag,&r);
2394 swf_SetShapeStyles(i->tag,shape);
2395 swf_ShapeCountBits(shape,NULL,NULL);
2396 swf_SetShapeBits(i->tag,shape);
2397 swf_ShapeSetAll(i->tag,shape,UNDEFINED_COORD,UNDEFINED_COORD,lsid,0,0);
2398 drawgfxline(dev, line, 1);
2399 swf_ShapeSetEnd(i->tag);
2400 swf_ShapeFree(shape);
2402 i->tag = swf_InsertTag(i->tag,ST_PLACEOBJECT2);
2403 swf_ObjectPlace(i->tag, myshapeid, getNewDepth(dev), 0,0,0);
2406 static void swf_startclip(gfxdevice_t*dev, gfxline_t*line)
2408 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
2409 if(i->config_noclips)
2415 if(i->clippos >= 127)
2417 msg("<warning> Too many clip levels.");
2421 if(i->config_showclipshapes)
2422 drawoutline(dev, line);
2424 int myshapeid = getNewID(dev);
2425 i->tag = swf_InsertTag(i->tag,ST_DEFINESHAPE3);
2427 memset(&col, 0, sizeof(RGBA));
2430 swf_ShapeNew(&shape);
2431 int fsid = swf_ShapeAddSolidFillStyle(shape,&col);
2433 RGBA markcol = {0,i->mark[0],i->mark[1],i->mark[2]};
2434 swf_ShapeAddSolidFillStyle(shape,&markcol);
2436 swf_SetU16(i->tag,myshapeid);
2437 SRECT r = gfxline_getSWFbbox(line);
2438 r = swf_ClipRect(i->pagebbox, r);
2439 swf_SetRect(i->tag,&r);
2440 swf_SetShapeStyles(i->tag,shape);
2441 swf_ShapeCountBits(shape,NULL,NULL);
2442 swf_SetShapeBits(i->tag,shape);
2443 swf_ShapeSetAll(i->tag,shape,UNDEFINED_COORD,UNDEFINED_COORD,0,fsid,0);
2444 i->swflastx = i->swflasty = UNDEFINED_COORD;
2445 i->shapeisempty = 1;
2446 drawgfxline(dev, line, 1);
2447 if(i->shapeisempty) {
2448 /* an empty clip shape is equivalent to a shape with no area */
2449 int x = line?line->x:0;
2450 int y = line?line->y:0;
2451 moveto(dev, i->tag, x,y);
2452 lineto(dev, i->tag, x,y);
2453 lineto(dev, i->tag, x,y);
2455 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)) {
2456 if(i->config_watermark) {
2457 gfxbbox_t r; r.xmin = r.ymin = 0;r.xmax = i->max_x;r.ymax = i->max_y;
2458 draw_watermark(dev, r, 1);
2461 swf_ShapeSetEnd(i->tag);
2462 swf_ShapeFree(shape);
2464 /* TODO: remember the bbox, and check all shapes against it */
2466 msg("<trace> Placing clip ID %d", myshapeid);
2467 i->tag = swf_InsertTag(i->tag,ST_PLACEOBJECT2);
2468 i->cliptags[i->clippos] = i->tag;
2469 i->clipshapes[i->clippos] = myshapeid;
2470 i->clipdepths[i->clippos] = getNewDepth(dev);
2474 static void swf_endclip(gfxdevice_t*dev)
2476 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
2477 if(i->config_noclips)
2485 msg("<error> Invalid end of clipping region");
2489 /*swf_ObjectPlaceClip(i->cliptags[i->clippos],i->clipshapes[i->clippos],i->clipdepths[i->clippos],&i->page_matrix,NULL,NULL,
2490 / * clip to depth: * / i->depth <= i->clipdepths[i->clippos]? i->depth : i->depth - 1);
2492 swf_ObjectPlaceClip(i->cliptags[i->clippos],i->clipshapes[i->clippos],i->clipdepths[i->clippos],&i->page_matrix,NULL,NULL,i->depth);
2494 static int gfxline_type(gfxline_t*line)
2500 int haszerosegments=0;
2503 if(line->type == gfx_moveTo) {
2506 } else if(line->type == gfx_lineTo) {
2510 } else if(line->type == gfx_splineTo) {
2512 if(tmpsplines>lines)
2520 if(lines==0 && splines==0) return 0;
2521 else if(lines==1 && splines==0) return 1;
2522 else if(lines==0 && splines==1) return 2;
2523 else if(splines==0) return 3;
2527 static int gfxline_has_dots(gfxline_t*line)
2535 if(line->type == gfx_moveTo) {
2536 /* test the length of the preceding line, and assume it is a dot if
2537 it's length is less than 1.0. But *only* if there's a noticable
2538 gap between the previous line and the next moveTo. (I've come
2539 across a PDF where thousands of "dots" were stringed together,
2541 int last_short_gap = short_gap;
2542 if((fabs(line->x - x) + fabs(line->y - y)) < 1.0) {
2547 if(isline && dist < 1 && !short_gap && !last_short_gap) {
2552 } else if(line->type == gfx_lineTo) {
2553 dist += fabs(line->x - x) + fabs(line->y - y);
2555 } else if(line->type == gfx_splineTo) {
2556 dist += fabs(line->sx - x) + fabs(line->sy - y) +
2557 fabs(line->x - line->sx) + fabs(line->y - line->sy);
2564 if(isline && dist < 1 && !short_gap) {
2570 static int gfxline_fix_short_edges(gfxline_t*line)
2574 if(line->type == gfx_lineTo) {
2575 if(fabs(line->x - x) + fabs(line->y - y) < 0.01) {
2578 } else if(line->type == gfx_splineTo) {
2579 if(fabs(line->sx - x) + fabs(line->sy - y) +
2580 fabs(line->x - line->sx) + fabs(line->y - line->sy) < 0.01) {
2591 static char is_inside_page(gfxdevice_t*dev, gfxcoord_t x, gfxcoord_t y)
2593 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
2594 if(x<i->min_x || x>i->max_x) return 0;
2595 if(y<i->min_y || y>i->max_y) return 0;
2599 gfxline_t* gfxline_move(gfxline_t*line, double x, double y)
2601 gfxline_t*l = line = gfxline_clone(line);
2613 //#define NORMALIZE_POLYGON_POSITIONS
2615 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)
2617 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
2618 if(line_is_empty(line))
2620 int type = gfxline_type(line);
2621 int has_dots = gfxline_has_dots(line);
2622 gfxbbox_t r = gfxline_getbbox(line);
2623 int is_outside_page = !is_inside_page(dev, r.xmin, r.ymin) || !is_inside_page(dev, r.xmax, r.ymax);
2625 /* TODO: * split line into segments, and perform this check for all segments */
2627 if(i->config_disable_polygon_conversion || /*type>=5 ||*/
2629 (width <= i->config_caplinewidth
2630 || (cap_style == gfx_capRound && joint_style == gfx_joinRound)
2631 || (cap_style == gfx_capRound && type<=2))))
2635 /* convert line to polygon */
2636 msg("<trace> draw as polygon, type=%d dots=%d", type, has_dots);
2638 gfxline_fix_short_edges(line);
2639 /* we need to convert the line into a polygon */
2640 gfxpoly_t* poly = gfxpoly_from_stroke(line, width, cap_style, joint_style, miterLimit, DEFAULT_GRID);
2641 gfxline_t*gfxline = gfxline_from_gfxpoly(poly);
2642 dev->fill(dev, gfxline, color);
2643 gfxline_free(gfxline);
2644 gfxpoly_destroy(poly);
2648 msg("<trace> draw as stroke, type=%d dots=%d", type, has_dots);
2651 if(i->config_normalize_polygon_positions) {
2653 double startx = 0, starty = 0;
2654 if(line && line->type == gfx_moveTo) {
2658 line = gfxline_move(line, -startx, -starty);
2659 i->shapeposx = (int)(startx*20);
2660 i->shapeposy = (int)(starty*20);
2663 swfoutput_setstrokecolor(dev, color->r, color->g, color->b, color->a);
2664 swfoutput_setlinewidth(dev, width);
2667 drawgfxline(dev, line, 0);
2669 if(i->config_normalize_polygon_positions) {
2670 free(line); //account for _move
2675 static void swf_fill(gfxdevice_t*dev, gfxline_t*line, gfxcolor_t*color)
2677 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
2678 if(line_is_empty(line))
2682 gfxbbox_t r = gfxline_getbbox(line);
2683 int is_outside_page = !is_inside_page(dev, r.xmin, r.ymin) || !is_inside_page(dev, r.xmax, r.ymax);
2687 if(!i->config_ignoredraworder)
2690 if(i->config_normalize_polygon_positions) {
2692 double startx = 0, starty = 0;
2693 if(line && line->type == gfx_moveTo) {
2697 line = gfxline_move(line, -startx, -starty);
2698 i->shapeposx = (int)(startx*20);
2699 i->shapeposy = (int)(starty*20);
2702 swfoutput_setfillcolor(dev, color->r, color->g, color->b, color->a);
2705 drawgfxline(dev, line, 1);
2707 if(i->currentswfid==2 && r.xmin==0 && r.ymin==0 && r.xmax==i->max_x && r.ymax==i->max_y) {
2708 if(i->config_watermark) {
2709 draw_watermark(dev, r, 1);
2713 msg("<trace> end of swf_fill (shapeid=%d)", i->shapeid);
2715 if(i->config_normalize_polygon_positions) {
2716 free(line); //account for _move
2720 static GRADIENT* gfxgradient_to_GRADIENT(gfxgradient_t*gradient)
2723 gfxgradient_t*g = gradient;
2728 GRADIENT* swfgradient = malloc(sizeof(GRADIENT));
2729 swfgradient->num = num;
2730 swfgradient->rgba = malloc(sizeof(swfgradient->rgba[0])*num);
2731 swfgradient->ratios = malloc(sizeof(swfgradient->ratios[0])*num);
2736 swfgradient->ratios[num] = g->pos*255;
2737 swfgradient->rgba[num] = *(RGBA*)&g->color;
2744 static void swf_fillgradient(gfxdevice_t*dev, gfxline_t*line, gfxgradient_t*gradient, gfxgradienttype_t type, gfxmatrix_t*matrix)
2746 if(line_is_empty(line))
2748 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
2750 if(line_is_empty(line))
2753 GRADIENT* swfgradient = gfxgradient_to_GRADIENT(gradient);
2760 double f = type==gfxgradient_radial?4:4;
2762 m.sx = (int)(matrix->m00*20*f); m.r1 = (int)(matrix->m10*20*f);
2763 m.r0 = (int)(matrix->m01*20*f); m.sy = (int)(matrix->m11*20*f);
2764 m.tx = (int)(matrix->tx*20);
2765 m.ty = (int)(matrix->ty*20);
2768 int myshapeid = getNewID(dev);
2769 i->tag = swf_InsertTag(i->tag, ST_DEFINESHAPE2);
2771 swf_ShapeNew(&shape);
2772 int fsid = swf_ShapeAddGradientFillStyle(shape,&m,swfgradient,type==gfxgradient_radial);
2773 swf_SetU16(i->tag, myshapeid);
2774 SRECT r = gfxline_getSWFbbox(line);
2775 r = swf_ClipRect(i->pagebbox, r);
2776 swf_SetRect(i->tag,&r);
2777 swf_SetShapeStyles(i->tag,shape);
2778 swf_ShapeCountBits(shape,NULL,NULL);
2779 swf_SetShapeBits(i->tag,shape);
2780 swf_ShapeSetAll(i->tag,shape,UNDEFINED_COORD,UNDEFINED_COORD,0,fsid,0);
2781 i->swflastx = i->swflasty = UNDEFINED_COORD;
2782 drawgfxline(dev, line, 1);
2783 swf_ShapeSetEnd(i->tag);
2784 swf_ShapeFree(shape);
2786 int depth = getNewDepth(dev);
2787 msg("<trace> Placing gradient, shape ID %d, depth %d", myshapeid, depth);
2788 i->tag = swf_InsertTag(i->tag,ST_PLACEOBJECT2);
2789 swf_ObjectPlace(i->tag,myshapeid,depth,&i->page_matrix,NULL,NULL);
2791 swf_FreeGradient(swfgradient);free(swfgradient);
2794 static SWFFONT* gfxfont_to_swffont(gfxfont_t*font, const char* id)
2796 SWFFONT*swffont = (SWFFONT*)rfx_calloc(sizeof(SWFFONT));
2798 SRECT bounds = {0,0,0,0};
2800 swffont->version = 2;
2801 swffont->name = (U8*)strdup(id);
2802 swffont->layout = (SWFLAYOUT*)rfx_calloc(sizeof(SWFLAYOUT));
2803 swffont->layout->ascent = 0;
2804 swffont->layout->descent = 0;
2805 swffont->layout->leading = 0;
2806 swffont->layout->bounds = (SRECT*)rfx_calloc(sizeof(SRECT)*font->num_glyphs);
2807 swffont->encoding = FONT_ENCODING_UNICODE;
2808 swffont->numchars = font->num_glyphs;
2809 swffont->maxascii = font->max_unicode;
2810 swffont->ascii2glyph = (int*)rfx_calloc(sizeof(int)*swffont->maxascii);
2811 swffont->glyph2ascii = (U16*)rfx_calloc(sizeof(U16)*swffont->numchars);
2812 swffont->glyph = (SWFGLYPH*)rfx_calloc(sizeof(SWFGLYPH)*swffont->numchars);
2813 swffont->glyphnames = (char**)rfx_calloc(sizeof(char*)*swffont->numchars);
2814 for(t=0;t<font->max_unicode;t++) {
2815 swffont->ascii2glyph[t] = font->unicode2glyph[t];
2817 SRECT max = {0,0,0,0};
2818 for(t=0;t<font->num_glyphs;t++) {
2822 swffont->glyph2ascii[t] = font->glyphs[t].unicode;
2823 if(swffont->glyph2ascii[t] == 0xffff || swffont->glyph2ascii[t] == 0x0000) {
2824 /* flash 8 flashtype requires unique unicode IDs for each character.
2825 We use the Unicode private user area to assign characters, hoping that
2826 the font doesn't contain more than 2048 glyphs */
2827 swffont->glyph2ascii[t] = 0xe000 + (t&0x1fff);
2830 if(font->glyphs[t].name) {
2831 swffont->glyphnames[t] = strdup(font->glyphs[t].name);
2833 swffont->glyphnames[t] = 0;
2835 advance = font->glyphs[t].advance;
2837 swf_Shape01DrawerInit(&draw, 0);
2838 line = font->glyphs[t].line;
2842 c.x = line->sx * GLYPH_SCALE; c.y = line->sy * GLYPH_SCALE;
2843 to.x = line->x * GLYPH_SCALE; to.y = line->y * GLYPH_SCALE;
2844 if(line->type == gfx_moveTo) {
2845 draw.moveTo(&draw, &to);
2846 } else if(line->type == gfx_lineTo) {
2847 draw.lineTo(&draw, &to);
2848 } else if(line->type == gfx_splineTo) {
2849 draw.splineTo(&draw, &c, &to);
2854 swffont->glyph[t].shape = swf_ShapeDrawerToShape(&draw);
2856 SRECT bbox = swf_ShapeDrawerGetBBox(&draw);
2857 swf_ExpandRect2(&max, &bbox);
2859 swffont->layout->bounds[t] = bbox;
2861 if(advance<32768.0/20) {
2862 swffont->glyph[t].advance = (int)(advance*20);
2864 //msg("<warning> Advance value overflow in glyph %d", t);
2865 swffont->glyph[t].advance = 32767;
2868 draw.dealloc(&draw);
2870 swf_ExpandRect2(&bounds, &swffont->layout->bounds[t]);
2872 for(t=0;t<font->num_glyphs;t++) {
2873 SRECT bbox = swffont->layout->bounds[t];
2875 /* if the glyph doesn't have a bounding box, use the
2876 combined bounding box (necessary e.g. for space characters) */
2877 if(!(bbox.xmin|bbox.ymin|bbox.xmax|bbox.ymax)) {
2878 swffont->layout->bounds[t] = bbox = max;
2881 /* check that the advance value is reasonable, by comparing it
2882 with the bounding box */
2883 if(bbox.xmax>0 && (bbox.xmax*10 < swffont->glyph[t].advance || !swffont->glyph[t].advance)) {
2884 if(swffont->glyph[t].advance)
2885 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);
2886 swffont->glyph[t].advance = bbox.xmax;
2888 //swffont->glyph[t].advance = bbox.xmax - bbox.xmin;
2892 /* Flash player will use the advance value from the char, and the ascent/descent values
2893 from the layout for text selection.
2894 ascent will extend the char into negative y direction, from the baseline, while descent
2895 will extend in positive y direction, also from the baseline.
2896 The baseline is defined as the y-position zero
2899 swffont->layout->ascent = -bounds.ymin;
2900 if(swffont->layout->ascent < 0)
2901 swffont->layout->ascent = 0;
2902 swffont->layout->descent = bounds.ymax;
2903 if(swffont->layout->descent < 0)
2904 swffont->layout->descent = 0;
2905 swffont->layout->leading = bounds.ymax - bounds.ymin;
2907 /* if the font has proper ascent/descent values (>0) and those define
2908 greater line spacing that what we estimated from the bounding boxes,
2909 use the font's parameters */
2910 if(font->ascent*20 > swffont->layout->ascent)
2911 swffont->layout->ascent = font->ascent*20;
2912 if(font->descent*20 > swffont->layout->descent)
2913 swffont->layout->descent = font->descent*20;
2918 static void swf_addfont(gfxdevice_t*dev, gfxfont_t*font)
2920 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
2922 if(i->swffont && i->swffont->name && !strcmp((char*)i->swffont->name,font->id))
2923 return; // the requested font is the current font
2925 fontlist_t*last=0,*l = i->fontlist;
2928 if(!strcmp((char*)l->swffont->name, font->id)) {
2929 return; // we already know this font
2933 l = (fontlist_t*)rfx_calloc(sizeof(fontlist_t));
2934 l->swffont = gfxfont_to_swffont(font, font->id);
2941 swf_FontSetID(l->swffont, getNewID(i->dev));
2943 if(getScreenLogLevel() >= LOGLEVEL_DEBUG) {
2945 // print font information
2946 msg("<debug> Font %s",font->id);
2947 msg("<debug> | ID: %d", l->swffont->id);
2948 msg("<debug> | Version: %d", l->swffont->version);
2949 msg("<debug> | Name: %s", l->swffont->name);
2950 msg("<debug> | Numchars: %d", l->swffont->numchars);
2951 msg("<debug> | Maxascii: %d", l->swffont->maxascii);
2952 msg("<debug> | Style: %d", l->swffont->style);
2953 msg("<debug> | Encoding: %d", l->swffont->encoding);
2954 for(iii=0; iii<l->swffont->numchars;iii++) {
2955 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,
2956 l->swffont->layout->bounds[iii].xmin/20.0,
2957 l->swffont->layout->bounds[iii].ymin/20.0,
2958 l->swffont->layout->bounds[iii].xmax/20.0,
2959 l->swffont->layout->bounds[iii].ymax/20.0
2962 for(t=0;t<l->swffont->maxascii;t++) {
2963 if(l->swffont->ascii2glyph[t] == iii)
2964 msg("<debug> | - maps to %d",t);
2970 static void swf_switchfont(gfxdevice_t*dev, const char*fontid)
2972 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
2974 if(i->swffont && i->swffont->name && !strcmp((char*)i->swffont->name,fontid))
2975 return; // the requested font is the current font
2977 fontlist_t*l = i->fontlist;
2979 if(!strcmp((char*)l->swffont->name, fontid)) {
2980 i->swffont = l->swffont;
2985 msg("<error> Unknown font id: %s", fontid);
2989 static void swf_drawchar(gfxdevice_t*dev, gfxfont_t*font, int glyph, gfxcolor_t*color, gfxmatrix_t*matrix)
2991 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
2993 msg("<error> swf_drawchar called (glyph %d) without font", glyph);
2997 if(i->config_drawonlyshapes) {
2998 gfxglyph_t*g = &font->glyphs[glyph];
2999 gfxline_t*line2 = gfxline_clone(g->line);
3000 gfxline_transform(line2, matrix);
3001 dev->fill(dev, line2, color);
3002 gfxline_free(line2);
3006 if(!i->swffont || !i->swffont->name || strcmp((char*)i->swffont->name,font->id)) // not equal to current font
3008 swf_switchfont(dev, font->id); // set the current font
3012 msg("<warning> swf_drawchar: Font is NULL");
3015 if(glyph<0 || glyph>=i->swffont->numchars) {
3016 msg("<warning> No character %d in font %s (%d chars)", glyph, FIXNULL((char*)i->swffont->name), i->swffont->numchars);
3020 setfontscale(dev, matrix->m00, matrix->m01, matrix->m10, matrix->m11, matrix->tx, matrix->ty, 0);
3022 double det = i->fontmatrix.sx/65536.0 * i->fontmatrix.sy/65536.0 -
3023 i->fontmatrix.r0/65536.0 * i->fontmatrix.r1/65536.0;
3024 if(fabs(det) < 0.0005) {
3025 /* x direction equals y direction- the text is invisible */
3026 msg("<verbose> Not drawing invisible character %d (det=%f, m=[%f %f;%f %f]\n", glyph,
3028 i->fontmatrix.sx/65536.0, i->fontmatrix.r1/65536.0,
3029 i->fontmatrix.r0/65536.0, i->fontmatrix.sy/65536.0);
3033 /*if(i->swffont->glyph[glyph].shape->bitlen <= 16) {
3034 msg("<warning> Glyph %d in current charset (%s, %d characters) is empty",
3035 glyph, FIXNULL((char*)i->swffont->name), i->swffont->numchars);
3039 /* calculate character position with respect to the current font matrix */
3040 double s = 20 * GLYPH_SCALE / det;
3041 double px = matrix->tx - i->fontmatrix.tx/20.0;
3042 double py = matrix->ty - i->fontmatrix.ty/20.0;
3043 int x = (SCOORD)(( px * i->fontmatrix.sy/65536.0 - py * i->fontmatrix.r1/65536.0)*s);
3044 int y = (SCOORD)((- px * i->fontmatrix.r0/65536.0 + py * i->fontmatrix.sx/65536.0)*s);
3045 if(x>32767 || x<-32768 || y>32767 || y<-32768) {
3046 msg("<verbose> Moving character origin to %f %f\n", matrix->tx, matrix->ty);
3048 setfontscale(dev, matrix->m00, matrix->m01, matrix->m10, matrix->m11, matrix->tx, matrix->ty, 1);
3049 /* since we just moved the char origin to the current char's position,
3050 it now has the relative position (0,0) */
3059 msg("<trace> Drawing char %d in font %d at %d,%d in color %02x%02x%02x%02x",
3060 glyph, i->swffont->id, x, y, color->r, color->g, color->b, color->a);
3062 if(color->a == 0 && i->config_invisibletexttofront) {
3063 if(i->config_flashversion>=8) {
3064 // use "multiply" blend mode
3065 color->a = color->r = color->g = color->b = 255;
3067 i->topchardata = charbuffer_append(i->topchardata, i->swffont, glyph, x, y, i->current_font_size, *(RGBA*)color, &i->fontmatrix);
3069 i->chardata = charbuffer_append(i->chardata, i->swffont, glyph, x, y, i->current_font_size, *(RGBA*)color, &i->fontmatrix);
3071 swf_FontUseGlyph(i->swffont, glyph);