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 void swfoutput_namedlink(gfxdevice_t*dev, char*name, gfxline_t*points);
222 static void swfoutput_linktopage(gfxdevice_t*dev, int page, gfxline_t*points);
223 static void swfoutput_linktourl(gfxdevice_t*dev, const char*url, gfxline_t*points);
225 static gfxresult_t* swf_finish(gfxdevice_t*driver);
227 static swfoutput_internal* init_internal_struct()
229 swfoutput_internal*i = (swfoutput_internal*)malloc(sizeof(swfoutput_internal));
230 memset(i, 0, sizeof(swfoutput_internal));
254 i->fillstylechanged = 0;
261 i->config_disablelinks=0;
262 i->config_dumpfonts=0;
263 i->config_ppmsubpixels=0;
264 i->config_jpegsubpixels=0;
265 i->config_opennewwindow=1;
266 i->config_ignoredraworder=0;
267 i->config_drawonlyshapes=0;
268 i->config_jpegquality=85;
269 i->config_storeallcharacters=0;
271 i->config_enablezlib=0;
272 i->config_insertstoptag=0;
273 i->config_flashversion=6;
274 i->config_framerate=0.25;
275 i->config_splinemaxerror=1;
276 i->config_fontsplinemaxerror=1;
277 i->config_filloverlap=0;
279 i->config_bboxvars=0;
280 i->config_showclipshapes=0;
281 i->config_minlinewidth=0.05;
282 i->config_caplinewidth=1;
283 i->config_linktarget=0;
284 i->config_internallinkfunction=0;
285 i->config_externallinkfunction=0;
286 i->config_reordertags=1;
287 i->config_linknameurl=0;
289 i->config_linkcolor.r = i->config_linkcolor.g = i->config_linkcolor.b = 255;
290 i->config_linkcolor.a = 0x40;
295 static int id_error = 0;
297 static U16 getNewID(gfxdevice_t* dev)
299 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
300 if(i->currentswfid == 65535) {
302 msg("<error> ID Table overflow");
303 msg("<error> This file is too complex to render- SWF only supports 65536 shapes at once");
309 return ++i->currentswfid;
311 static U16 getNewDepth(gfxdevice_t* dev)
313 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
314 if(i->depth == 65520) {
316 msg("<error> Depth Table overflow");
317 msg("<error> This file is too complex to render- SWF only supports 65536 shapes at once");
326 static void startshape(gfxdevice_t* dev);
327 static void starttext(gfxdevice_t* dev);
328 static void endshape(gfxdevice_t* dev);
329 static void endtext(gfxdevice_t* dev);
331 typedef struct _plotxy
336 static inline int twipsnap(double f)
338 /* if(f < -0x40000000/20.0) {
339 fprintf(stderr, "Warning: Coordinate underflow (%f)\n", f);
340 f = -0x40000000/20.0;
341 } else if(f>0x3fffffff/20.0) {
342 fprintf(stderr, "Warning: Coordinate overflow (%f)\n", f);
346 /* clamp coordinates to a rectangle with the property that we
347 can represent a line from the upper left corner to the upper
348 right corner using no more than 64 strokes */
349 const double min = -(1<<(18+4))/20.0;
350 const double max = ((1<<(18+4))-1)/20.0;
352 fprintf(stderr, "Warning: Coordinate underflow (%f)\n", f);
355 fprintf(stderr, "Warning: Coordinate overflow (%f)\n", f);
362 // write a move-to command into the swf
363 static int movetoxy(gfxdevice_t*dev, TAG*tag, plotxy_t p0)
365 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
366 int rx = twipsnap(p0.x);
367 int ry = twipsnap(p0.y);
368 if(rx!=i->swflastx || ry!=i->swflasty || i->fillstylechanged) {
369 swf_ShapeSetMove (tag, i->shape, rx,ry);
370 i->fillstylechanged = 0;
377 static int moveto(gfxdevice_t*dev, TAG*tag, double x, double y)
379 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
383 return movetoxy(dev, tag, p);
385 static void addPointToBBox(gfxdevice_t*dev, int px, int py)
387 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
393 swf_ExpandRect(&i->bboxrect, p);
395 swf_ExpandRect3(&i->bboxrect, p, i->linewidth*3/2);
399 /*static void plot(gfxdevice_t*dev, int x, int y, TAG*tag)
401 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
402 int width = i->linewidth/4;
406 //swf_ShapeSetLine(tag, i->shape,-width,-width);
407 //swf_ShapeSetLine(tag, i->shape,width*2,0);
408 //swf_ShapeSetLine(tag, i->shape,0,width*2);
409 //swf_ShapeSetLine(tag, i->shape,-width*2,0);
410 //swf_ShapeSetLine(tag, i->shape,0,-width*2);
411 //swf_ShapeSetLine(tag, i->shape,width,width);
414 swf_ShapeSetLine(tag, i->shape,-width,0);
415 swf_ShapeSetLine(tag, i->shape,width,-width);
416 swf_ShapeSetLine(tag, i->shape,width,width);
417 swf_ShapeSetLine(tag, i->shape,-width,width);
418 swf_ShapeSetLine(tag, i->shape,-width,-width);
419 swf_ShapeSetLine(tag, i->shape,width,0);
421 addPointToBBox(dev, x-width ,y-width);
422 addPointToBBox(dev, x+width ,y+width);
425 // write a line-to command into the swf
426 static void linetoxy(gfxdevice_t*dev, TAG*tag, plotxy_t p0)
428 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
429 int px = twipsnap(p0.x);
430 int py = twipsnap(p0.y);
431 int rx = (px-i->swflastx);
432 int ry = (py-i->swflasty);
434 swf_ShapeSetLine (tag, i->shape, rx,ry);
435 addPointToBBox(dev, i->swflastx,i->swflasty);
436 addPointToBBox(dev, px,py);
437 } /* this is a nice idea, but doesn't work with current flash
438 players (the pixel will be invisible if they're not
439 precisely on a pixel boundary)
440 Besides, we should only do this if this lineto itself
441 is again followed by a "move".
442 else if(!i->fill && i->config_dots) {
443 // treat lines of length 0 as plots, making them
444 // at least 1 twip wide so Flash will display them
445 //plot(dev, i->swflastx, i->swflasty, tag);
446 swf_ShapeSetLine (tag, i->shape, rx+1,ry);
453 static void lineto(gfxdevice_t*dev, TAG*tag, double x, double y)
458 linetoxy(dev,tag, p);
461 // write a spline-to command into the swf
462 static void splineto(gfxdevice_t*dev, TAG*tag, plotxy_t control,plotxy_t end)
464 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
465 int lastlastx = i->swflastx;
466 int lastlasty = i->swflasty;
468 int cx = (twipsnap(control.x)-i->swflastx);
469 int cy = (twipsnap(control.y)-i->swflasty);
472 int ex = (twipsnap(end.x)-i->swflastx);
473 int ey = (twipsnap(end.y)-i->swflasty);
477 if((cx || cy) && (ex || ey)) {
478 swf_ShapeSetCurve(tag, i->shape, cx,cy,ex,ey);
479 addPointToBBox(dev, lastlastx ,lastlasty );
480 addPointToBBox(dev, lastlastx+cx,lastlasty+cy);
481 addPointToBBox(dev, lastlastx+cx+ex,lastlasty+cy+ey);
482 } else if(cx || cy || ex || ey) {
483 swf_ShapeSetLine(tag, i->shape, cx+ex,cy+ey);
484 addPointToBBox(dev, lastlastx ,lastlasty );
485 addPointToBBox(dev, lastlastx+cx,lastlasty+cy);
486 addPointToBBox(dev, lastlastx+cx+ex,lastlasty+cy+ey);
492 /* write a line, given two points and the transformation
494 /*static void line(gfxdevice_t*dev, TAG*tag, plotxy_t p0, plotxy_t p1)
496 moveto(dev, tag, p0);
497 lineto(dev, tag, p1);
500 void resetdrawer(gfxdevice_t*dev)
502 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
507 static void stopFill(gfxdevice_t*dev)
509 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
510 if(i->lastwasfill!=0)
512 swf_ShapeSetStyle(i->tag,i->shape,i->linestyleid,0x8000,0);
513 i->fillstylechanged = 1;
517 static void startFill(gfxdevice_t*dev)
519 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
520 if(i->lastwasfill!=1)
522 swf_ShapeSetStyle(i->tag,i->shape,0x8000,i->fillstyleid,0);
523 i->fillstylechanged = 1;
528 static inline int colorcompare(RGBA*a,RGBA*b)
540 static SRECT getcharacterbbox(chararray_t*chardata, MATRIX* m)
544 memset(&r, 0, sizeof(r));
547 if(debug) printf("\n");
550 for(t=0;t<chardata->pos;t++) {
551 charatposition_t*chr = &chardata->chr[t];
552 SRECT b = chr->font->layout->bounds[chr->charid];
558 /* divide by 1024, rounding xmax/ymax up */
559 b.xmax += 1023; b.ymax += 1023; b.xmin /= 1024; b.ymin /= 1024; b.xmax /= 1024; b.ymax /= 1024;
566 /* until we solve the INTERNAL_SCALING problem (see below)
567 make sure the bounding box is big enough */
573 b = swf_TurnRect(b, m);
575 if(debug) printf("(%f,%f,%f,%f) -> (%f,%f,%f,%f) [font %d, char %d]\n",
576 chr->font->layout->bounds[chr->charid].xmin/20.0,
577 chr->font->layout->bounds[chr->charid].ymin/20.0,
578 chr->font->layout->bounds[chr->charid].xmax/20.0,
579 chr->font->layout->bounds[chr->charid].ymax/20.0,
586 swf_ExpandRect2(&r, &b);
588 chardata = chardata->next;
590 if(debug) printf("-----> (%f,%f,%f,%f)\n",
598 static chararray_t*chararray_reverse(chararray_t*buf)
600 chararray_t*prev = 0;
602 chararray_t*next = buf->next;
610 static void chararray_writetotag(chararray_t*_chardata, TAG*tag)
614 color.r = _chardata?_chardata->chr[0].color.r^255:0;
623 int charadvance[128];
626 int glyphbits=1; //TODO: can this be zero?
629 if(tag->id != ST_DEFINETEXT &&
630 tag->id != ST_DEFINETEXT2) {
631 msg("<error> internal error: charbuffer_put needs an text tag, not %d\n",tag->id);
635 msg("<warning> charbuffer_put called with zero characters");
638 for(pass = 0; pass < 2; pass++)
648 advancebits++; // add sign bit
649 swf_SetU8(tag, glyphbits);
650 swf_SetU8(tag, advancebits);
653 chararray_t*chardata = _chardata;
658 assert(!chardata->next || chardata->pos == CHARDATAMAX);
659 assert(chardata->pos);
661 int to = chardata->next?chardata->pos-1:chardata->pos;
665 char islast = t==chardata->pos;
667 charatposition_t*chr = &chardata->chr[t];
669 if(lastfont != chardata->chr[t].font ||
670 lastx!=chardata->chr[t].x ||
671 lasty!=chardata->chr[t].y ||
672 !colorcompare(&color, &chardata->chr[t].color) ||
674 lastsize != chardata->chr[t].size ||
677 if(charstorepos && pass==0)
680 for(s=0;s<charstorepos;s++)
682 while(charids[s]>=(1<<glyphbits))
684 while(charadvance[s]>=(1<<advancebits))
688 if(charstorepos && pass==1)
690 tag->writeBit = 0; // Q&D
691 swf_SetBits(tag, 0, 1); // GLYPH Record
692 swf_SetBits(tag, charstorepos, 7); // number of glyphs
694 for(s=0;s<charstorepos;s++)
696 swf_SetBits(tag, charids[s], glyphbits);
697 swf_SetBits(tag, charadvance[s], advancebits);
702 if(pass == 1 && !islast)
708 if(lastx != chr->x ||
718 if(!colorcompare(&color, &chr->color))
723 font.id = chr->font->id;
724 if(lastfont != chr->font || lastsize != chr->size)
727 tag->writeBit = 0; // Q&D
728 swf_TextSetInfoRecord(tag, newfont, chr->size, newcolor, newx,newy);
731 lastfont = chr->font;
734 lastsize = chr->size;
741 if(t<chardata->pos-1) nextx = chardata->chr[t+1].x;
742 if(t==chardata->pos-1 && chardata->next) nextx = chardata->next->chr[0].x;
743 int dx = nextx-chr->x;
746 if(dx>=0 && (dx<(1<<(advancebits-1)) || pass==0)) {
753 charids[charstorepos] = chr->charid;
754 charadvance[charstorepos] = advance;
757 chardata = chardata->next;
762 static void chararray_destroy(chararray_t*chr)
765 chararray_t*next = chr->next;
772 static inline int matrix_diff(MATRIX*m1, MATRIX*m2)
774 return memcmp(m1,m2,sizeof(MATRIX));
776 static charbuffer_t*charbuffer_append(charbuffer_t*buf, SWFFONT*font, int charid, int x,int y, int size, RGBA color, MATRIX*m)
778 if(!buf || matrix_diff(&buf->matrix,m)) {
779 charbuffer_t*n = rfx_calloc(sizeof(charbuffer_t));
784 if(!buf->last || buf->last->pos == CHARDATAMAX) {
785 chararray_t*n = rfx_calloc(sizeof(chararray_t));
787 buf->array = buf->last = n;
793 chararray_t*a = buf->last;
794 a->chr[a->pos].font = font;
795 a->chr[a->pos].charid = charid;
796 a->chr[a->pos].x = x;
797 a->chr[a->pos].y = y;
798 a->chr[a->pos].color = color;
799 a->chr[a->pos].size = size;
804 /* Notice: we can only put chars in the range -1639,1638 (-32768/20,32768/20).
805 So if we set this value to high, the char coordinates will overflow.
806 If we set it to low, however, the char positions will be inaccurate */
807 #define GLYPH_SCALE 1
809 static void chararray_writetodev(gfxdevice_t*dev, chararray_t*array, MATRIX*matrix, char invisible)
811 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
813 int textid = getNewID(dev);
814 i->tag = swf_InsertTag(i->tag,ST_DEFINETEXT2);
815 swf_SetU16(i->tag, textid);
817 r = getcharacterbbox(array, matrix);
818 r = swf_ClipRect(i->pagebbox, r);
819 swf_SetRect(i->tag,&r);
820 swf_SetMatrix(i->tag, matrix);
821 msg("<trace> Placing text as ID %d", textid);
822 chararray_writetotag(array, i->tag);
827 if(i->swf->fileVersion >= 8) {
828 i->tag = swf_InsertTag(i->tag, ST_CSMTEXTSETTINGS);
829 swf_SetU16(i->tag, textid);
831 //swf_SetU8(i->tag, /*subpixel grid*/(2<<3)|/*flashtype*/0x40);
832 swf_SetU8(i->tag, /*grid*/(1<<3)|/*flashtype*/0x40);
833 //swf_SetU8(i->tag, /*no grid*/(0<<3)|/*flashtype*/0x40);
835 swf_SetU32(i->tag, 0);//thickness
836 swf_SetU32(i->tag, 0);//sharpness
837 swf_SetU8(i->tag, 0);//reserved
839 if(invisible && i->config_flashversion>=8) {
840 i->tag = swf_InsertTag(i->tag,ST_PLACEOBJECT3);
841 swf_ObjectPlaceBlend(i->tag,textid,getNewDepth(dev),&i->page_matrix,NULL,NULL,BLENDMODE_MULTIPLY);
843 i->tag = swf_InsertTag(i->tag,ST_PLACEOBJECT2);
844 swf_ObjectPlace(i->tag,textid,getNewDepth(dev),&i->page_matrix,NULL,NULL);
848 static void charbuffer_writetodevandfree(gfxdevice_t*dev, charbuffer_t*buf, char invisible)
851 charbuffer_t*next = buf->next;buf->next = 0;
852 chararray_writetodev(dev, buf->array, &buf->matrix, invisible);
853 chararray_destroy(buf->array);
859 static void endtext(gfxdevice_t*dev)
861 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
864 charbuffer_writetodevandfree(dev, i->chardata, 0);i->chardata = 0;
868 /* sets the matrix which is to be applied to characters drawn by swfoutput_drawchar() */
869 static void setfontscale(gfxdevice_t*dev,double m11,double m12, double m21,double m22,double x, double y, char force)
875 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
876 if(i->lastfontm11 == m11 &&
877 i->lastfontm12 == m12 &&
878 i->lastfontm21 == m21 &&
879 i->lastfontm22 == m22 && !force)
884 i->lastfontm11 = m11;
885 i->lastfontm12 = m12;
886 i->lastfontm21 = m21;
887 i->lastfontm22 = m22;
889 double xsize = sqrt(m11*m11 + m12*m12);
890 double ysize = sqrt(m21*m21 + m22*m22);
891 i->current_font_size = (xsize>ysize?xsize:ysize)*1;
892 if(i->current_font_size < 1)
893 i->current_font_size = 1;
894 double ifs = 1.0 / (i->current_font_size*GLYPH_SCALE);
897 m.sx = (S32)((m11*ifs)*65536); m.r1 = (S32)((m21*ifs)*65536);
898 m.r0 = (S32)((m12*ifs)*65536); m.sy = (S32)((m22*ifs)*65536);
899 /* this is the position of the first char to set a new fontmatrix-
900 we hope that it's close enough to all other characters using the
901 font, so we use its position as origin for the matrix */
907 static int watermark2_width=47;
908 static int watermark2_height=11;
909 static int watermark2[47] = {95,1989,71,0,2015,337,1678,0,2015,5,1921,320,1938,25,2006,1024,
910 1042,21,13,960,1039,976,8,2000,1359,1088,31,1989,321,1728,0,1152,
911 1344,832,0,1984,0,896,1088,1088,896,0,1984,128,256,512,1984};
913 static void draw_watermark(gfxdevice_t*dev, gfxbbox_t r, char drawall)
915 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
916 double wx = r.xmax / 5.0;
917 double tx = r.xmax*4.0 / 5.0;
918 double ty = r.ymax-wx*watermark2_height/watermark2_width;
919 double sx = (r.xmax - tx) / watermark2_width;
920 double sy = (r.ymax - ty) / watermark2_height;
923 if(ty > 0 && px > 1.0 && py > 1.0) {
925 for(y=0;y<watermark2_height;y++)
926 for(x=0;x<watermark2_width;x++) {
927 if(((watermark2[x]>>y)&1)) {
928 if(!drawall && rand()%5)
930 unsigned int b = rand();
931 moveto(dev, i->tag, x*sx+tx+((b>>1)&1)/20.0, y*sy+ty+((b>>3)&1)/20.0);
932 lineto(dev, i->tag, x*sx+px+tx+((b>>2)&1)/20.0, y*sy+ty+((b>>3)&1)/20.0);
933 lineto(dev, i->tag, x*sx+px+tx+((b>>2)&1)/20.0, y*sy+py+ty+((b>>4)&1)/20.0);
934 lineto(dev, i->tag, x*sx+tx+((b>>1)&1)/20.0, y*sy+py+ty+((b>>4)&1)/20.0);
935 lineto(dev, i->tag, x*sx+tx+((b>>1)&1)/20.0, y*sy+ty+((b>>3)&1)/20.0);
941 static void swfoutput_setfillcolor(gfxdevice_t* dev, U8 r, U8 g, U8 b, U8 a)
943 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
944 if(i->fillrgb.r == r &&
947 i->fillrgb.a == a) return;
956 static void insert_watermark(gfxdevice_t*dev, char drawall)
958 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
959 if(!drawall && i->watermarks>20)
965 swfoutput_setfillcolor(dev, 0,0,255,192);
967 swfoutput_setfillcolor(dev, rand(),rand(),rand(),(rand()&127)+128);
972 gfxbbox_t r; r.xmin = r.ymin = 0;
975 draw_watermark(dev, r, drawall);
981 static void endpage(gfxdevice_t*dev)
983 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
991 charbuffer_writetodevandfree(dev, i->topchardata, 1);
998 if(i->config_watermark) {
999 insert_watermark(dev, 1);
1002 i->pagefinished = 1;
1005 static void addViewer(gfxdevice_t* dev)
1007 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1010 RGBA button_colors[3]= {{0xbf,0x00,0x00,0x80},{0xbf,0x20,0x20,0xc0}, {0xbf,0xc0,0xc0,0xff}};
1012 int button_sizex = 20;
1013 int button_sizey = 20;
1015 RGBA black = {255,0,0,0};
1017 i->tag = swf_InsertTag(i->tag,ST_DEFINESHAPE3);
1019 int ls1 = swf_ShapeAddLineStyle(s,40,&black);
1020 int fs1 = swf_ShapeAddSolidFillStyle(s,&button_colors[t/2]);
1021 int shapeid = ids[t] = getNewID(dev);
1022 swf_SetU16(i->tag,shapeid);
1024 r.xmin = -20*button_sizex;
1025 r.xmax = 20*button_sizex;
1027 r.ymax = 40*button_sizey;
1028 swf_SetRect(i->tag,&r); // set shape bounds
1029 swf_SetShapeHeader(i->tag,s); // write all styles to tag
1030 swf_ShapeSetAll(i->tag,s,0*button_sizex,0,ls1,fs1,0);
1031 swf_ShapeSetLine(i->tag,s,(1-(t&1)*2)*20*button_sizex,20*button_sizey);
1032 swf_ShapeSetLine(i->tag,s,-(1-(t&1)*2)*20*button_sizex,20*button_sizey);
1033 swf_ShapeSetLine(i->tag,s,0,-40*button_sizey);
1034 swf_ShapeSetEnd(i->tag); // finish drawing
1035 swf_ShapeFree(s); // clean shape structure (which isn't needed anymore after writing the tag)
1037 ActionTAG*a1=0,*a2=0,*a3=0;
1038 a1 = action_NextFrame(a1);
1039 a1 = action_Stop(a1);
1040 a1 = action_End(a1);
1042 a2 = action_PreviousFrame(a2);
1043 a2 = action_Stop(a2);
1044 a2 = action_End(a2);
1046 a3 = action_Stop(a3);
1047 a3 = action_End(a3);
1049 i->tag = swf_InsertTag(i->tag, ST_DOACTION);
1050 swf_ActionSet(i->tag,a3);
1052 i->tag = swf_InsertTag(i->tag,ST_DEFINEBUTTON);
1053 int buttonid1 = getNewID(dev);
1054 swf_SetU16(i->tag, buttonid1);
1055 swf_ButtonSetRecord(i->tag,BS_UP|BS_HIT,ids[0],1,NULL,NULL);
1056 swf_ButtonSetRecord(i->tag,BS_OVER,ids[2],1,NULL,NULL);
1057 swf_ButtonSetRecord(i->tag,BS_DOWN,ids[4],1,NULL,NULL);
1058 swf_SetU8(i->tag,0); // end of button records
1059 swf_ActionSet(i->tag,a1);
1061 i->tag = swf_InsertTag(i->tag,ST_DEFINEBUTTON);
1062 int buttonid2 = getNewID(dev);
1063 swf_SetU16(i->tag, buttonid2);
1064 swf_ButtonSetRecord(i->tag,BS_UP|BS_HIT,ids[1],1,NULL,NULL);
1065 swf_ButtonSetRecord(i->tag,BS_OVER,ids[3],1,NULL,NULL);
1066 swf_ButtonSetRecord(i->tag,BS_DOWN,ids[5],1,NULL,NULL);
1067 swf_SetU8(i->tag,0); // end of button records
1068 swf_ActionSet(i->tag,a2);
1070 i->tag = swf_InsertTag(i->tag,ST_PLACEOBJECT2);
1072 swf_GetMatrix(0, &m);
1073 m.tx = button_sizex*20+200;
1074 swf_ObjectPlace(i->tag, buttonid2, 65534,&m,0,0);
1075 i->tag = swf_InsertTag(i->tag,ST_PLACEOBJECT2);
1076 m.tx = button_sizex*20+200+200;
1077 swf_ObjectPlace(i->tag, buttonid1, 65535,&m,0,0);
1081 void swf_startframe(gfxdevice_t*dev, int width, int height)
1083 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1085 if(i->config_protect) {
1086 i->tag = swf_InsertTag(i->tag, ST_PROTECT);
1087 i->config_protect = 0;
1089 if(i->config_simpleviewer) {
1094 if(!i->firstpage && !i->pagefinished)
1097 msg("<verbose> Starting new SWF page of size %dx%d", width, height);
1099 swf_GetMatrix(0, &i->page_matrix);
1100 i->page_matrix.tx = 0;
1101 i->page_matrix.ty = 0;
1108 /* create a bbox structure with the page size. This is used
1109 for clipping shape and text bounding boxes. As we don't want to
1110 generate bounding boxes which extend beyond the movie size (in
1111 order to not confuse Flash), we clip everything against i->pagebbox */
1112 i->pagebbox.xmin = 0;
1113 i->pagebbox.ymin = 0;
1114 i->pagebbox.xmax = width*20;
1115 i->pagebbox.ymax = height*20;
1117 /* increase SWF's bounding box */
1118 swf_ExpandRect2(&i->swf->movieSize, &i->pagebbox);
1120 i->lastframeno = i->frameno;
1122 i->pagefinished = 0;
1126 void swf_endframe(gfxdevice_t*dev)
1128 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1130 if(!i->pagefinished)
1133 if( (i->swf->fileVersion <= 8) && (i->config_insertstoptag) ) {
1135 atag = action_Stop(atag);
1136 atag = action_End(atag);
1137 i->tag = swf_InsertTag(i->tag,ST_DOACTION);
1138 swf_ActionSet(i->tag,atag);
1140 i->tag = swf_InsertTag(i->tag,ST_SHOWFRAME);
1143 for(i->depth;i->depth>i->startdepth;i->depth--) {
1144 i->tag = swf_InsertTag(i->tag,ST_REMOVEOBJECT2);
1145 swf_SetU16(i->tag,i->depth);
1147 i->depth = i->startdepth;
1149 if(i->config_frameresets) {
1150 for(i->currentswfid;i->currentswfid>i->startids;i->currentswfid--) {
1151 i->tag = swf_InsertTag(i->tag,ST_FREECHARACTER);
1152 swf_SetU16(i->tag,i->currentswfid);
1154 i->currentswfid = i->startids;
1158 static void setBackground(gfxdevice_t*dev, int x1, int y1, int x2, int y2)
1160 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1162 rgb.a = rgb.r = rgb.g = rgb.b = 0xff;
1166 int shapeid = getNewID(dev);
1171 i->tag = swf_InsertTag(i->tag, ST_DEFINESHAPE);
1173 fs1 = swf_ShapeAddSolidFillStyle(s, &rgb);
1174 swf_SetU16(i->tag,shapeid);
1175 swf_SetRect(i->tag,&r);
1176 swf_SetShapeHeader(i->tag,s);
1177 swf_ShapeSetAll(i->tag,s,x1,y1,ls1,fs1,0);
1178 swf_ShapeSetLine(i->tag,s,(x2-x1),0);
1179 swf_ShapeSetLine(i->tag,s,0,(y2-y1));
1180 swf_ShapeSetLine(i->tag,s,(x1-x2),0);
1181 swf_ShapeSetLine(i->tag,s,0,(y1-y2));
1182 swf_ShapeSetEnd(i->tag);
1184 i->tag = swf_InsertTag(i->tag, ST_PLACEOBJECT2);
1185 swf_ObjectPlace(i->tag,shapeid,getNewDepth(dev),0,0,0);
1186 i->tag = swf_InsertTag(i->tag, ST_PLACEOBJECT2);
1187 swf_ObjectPlaceClip(i->tag,shapeid,getNewDepth(dev),0,0,0,65535);
1190 /* initialize the swf writer */
1191 void gfxdevice_swf_init(gfxdevice_t* dev)
1193 memset(dev, 0, sizeof(gfxdevice_t));
1197 dev->internal = init_internal_struct(); // set config to default values
1199 dev->startpage = swf_startframe;
1200 dev->endpage = swf_endframe;
1201 dev->finish = swf_finish;
1202 dev->fillbitmap = swf_fillbitmap;
1203 dev->setparameter = swf_setparameter;
1204 dev->stroke = swf_stroke;
1205 dev->startclip = swf_startclip;
1206 dev->endclip = swf_endclip;
1207 dev->fill = swf_fill;
1208 dev->fillbitmap = swf_fillbitmap;
1209 dev->fillgradient = swf_fillgradient;
1210 dev->addfont = swf_addfont;
1211 dev->drawchar = swf_drawchar;
1212 dev->drawlink = swf_drawlink;
1214 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1217 msg("<verbose> initializing swf output\n", i->max_x,i->max_y);
1221 i->swf = (SWF*)rfx_calloc(sizeof(SWF));
1222 i->swf->fileVersion = 0;
1223 i->swf->frameRate = 0x80;
1224 i->swf->movieSize.xmin = 0;
1225 i->swf->movieSize.ymin = 0;
1226 i->swf->movieSize.xmax = 0;
1227 i->swf->movieSize.ymax = 0;
1228 i->swf->fileAttributes = 9; // as3, local-with-network
1230 i->swf->firstTag = swf_InsertTag(NULL,ST_SETBACKGROUNDCOLOR);
1231 i->tag = i->swf->firstTag;
1233 rgb.a = rgb.r = rgb.g = rgb.b = 0xff;
1235 swf_SetRGB(i->tag,&rgb);
1237 i->startdepth = i->depth = 0;
1238 i->startids = i->currentswfid = 0;
1241 static void startshape(gfxdevice_t*dev)
1243 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1248 //if(i->chardatapos && i->chardata[i->chardatapos-1].color.a)
1251 i->tag = swf_InsertTag(i->tag,ST_DEFINESHAPE3);
1253 swf_ShapeNew(&i->shape);
1254 i->linestyleid = swf_ShapeAddLineStyle(i->shape,i->linewidth,&i->strokergb);
1255 i->fillstyleid = swf_ShapeAddSolidFillStyle(i->shape,&i->fillrgb);
1257 RGBA markcol = {0,i->mark[0],i->mark[1],i->mark[2]};
1258 swf_ShapeAddSolidFillStyle(i->shape,&markcol);
1261 i->shapeid = getNewID(dev);
1263 msg("<debug> Using shape id %d", i->shapeid);
1265 swf_SetU16(i->tag,i->shapeid); // ID
1267 i->bboxrectpos = i->tag->len;
1269 swf_SetRect(i->tag,&i->pagebbox);
1271 memset(&i->bboxrect, 0, sizeof(i->bboxrect));
1273 swf_SetShapeStyles(i->tag,i->shape);
1274 swf_ShapeCountBits(i->shape,NULL,NULL);
1275 swf_SetShapeBits(i->tag,i->shape);
1277 /* TODO: do we really need this? */
1278 //swf_ShapeSetAll(i->tag,i->shape,/*x*/0,/*y*/0,i->linestyleid,0,0);
1279 //swf_ShapeSetAll(i->tag,i->shape,/*x*/UNDEFINED_COORD,/*y*/UNDEFINED_COORD,i->linestyleid,0,0);
1280 i->swflastx=i->swflasty=UNDEFINED_COORD;
1281 i->lastwasfill = -1;
1282 i->shapeisempty = 1;
1285 static void starttext(gfxdevice_t*dev)
1287 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1291 if(i->config_watermark) {
1292 insert_watermark(dev, 0);
1295 i->swflastx=i->swflasty=0;
1299 /* TODO: move to ../lib/rfxswf */
1300 void changeRect(gfxdevice_t*dev, TAG*tag, int pos, SRECT*newrect)
1302 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1303 /* determine length of old rect */
1307 swf_GetRect(tag, &old);
1308 swf_ResetReadBits(tag);
1309 int pos_end = tag->pos;
1311 int len = tag->len - pos_end;
1312 U8*data = (U8*)malloc(len);
1313 memcpy(data, &tag->data[pos_end], len);
1316 swf_SetRect(tag, newrect);
1317 swf_SetBlock(tag, data, len);
1319 tag->pos = tag->readBit = 0;
1322 void cancelshape(gfxdevice_t*dev)
1324 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1325 /* delete old shape tag */
1327 i->tag = i->tag->prev;
1328 swf_DeleteTag(0, todel);
1329 if(i->shape) {swf_ShapeFree(i->shape);i->shape=0;}
1331 i->bboxrectpos = -1;
1333 // i->currentswfid--; // doesn't work, for some reason
1336 void fixAreas(gfxdevice_t*dev)
1338 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1339 if(!i->shapeisempty && i->fill &&
1340 (i->bboxrect.xmin == i->bboxrect.xmax ||
1341 i->bboxrect.ymin == i->bboxrect.ymax) &&
1342 i->config_minlinewidth >= 0.001
1344 msg("<debug> Shape has size 0: width=%.2f height=%.2f",
1345 (i->bboxrect.xmax-i->bboxrect.xmin)/20.0,
1346 (i->bboxrect.ymax-i->bboxrect.ymin)/20.0
1349 SRECT r = i->bboxrect;
1351 if(r.xmin == r.xmax && r.ymin == r.ymax) {
1352 /* this thing comes down to a single dot- nothing to fix here */
1358 RGBA save_col = i->strokergb;
1359 int save_width = i->linewidth;
1361 i->strokergb = i->fillrgb;
1362 i->linewidth = (int)(i->config_minlinewidth*20);
1363 if(i->linewidth==0) i->linewidth = 1;
1368 moveto(dev, i->tag, r.xmin/20.0,r.ymin/20.0);
1369 lineto(dev, i->tag, r.xmax/20.0,r.ymax/20.0);
1371 i->strokergb = save_col;
1372 i->linewidth = save_width;
1377 static void endshape_noput(gfxdevice_t*dev)
1379 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1382 //changeRect(dev, i->tag, i->bboxrectpos, &i->bboxrect);
1385 swf_ShapeFree(i->shape);
1393 static void endshape(gfxdevice_t*dev)
1395 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1401 if(i->shapeisempty ||
1403 (i->bboxrect.xmin == i->bboxrect.xmax &&
1404 i->bboxrect.ymin == i->bboxrect.ymax))
1406 // delete the shape again, we didn't do anything
1407 msg("<debug> cancelling shape: bbox is (%f,%f,%f,%f)",
1408 i->bboxrect.xmin /20.0,
1409 i->bboxrect.ymin /20.0,
1410 i->bboxrect.xmax /20.0,
1411 i->bboxrect.ymax /20.0
1417 swf_ShapeSetEnd(i->tag);
1419 SRECT r = swf_ClipRect(i->pagebbox, i->bboxrect);
1420 changeRect(dev, i->tag, i->bboxrectpos, &r);
1422 msg("<trace> Placing shape ID %d", i->shapeid);
1424 i->tag = swf_InsertTag(i->tag,ST_PLACEOBJECT2);
1425 MATRIX m = i->page_matrix;
1426 m.tx += i->shapeposx;
1427 m.ty += i->shapeposy;
1428 swf_ObjectPlace(i->tag,i->shapeid,getNewDepth(dev),&m,NULL,NULL);
1430 if(i->config_animate) {
1431 i->tag = swf_InsertTag(i->tag,ST_SHOWFRAME);
1434 swf_ShapeFree(i->shape);
1437 i->bboxrectpos = -1;
1444 void wipeSWF(SWF*swf)
1446 TAG*tag = swf->firstTag;
1448 TAG*next = tag->next;
1449 if(tag->id != ST_SETBACKGROUNDCOLOR &&
1450 tag->id != ST_END &&
1451 tag->id != ST_DOACTION &&
1452 tag->id != ST_SHOWFRAME) {
1453 swf_DeleteTag(swf, tag);
1459 void swfoutput_finalize(gfxdevice_t*dev)
1461 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1463 if(i->tag && i->tag->id == ST_END)
1464 return; //already done
1466 i->swf->fileVersion = i->config_flashversion;
1467 i->swf->frameRate = i->config_framerate*0x100;
1469 if(i->config_bboxvars) {
1470 TAG* tag = swf_InsertTag(i->swf->firstTag, ST_DOACTION);
1472 a = action_PushString(a, "xmin");
1473 a = action_PushFloat(a, i->swf->movieSize.xmin / 20.0);
1474 a = action_SetVariable(a);
1475 a = action_PushString(a, "ymin");
1476 a = action_PushFloat(a, i->swf->movieSize.ymin / 20.0);
1477 a = action_SetVariable(a);
1478 a = action_PushString(a, "xmax");
1479 a = action_PushFloat(a, i->swf->movieSize.xmax / 20.0);
1480 a = action_SetVariable(a);
1481 a = action_PushString(a, "ymax");
1482 a = action_PushFloat(a, i->swf->movieSize.ymax / 20.0);
1483 a = action_SetVariable(a);
1484 a = action_PushString(a, "width");
1485 a = action_PushFloat(a, (i->swf->movieSize.xmax - i->swf->movieSize.xmin) / 20.0);
1486 a = action_SetVariable(a);
1487 a = action_PushString(a, "height");
1488 a = action_PushFloat(a, (i->swf->movieSize.ymax - i->swf->movieSize.ymin) / 20.0);
1489 a = action_SetVariable(a);
1491 swf_ActionSet(tag, a);
1496 free(i->mark);i->mark = 0;
1500 fontlist_t *iterator = i->fontlist;
1502 TAG*mtag = i->swf->firstTag;
1503 if(iterator->swffont) {
1504 if(!i->config_storeallcharacters) {
1505 msg("<debug> Reducing font %s", iterator->swffont->name);
1506 swf_FontReduce(iterator->swffont);
1508 int used = iterator->swffont->use && iterator->swffont->use->used_glyphs;
1510 mtag = swf_InsertTag(mtag, ST_DEFINEFONT2);
1511 swf_FontSetDefine2(mtag, iterator->swffont);
1515 iterator = iterator->next;
1518 i->tag = swf_InsertTag(i->tag,ST_END);
1519 TAG* tag = i->tag->prev;
1521 /* remove the removeobject2 tags between the last ST_SHOWFRAME
1522 and the ST_END- they confuse the flash player */
1523 while(tag->id == ST_REMOVEOBJECT2) {
1524 TAG* prev = tag->prev;
1525 swf_DeleteTag(i->swf, tag);
1532 if(i->config_enablezlib || i->config_flashversion>=6) {
1533 i->swf->compressed = 1;
1536 /* Add AVM2 actionscript */
1537 if(i->config_flashversion>=9 &&
1538 (i->config_insertstoptag || i->hasbuttons) && !i->config_linknameurl) {
1539 swf_AddButtonLinks(i->swf, i->config_insertstoptag,
1540 i->config_internallinkfunction||i->config_externallinkfunction);
1542 // if(i->config_reordertags)
1543 // swf_Optimize(i->swf);
1546 int swfresult_save(gfxresult_t*gfx, const char*filename)
1548 SWF*swf = (SWF*)gfx->internal;
1551 fi = open(filename, O_BINARY|O_CREAT|O_TRUNC|O_WRONLY, 0777);
1556 msg("<fatal> Could not create \"%s\". ", FIXNULL(filename));
1560 if FAILED(swf_WriteSWF(fi,swf))
1561 msg("<error> WriteSWF() failed.\n");
1567 void* swfresult_get(gfxresult_t*gfx, const char*name)
1569 SWF*swf = (SWF*)gfx->internal;
1570 if(!strcmp(name, "swf")) {
1571 return (void*)swf_CopySWF(swf);
1572 } else if(!strcmp(name, "xmin")) {
1573 return (void*)(ptroff_t)(swf->movieSize.xmin/20);
1574 } else if(!strcmp(name, "ymin")) {
1575 return (void*)(ptroff_t)(swf->movieSize.ymin/20);
1576 } else if(!strcmp(name, "xmax")) {
1577 return (void*)(ptroff_t)(swf->movieSize.xmax/20);
1578 } else if(!strcmp(name, "ymax")) {
1579 return (void*)(ptroff_t)(swf->movieSize.ymax/20);
1580 } else if(!strcmp(name, "width")) {
1581 return (void*)(ptroff_t)((swf->movieSize.xmax - swf->movieSize.xmin)/20);
1582 } else if(!strcmp(name, "height")) {
1583 return (void*)(ptroff_t)((swf->movieSize.ymax - swf->movieSize.ymin)/20);
1587 void swfresult_destroy(gfxresult_t*gfx)
1590 swf_FreeTags((SWF*)gfx->internal);
1591 free(gfx->internal);
1594 memset(gfx, 0, sizeof(gfxresult_t));
1598 static void swfoutput_destroy(gfxdevice_t* dev);
1600 gfxresult_t* swf_finish(gfxdevice_t* dev)
1602 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1605 if(i->config_linktarget) {
1606 free(i->config_linktarget);
1607 i->config_linktarget = 0;
1610 swfoutput_finalize(dev);
1611 SWF* swf = i->swf;i->swf = 0;
1612 swfoutput_destroy(dev);
1614 result = (gfxresult_t*)rfx_calloc(sizeof(gfxresult_t));
1615 result->internal = swf;
1616 result->save = swfresult_save;
1618 result->get = swfresult_get;
1619 result->destroy = swfresult_destroy;
1623 /* Perform cleaning up */
1624 static void swfoutput_destroy(gfxdevice_t* dev)
1626 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1628 /* not initialized yet- nothing to destroy */
1632 fontlist_t *tmp,*iterator = i->fontlist;
1634 if(iterator->swffont) {
1635 swf_FontFree(iterator->swffont);iterator->swffont=0;
1638 iterator = iterator->next;
1641 if(i->swf) {swf_FreeTags(i->swf);free(i->swf);i->swf = 0;}
1644 memset(dev, 0, sizeof(gfxdevice_t));
1647 static void swfoutput_setstrokecolor(gfxdevice_t* dev, U8 r, U8 g, U8 b, U8 a)
1649 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1650 if(i->strokergb.r == r &&
1651 i->strokergb.g == g &&
1652 i->strokergb.b == b &&
1653 i->strokergb.a == a) return;
1663 //#define ROUND_UP 19
1664 //#define ROUND_UP 10
1666 static void swfoutput_setlinewidth(gfxdevice_t*dev, double _linewidth)
1668 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1669 if(i->linewidth == (U16)(_linewidth*20+19.0/20.0))
1673 i->linewidth = (U16)(_linewidth*20+19.0/20.0);
1677 static void drawlink(gfxdevice_t*dev, ActionTAG*,ActionTAG*, gfxline_t*points, char mouseover, char*type, const char*url);
1678 static void swfoutput_namedlink(gfxdevice_t*dev, char*name, gfxline_t*points);
1679 static void swfoutput_linktopage(gfxdevice_t*dev, int page, gfxline_t*points);
1680 static void swfoutput_linktourl(gfxdevice_t*dev, const char*url, gfxline_t*points);
1682 /*void swfoutput_drawlink(gfxdevice_t*dev, char*url, gfxline_t*points)
1684 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1685 dev->drawlink(dev, points, url);
1688 void swf_drawlink(gfxdevice_t*dev, gfxline_t*points, const char*url)
1690 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1692 if(i->config_disablelinks)
1695 if(!strncmp("http://pdf2swf:", url, 15)) {
1696 char*tmp = strdup(url);
1697 int l = strlen(tmp);
1700 swfoutput_namedlink(dev, tmp+15, points);
1703 } else if(!strncmp("page", url, 4)) {
1706 if(url[t]<'0' || url[t]>'9')
1709 int page = atoi(&url[4]);
1710 if(page<0) page = 0;
1711 swfoutput_linktopage(dev, page, points);
1714 swfoutput_linktourl(dev, url, points);
1717 void swfoutput_linktourl(gfxdevice_t*dev, const char*url, gfxline_t*points)
1719 ActionTAG* actions = 0;
1720 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1726 /* TODO: escape special characters in url */
1728 if(i->config_externallinkfunction && i->config_flashversion<=8) {
1729 actions = action_PushString(actions, url); //parameter
1730 actions = action_PushInt(actions, 1); //number of parameters (1)
1731 actions = action_PushString(actions, i->config_externallinkfunction); //function name
1732 actions = action_CallFunction(actions);
1733 } else if(!i->config_linktarget) {
1734 if(!i->config_opennewwindow)
1735 actions = action_GetUrl(actions, url, "_parent");
1737 actions = action_GetUrl(actions, url, "_this");
1739 actions = action_GetUrl(actions, url, i->config_linktarget);
1741 actions = action_End(actions);
1743 drawlink(dev, actions, 0, points, 0, "url", url);
1745 swf_ActionFree(actions);
1747 void swfoutput_linktopage(gfxdevice_t*dev, int page, gfxline_t*points)
1749 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1750 ActionTAG* actions = 0;
1757 if(!i->config_internallinkfunction || i->config_flashversion>=9) {
1758 actions = action_GotoFrame(actions, page-1);
1759 actions = action_End(actions);
1761 actions = action_PushInt(actions, page); //parameter
1762 actions = action_PushInt(actions, 1); //number of parameters (1)
1763 actions = action_PushString(actions, i->config_internallinkfunction); //function name
1764 actions = action_CallFunction(actions);
1765 actions = action_End(actions);
1769 sprintf(name, "page%d", page);
1771 drawlink(dev, actions, 0, points, 0, "page", name);
1773 swf_ActionFree(actions);
1776 /* Named Links (a.k.a. Acrobatmenu) are used to implement various gadgets
1777 of the viewer objects, like subtitles, index elements etc.
1779 void swfoutput_namedlink(gfxdevice_t*dev, char*name, gfxline_t*points)
1781 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1782 ActionTAG *actions1,*actions2;
1783 char*tmp = strdup(name);
1792 if(!strncmp(tmp, "call:", 5))
1794 char*x = strchr(&tmp[5], ':');
1796 actions1 = action_PushInt(0, 0); //number of parameters (0)
1797 actions1 = action_PushString(actions1, &tmp[5]); //function name
1798 actions1 = action_CallFunction(actions1);
1799 actions1 = action_End(actions1);
1802 actions1 = action_PushString(0, x+1); //parameter
1803 actions1 = action_PushInt(actions1, 1); //number of parameters (1)
1804 actions1 = action_PushString(actions1, &tmp[5]); //function name
1805 actions1 = action_CallFunction(actions1);
1806 actions1 = action_End(actions1);
1808 actions2 = action_End(0);
1814 actions1 = action_PushString(0, "/:subtitle");
1815 actions1 = action_PushString(actions1, name);
1816 actions1 = action_SetVariable(actions1);
1817 actions1 = action_End(actions1);
1819 actions2 = action_PushString(0, "/:subtitle");
1820 actions2 = action_PushString(actions2, "");
1821 actions2 = action_SetVariable(actions2);
1822 actions2 = action_End(actions2);
1826 drawlink(dev, actions1, actions2, points, mouseover, type, name);
1828 swf_ActionFree(actions1);
1829 swf_ActionFree(actions2);
1833 static void drawgfxline(gfxdevice_t*dev, gfxline_t*line, int fill)
1835 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1836 gfxcoord_t lastx=0,lasty=0,px=0,py=0;
1838 int lines= 0, splines=0;
1845 /* check whether the next segment is zero */
1846 if(line->type == gfx_moveTo) {
1847 moveto(dev, i->tag, line->x, line->y);
1848 px = lastx = line->x;
1849 py = lasty = line->y;
1851 } if(line->type == gfx_lineTo) {
1852 lineto(dev, i->tag, line->x, line->y);
1857 } else if(line->type == gfx_splineTo) {
1859 s.x = line->sx;p.x = line->x;
1860 s.y = line->sy;p.y = line->y;
1861 splineto(dev, i->tag, s, p);
1869 msg("<trace> drawgfxline, %d lines, %d splines", lines, splines);
1873 static void drawlink(gfxdevice_t*dev, ActionTAG*actions1, ActionTAG*actions2, gfxline_t*points, char mouseover, char*type, const char*url)
1875 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1884 int buttonid = getNewID(dev);
1885 gfxbbox_t bbox = gfxline_getbbox(points);
1887 if(i->config_linknameurl) {
1895 myshapeid = getNewID(dev);
1896 i->tag = swf_InsertTag(i->tag,ST_DEFINESHAPE3);
1897 swf_ShapeNew(&i->shape);
1898 rgb.r = rgb.b = rgb.a = rgb.g = 0;
1899 fsid = swf_ShapeAddSolidFillStyle(i->shape,&rgb);
1900 swf_SetU16(i->tag, myshapeid);
1901 r.xmin = (int)(bbox.xmin*20);
1902 r.ymin = (int)(bbox.ymin*20);
1903 r.xmax = (int)(bbox.xmax*20);
1904 r.ymax = (int)(bbox.ymax*20);
1905 r = swf_ClipRect(i->pagebbox, r);
1906 swf_SetRect(i->tag,&r);
1907 swf_SetShapeStyles(i->tag,i->shape);
1908 swf_ShapeCountBits(i->shape,NULL,NULL);
1909 swf_SetShapeBits(i->tag,i->shape);
1910 swf_ShapeSetAll(i->tag,i->shape,/*x*/0,/*y*/0,0,fsid,0);
1911 i->swflastx = i->swflasty = 0;
1912 drawgfxline(dev, points, 1);
1913 swf_ShapeSetEnd(i->tag);
1914 swf_ShapeFree(i->shape);
1917 myshapeid2 = getNewID(dev);
1918 i->tag = swf_InsertTag(i->tag,ST_DEFINESHAPE3);
1919 swf_ShapeNew(&i->shape);
1921 rgb = i->config_linkcolor;
1923 fsid = swf_ShapeAddSolidFillStyle(i->shape,&rgb);
1924 swf_SetU16(i->tag, myshapeid2);
1925 r.xmin = (int)(bbox.xmin*20);
1926 r.ymin = (int)(bbox.ymin*20);
1927 r.xmax = (int)(bbox.xmax*20);
1928 r.ymax = (int)(bbox.ymax*20);
1929 r = swf_ClipRect(i->pagebbox, r);
1930 swf_SetRect(i->tag,&r);
1931 swf_SetShapeStyles(i->tag,i->shape);
1932 swf_ShapeCountBits(i->shape,NULL,NULL);
1933 swf_SetShapeBits(i->tag,i->shape);
1934 swf_ShapeSetAll(i->tag,i->shape,/*x*/0,/*y*/0,0,fsid,0);
1935 i->swflastx = i->swflasty = 0;
1936 drawgfxline(dev, points, 1);
1937 swf_ShapeSetEnd(i->tag);
1938 swf_ShapeFree(i->shape);
1942 i->tag = swf_InsertTag(i->tag,ST_DEFINEBUTTON);
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);
1950 swf_ActionSet(i->tag,actions1);
1951 swf_SetU8(i->tag,0);
1955 i->tag = swf_InsertTag(i->tag,ST_DEFINEBUTTON2);
1956 swf_SetU16(i->tag,buttonid); //id
1957 swf_ButtonSetFlags(i->tag, 0); //menu=no
1958 swf_ButtonSetRecord(i->tag,0x01,myshapeid,i->depth,0,0);
1959 swf_ButtonSetRecord(i->tag,0x02,myshapeid2,i->depth,0,0);
1960 swf_ButtonSetRecord(i->tag,0x04,myshapeid2,i->depth,0,0);
1961 swf_ButtonSetRecord(i->tag,0x08,myshapeid,i->depth,0,0);
1962 swf_SetU8(i->tag,0); // end of button records
1963 swf_ButtonSetCondition(i->tag, BC_IDLE_OVERUP);
1964 swf_ActionSet(i->tag,actions1);
1966 swf_ButtonSetCondition(i->tag, BC_OVERUP_IDLE);
1967 swf_ActionSet(i->tag,actions2);
1968 swf_SetU8(i->tag,0);
1969 swf_ButtonPostProcess(i->tag, 2);
1971 swf_SetU8(i->tag,0);
1972 swf_ButtonPostProcess(i->tag, 1);
1976 const char* name = 0;
1977 if(i->config_linknameurl) {
1981 sprintf(buf, "button%d", buttonid);
1984 msg("<trace> Placing link ID %d", buttonid);
1985 i->tag = swf_InsertTag(i->tag,ST_PLACEOBJECT2);
1987 if(posx!=0 || posy!=0) {
1989 p.x = (int)(posx*20);
1990 p.y = (int)(posy*20);
1991 p = swf_TurnPoint(p, &i->page_matrix);
1996 swf_ObjectPlace(i->tag, buttonid, getNewDepth(dev),&m,0,(U8*)name);
1998 swf_ObjectPlace(i->tag, buttonid, getNewDepth(dev),&i->page_matrix,0,(U8*)name);
2005 for(t=0;t<picpos;t++)
2007 if(pic_xids[t] == xid &&
2008 pic_yids[t] == yid) {
2009 width = pic_width[t];
2010 height = pic_height[t];
2014 pic_ids[picpos] = swfoutput_drawimagelosslessN(&output, pic, pal, width, height, x1,y1,x2,y2,x3,y3,x4,y4, numpalette);
2015 pic_xids[picpos] = xid;
2016 pic_yids[picpos] = yid;
2017 pic_width[picpos] = width;
2018 pic_height[picpos] = height;
2021 pic[width*y+x] = buf[0];
2025 xid += pal[1].r*3 + pal[1].g*11 + pal[1].b*17;
2026 yid += pal[1].r*7 + pal[1].g*5 + pal[1].b*23;
2030 xid += x*r+x*b*3+x*g*7+x*a*11;
2031 yid += y*r*3+y*b*17+y*g*19+y*a*11;
2033 for(t=0;t<picpos;t++)
2035 if(pic_xids[t] == xid &&
2036 pic_yids[t] == yid) {
2045 int swf_setparameter(gfxdevice_t*dev, const char*name, const char*value)
2047 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
2049 msg("<trace> swfdevice: %s=%s", name, value);
2050 if(!strcmp(name, "jpegsubpixels")) {
2051 i->config_jpegsubpixels = atof(value);
2052 } else if(!strcmp(name, "ppmsubpixels")) {
2053 i->config_ppmsubpixels = atof(value);
2054 } else if(!strcmp(name, "subpixels")) {
2055 i->config_ppmsubpixels = i->config_jpegsubpixels = atof(value);
2056 } else if(!strcmp(name, "drawonlyshapes")) {
2057 i->config_drawonlyshapes = atoi(value);
2058 } else if(!strcmp(name, "ignoredraworder")) {
2059 i->config_ignoredraworder = atoi(value);
2060 } else if(!strcmp(name, "mark")) {
2061 if(!value || !value[0]) {
2062 if(i->mark) free(i->mark);
2066 i->mark = strdup("...");
2067 for(t=0;t<3;t++) if(value[t]) i->mark[t] = value[t];
2069 } else if(!strcmp(name, "filloverlap")) {
2070 i->config_filloverlap = atoi(value);
2071 } else if(!strcmp(name, "linksopennewwindow")) {
2072 i->config_opennewwindow = atoi(value);
2073 } else if(!strcmp(name, "opennewwindow")) {
2074 i->config_opennewwindow = atoi(value);
2075 } else if(!strcmp(name, "storeallcharacters")) {
2076 i->config_storeallcharacters = atoi(value);
2077 } else if(!strcmp(name, "enablezlib")) {
2078 i->config_enablezlib = atoi(value);
2079 } else if(!strcmp(name, "bboxvars")) {
2080 i->config_bboxvars = atoi(value);
2081 } else if(!strcmp(name, "dots")) {
2082 i->config_dots = atoi(value);
2083 } else if(!strcmp(name, "frameresets")) {
2084 i->config_frameresets = atoi(value);
2085 } else if(!strcmp(name, "showclipshapes")) {
2086 i->config_showclipshapes = atoi(value);
2087 } else if(!strcmp(name, "reordertags")) {
2088 i->config_reordertags = atoi(value);
2089 } else if(!strcmp(name, "internallinkfunction")) {
2090 i->config_internallinkfunction = strdup(value);
2091 } else if(!strcmp(name, "externallinkfunction")) {
2092 i->config_externallinkfunction = strdup(value);
2093 } else if(!strcmp(name, "linkfunction")) { //sets both internallinkfunction and externallinkfunction
2094 i->config_internallinkfunction = strdup(value);
2095 i->config_externallinkfunction = strdup(value);
2096 } else if(!strcmp(name, "disable_polygon_conversion")) {
2097 i->config_disable_polygon_conversion = atoi(value);
2098 } else if(!strcmp(name, "normalize_polygon_positions")) {
2099 i->config_normalize_polygon_positions = atoi(value);
2100 } else if(!strcmp(name, "wxwindowparams")) {
2101 i->config_watermark = atoi(value);
2102 } else if(!strcmp(name, "insertstop")) {
2103 i->config_insertstoptag = atoi(value);
2104 } else if(!strcmp(name, "protect")) {
2105 i->config_protect = atoi(value);
2106 if(i->config_protect && i->tag) {
2107 i->tag = swf_InsertTag(i->tag, ST_PROTECT);
2109 } else if(!strcmp(name, "flashversion")) {
2110 i->config_flashversion = atoi(value);
2112 i->swf->fileVersion = i->config_flashversion;
2114 } else if(!strcmp(name, "framerate")) {
2115 i->config_framerate = atof(value);
2117 i->swf->frameRate = i->config_framerate*0x100;
2119 } else if(!strcmp(name, "minlinewidth")) {
2120 i->config_minlinewidth = atof(value);
2121 } else if(!strcmp(name, "caplinewidth")) {
2122 i->config_caplinewidth = atof(value);
2123 } else if(!strcmp(name, "linktarget")) {
2124 i->config_linktarget = strdup(value);
2125 } else if(!strcmp(name, "invisibletexttofront")) {
2126 i->config_invisibletexttofront = atoi(value);
2127 } else if(!strcmp(name, "noclips")) {
2128 i->config_noclips = atoi(value);
2129 } else if(!strcmp(name, "dumpfonts")) {
2130 i->config_dumpfonts = atoi(value);
2131 } else if(!strcmp(name, "animate")) {
2132 i->config_animate = atoi(value);
2133 } else if(!strcmp(name, "disablelinks")) {
2134 i->config_disablelinks = atoi(value);
2135 } else if(!strcmp(name, "simpleviewer")) {
2136 i->config_simpleviewer = atoi(value);
2137 } else if(!strcmp(name, "next_bitmap_is_jpeg")) {
2139 } else if(!strcmp(name, "jpegquality")) {
2140 int val = atoi(value);
2142 if(val>101) val=101;
2143 i->config_jpegquality = val;
2144 } else if(!strcmp(name, "splinequality")) {
2145 int v = atoi(value);
2146 v = 500-(v*5); // 100% = 0.25 pixel, 0% = 25 pixel
2148 i->config_splinemaxerror = v;
2149 } else if(!strcmp(name, "fontquality")) {
2150 int v = atoi(value);
2151 v = 500-(v*5); // 100% = 0.25 pixel, 0% = 25 pixel
2153 i->config_fontsplinemaxerror = v;
2154 } else if(!strcmp(name, "linkcolor")) {
2155 if(strlen(value)!=8) {
2156 fprintf(stderr, "Unknown format for option 'linkcolor'. (%s <-> RRGGBBAA)\n", value);
2159 # define NIBBLE(s) (((s)>='0' && (s)<='9')?((s)-'0'):((s)&0x0f)+9)
2160 i->config_linkcolor.r = NIBBLE(value[0])<<4 | NIBBLE(value[1]);
2161 i->config_linkcolor.g = NIBBLE(value[2])<<4 | NIBBLE(value[3]);
2162 i->config_linkcolor.b = NIBBLE(value[4])<<4 | NIBBLE(value[5]);
2163 i->config_linkcolor.a = NIBBLE(value[6])<<4 | NIBBLE(value[7]);
2164 } else if(!strcmp(name, "help")) {
2165 printf("\nSWF layer options:\n");
2166 printf("jpegsubpixels=<pixels> resolution adjustment for jpeg images (same as jpegdpi, but in pixels)\n");
2167 printf("ppmsubpixels=<pixels resolution adjustment for lossless images (same as ppmdpi, but in pixels)\n");
2168 printf("subpixels=<pixels> shortcut for setting both jpegsubpixels and ppmsubpixels\n");
2169 printf("drawonlyshapes convert everything to shapes (currently broken)\n");
2170 printf("ignoredraworder allow to perform a few optimizations for creating smaller SWFs\n");
2171 printf("linksopennewwindow make links open a new browser window\n");
2172 printf("linktarget target window name of new links\n");
2173 printf("linkcolor=<color) color of links (format: RRGGBBAA)\n");
2174 printf("linknameurl Link buttons will be named like the URL they refer to (handy for iterating through links with actionscript)\n");
2175 printf("storeallcharacters don't reduce the fonts to used characters in the output file\n");
2176 printf("enablezlib switch on zlib compression (also done if flashversion>=7)\n");
2177 printf("bboxvars store the bounding box of the SWF file in actionscript variables\n");
2178 printf("dots Take care to handle dots correctly\n");
2179 printf("reordertags=0/1 (default: 1) perform some tag optimizations\n");
2180 printf("internallinkfunction=<name> when the user clicks a internal link (to a different page) in the converted file, this actionscript function is called\n");
2181 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");
2182 printf("disable_polygon_conversion never convert strokes to polygons (will remove capstyles and joint styles)\n");
2183 printf("caplinewidth=<width> the minimum thichness a line needs to have so that capstyles become visible (and are converted)\n");
2184 printf("insertstop put an ActionScript \"STOP\" tag in every frame\n");
2185 printf("protect add a \"protect\" tag to the file, to prevent loading in the Flash editor\n");
2186 printf("flashversion=<version> the SWF fileversion (6)\n");
2187 printf("framerate=<fps> SWF framerate\n");
2188 printf("minlinewidth=<width> convert horizontal/vertical boxes smaller than this width to lines (0.05) \n");
2189 printf("simpleviewer Add next/previous buttons to the SWF\n");
2190 printf("animate insert a showframe tag after each placeobject (animate draw order of PDF files)\n");
2191 printf("jpegquality=<quality> set compression quality of jpeg images\n");
2192 printf("splinequality=<value> Set the quality of spline convertion to value (0-100, default: 100).\n");
2193 printf("disablelinks Disable links.\n");
2200 // --------------------------------------------------------------------
2202 static CXFORM gfxcxform_to_cxform(gfxcxform_t* c)
2205 swf_GetCXForm(0, &cx, 1);
2208 if(c->rg!=0 || c->rb!=0 || c->ra!=0 ||
2209 c->gr!=0 || c->gb!=0 || c->ga!=0 ||
2210 c->br!=0 || c->bg!=0 || c->ba!=0 ||
2211 c->ar!=0 || c->ag!=0 || c->ab!=0)
2212 msg("<warning> CXForm not SWF-compatible");
2214 cx.a0 = (S16)(c->aa*256);
2215 cx.r0 = (S16)(c->rr*256);
2216 cx.g0 = (S16)(c->gg*256);
2217 cx.b0 = (S16)(c->bb*256);
2226 static int imageInCache(gfxdevice_t*dev, void*data, int width, int height)
2230 static void addImageToCache(gfxdevice_t*dev, void*data, int width, int height)
2234 static int add_image(swfoutput_internal*i, gfximage_t*img, int targetwidth, int targetheight, int* newwidth, int* newheight)
2236 gfxdevice_t*dev = i->dev;
2238 RGBA*mem = (RGBA*)img->data;
2240 int sizex = img->width;
2241 int sizey = img->height;
2242 int is_jpeg = i->jpeg;
2245 int newsizex=sizex, newsizey=sizey;
2248 if(is_jpeg && i->config_jpegsubpixels) {
2249 newsizex = (int)(targetwidth*i->config_jpegsubpixels + 0.5);
2250 newsizey = (int)(targetheight*i->config_jpegsubpixels + 0.5);
2251 } else if(!is_jpeg && i->config_ppmsubpixels) {
2252 newsizex = (int)(targetwidth*i->config_ppmsubpixels + 0.5);
2253 newsizey = (int)(targetheight*i->config_ppmsubpixels + 0.5);
2257 if(sizex<=0 || sizey<=0)
2264 /* TODO: cache images */
2266 if(newsizex<sizex || newsizey<sizey) {
2267 msg("<verbose> Scaling %dx%d image to %dx%d", sizex, sizey, newsizex, newsizey);
2268 newpic = swf_ImageScale(mem, sizex, sizey, newsizex, newsizey);
2269 *newwidth = sizex = newsizex;
2270 *newheight = sizey = newsizey;
2273 *newwidth = newsizex = sizex;
2274 *newheight = newsizey = sizey;
2277 int num_colors = swf_ImageGetNumberOfPaletteEntries(mem,sizex,sizey,0);
2278 int has_alpha = swf_ImageHasAlpha(mem,sizex,sizey);
2280 msg("<verbose> Drawing %dx%d %s%simage (id %d) at size %dx%d (%dx%d), %s%d colors",
2282 has_alpha?(has_alpha==2?"semi-transparent ":"transparent "):"",
2283 is_jpeg?"jpeg-":"", i->currentswfid+1,
2285 targetwidth, targetheight,
2286 /*newsizex, newsizey,*/
2287 num_colors>256?">":"", num_colors>256?256:num_colors);
2289 /*RGBA* pal = (RGBA*)rfx_alloc(sizeof(RGBA)*num_colors);
2290 swf_ImageGetNumberOfPaletteEntries(mem,sizex,sizey,pal);
2292 for(t=0;t<num_colors;t++) {
2293 printf("%02x%02x%02x%02x ",
2294 pal[t].r, pal[t].g, pal[t].b, pal[t].a);
2301 int cacheid = imageInCache(dev, mem, sizex, sizey);
2304 bitid = getNewID(dev);
2306 i->tag = swf_AddImage(i->tag, bitid, mem, sizex, sizey, i->config_jpegquality);
2307 addImageToCache(dev, mem, sizex, sizey);
2317 static SRECT gfxline_getSWFbbox(gfxline_t*line)
2319 gfxbbox_t bbox = gfxline_getbbox(line);
2321 r.xmin = (int)(bbox.xmin*20);
2322 r.ymin = (int)(bbox.ymin*20);
2323 r.xmax = (int)(bbox.xmax*20);
2324 r.ymax = (int)(bbox.ymax*20);
2328 int line_is_empty(gfxline_t*line)
2331 if(line->type != gfx_moveTo)
2338 static void swf_fillbitmap(gfxdevice_t*dev, gfxline_t*line, gfximage_t*img, gfxmatrix_t*matrix, gfxcxform_t*cxform)
2340 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
2342 if(line_is_empty(line))
2348 int targetx = (int)(sqrt(matrix->m00*matrix->m00 + matrix->m01*matrix->m01)*img->width);
2349 int targety = (int)(sqrt(matrix->m10*matrix->m10 + matrix->m11*matrix->m11)*img->height);
2351 int newwidth=0,newheight=0;
2352 int bitid = add_image(i, img, targetx, targety, &newwidth, &newheight);
2355 double fx = (double)img->width / (double)newwidth;
2356 double fy = (double)img->height / (double)newheight;
2359 m.sx = (int)(65536*20*matrix->m00*fx); m.r1 = (int)(65536*20*matrix->m10*fy);
2360 m.r0 = (int)(65536*20*matrix->m01*fx); m.sy = (int)(65536*20*matrix->m11*fy);
2361 m.tx = (int)(matrix->tx*20);
2362 m.ty = (int)(matrix->ty*20);
2365 int myshapeid = getNewID(dev);
2366 i->tag = swf_InsertTag(i->tag,ST_DEFINESHAPE);
2368 swf_ShapeNew(&shape);
2369 int fsid = swf_ShapeAddBitmapFillStyle(shape,&m,bitid,1);
2370 swf_SetU16(i->tag, myshapeid);
2371 SRECT r = gfxline_getSWFbbox(line);
2372 r = swf_ClipRect(i->pagebbox, r);
2373 swf_SetRect(i->tag,&r);
2374 swf_SetShapeStyles(i->tag,shape);
2375 swf_ShapeCountBits(shape,NULL,NULL);
2376 swf_SetShapeBits(i->tag,shape);
2377 swf_ShapeSetAll(i->tag,shape,UNDEFINED_COORD,UNDEFINED_COORD,0,fsid,0);
2378 i->swflastx = i->swflasty = UNDEFINED_COORD;
2379 drawgfxline(dev, line, 1);
2380 swf_ShapeSetEnd(i->tag);
2381 swf_ShapeFree(shape);
2383 msg("<trace> Placing image, shape ID %d, bitmap ID %d", myshapeid, bitid);
2384 i->tag = swf_InsertTag(i->tag,ST_PLACEOBJECT2);
2385 CXFORM cxform2 = gfxcxform_to_cxform(cxform);
2386 swf_ObjectPlace(i->tag,myshapeid,getNewDepth(dev),&i->page_matrix,&cxform2,NULL);
2389 static RGBA col_black = {255,0,0,0};
2391 static void drawoutline(gfxdevice_t*dev, gfxline_t*line)
2393 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
2395 int myshapeid = getNewID(dev);
2396 i->tag = swf_InsertTag(i->tag,ST_DEFINESHAPE3);
2399 swf_ShapeNew(&shape);
2400 int lsid = swf_ShapeAddLineStyle(shape,1,&col_black);
2402 swf_SetU16(i->tag,myshapeid);
2403 SRECT r = gfxline_getSWFbbox(line);
2404 r = swf_ClipRect(i->pagebbox, r);
2405 swf_SetRect(i->tag,&r);
2406 swf_SetShapeStyles(i->tag,shape);
2407 swf_ShapeCountBits(shape,NULL,NULL);
2408 swf_SetShapeBits(i->tag,shape);
2409 swf_ShapeSetAll(i->tag,shape,UNDEFINED_COORD,UNDEFINED_COORD,lsid,0,0);
2410 drawgfxline(dev, line, 1);
2411 swf_ShapeSetEnd(i->tag);
2412 swf_ShapeFree(shape);
2414 i->tag = swf_InsertTag(i->tag,ST_PLACEOBJECT2);
2415 swf_ObjectPlace(i->tag, myshapeid, getNewDepth(dev), 0,0,0);
2418 static void swf_startclip(gfxdevice_t*dev, gfxline_t*line)
2420 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
2421 if(i->config_noclips)
2427 if(i->clippos >= 127)
2429 msg("<warning> Too many clip levels.");
2433 if(i->config_showclipshapes)
2434 drawoutline(dev, line);
2436 int myshapeid = getNewID(dev);
2437 i->tag = swf_InsertTag(i->tag,ST_DEFINESHAPE3);
2439 memset(&col, 0, sizeof(RGBA));
2442 swf_ShapeNew(&shape);
2443 int fsid = swf_ShapeAddSolidFillStyle(shape,&col);
2445 RGBA markcol = {0,i->mark[0],i->mark[1],i->mark[2]};
2446 swf_ShapeAddSolidFillStyle(shape,&markcol);
2448 swf_SetU16(i->tag,myshapeid);
2449 SRECT r = gfxline_getSWFbbox(line);
2450 r = swf_ClipRect(i->pagebbox, r);
2451 swf_SetRect(i->tag,&r);
2452 swf_SetShapeStyles(i->tag,shape);
2453 swf_ShapeCountBits(shape,NULL,NULL);
2454 swf_SetShapeBits(i->tag,shape);
2455 swf_ShapeSetAll(i->tag,shape,UNDEFINED_COORD,UNDEFINED_COORD,0,fsid,0);
2456 i->swflastx = i->swflasty = UNDEFINED_COORD;
2457 i->shapeisempty = 1;
2458 drawgfxline(dev, line, 1);
2459 if(i->shapeisempty) {
2460 /* an empty clip shape is equivalent to a shape with no area */
2461 int x = line?line->x:0;
2462 int y = line?line->y:0;
2463 moveto(dev, i->tag, x,y);
2464 lineto(dev, i->tag, x,y);
2465 lineto(dev, i->tag, x,y);
2467 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)) {
2468 if(i->config_watermark) {
2469 gfxbbox_t r; r.xmin = r.ymin = 0;r.xmax = i->max_x;r.ymax = i->max_y;
2470 draw_watermark(dev, r, 1);
2473 swf_ShapeSetEnd(i->tag);
2474 swf_ShapeFree(shape);
2476 /* TODO: remember the bbox, and check all shapes against it */
2478 msg("<trace> Placing clip ID %d", myshapeid);
2479 i->tag = swf_InsertTag(i->tag,ST_PLACEOBJECT2);
2480 i->cliptags[i->clippos] = i->tag;
2481 i->clipshapes[i->clippos] = myshapeid;
2482 i->clipdepths[i->clippos] = getNewDepth(dev);
2486 static void swf_endclip(gfxdevice_t*dev)
2488 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
2489 if(i->config_noclips)
2497 msg("<error> Invalid end of clipping region");
2501 /*swf_ObjectPlaceClip(i->cliptags[i->clippos],i->clipshapes[i->clippos],i->clipdepths[i->clippos],&i->page_matrix,NULL,NULL,
2502 / * clip to depth: * / i->depth <= i->clipdepths[i->clippos]? i->depth : i->depth - 1);
2504 swf_ObjectPlaceClip(i->cliptags[i->clippos],i->clipshapes[i->clippos],i->clipdepths[i->clippos],&i->page_matrix,NULL,NULL,i->depth);
2506 static int gfxline_type(gfxline_t*line)
2512 int haszerosegments=0;
2515 if(line->type == gfx_moveTo) {
2518 } else if(line->type == gfx_lineTo) {
2522 } else if(line->type == gfx_splineTo) {
2524 if(tmpsplines>lines)
2532 if(lines==0 && splines==0) return 0;
2533 else if(lines==1 && splines==0) return 1;
2534 else if(lines==0 && splines==1) return 2;
2535 else if(splines==0) return 3;
2539 static int gfxline_has_dots(gfxline_t*line)
2547 if(line->type == gfx_moveTo) {
2548 /* test the length of the preceding line, and assume it is a dot if
2549 it's length is less than 1.0. But *only* if there's a noticable
2550 gap between the previous line and the next moveTo. (I've come
2551 across a PDF where thousands of "dots" were stringed together,
2553 int last_short_gap = short_gap;
2554 if((fabs(line->x - x) + fabs(line->y - y)) < 1.0) {
2559 if(isline && dist < 1 && !short_gap && !last_short_gap) {
2564 } else if(line->type == gfx_lineTo) {
2565 dist += fabs(line->x - x) + fabs(line->y - y);
2567 } else if(line->type == gfx_splineTo) {
2568 dist += fabs(line->sx - x) + fabs(line->sy - y) +
2569 fabs(line->x - line->sx) + fabs(line->y - line->sy);
2576 if(isline && dist < 1 && !short_gap) {
2582 static int gfxline_fix_short_edges(gfxline_t*line)
2586 if(line->type == gfx_lineTo) {
2587 if(fabs(line->x - x) + fabs(line->y - y) < 0.01) {
2590 } else if(line->type == gfx_splineTo) {
2591 if(fabs(line->sx - x) + fabs(line->sy - y) +
2592 fabs(line->x - line->sx) + fabs(line->y - line->sy) < 0.01) {
2603 static char is_inside_page(gfxdevice_t*dev, gfxcoord_t x, gfxcoord_t y)
2605 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
2606 if(x<i->min_x || x>i->max_x) return 0;
2607 if(y<i->min_y || y>i->max_y) return 0;
2611 gfxline_t* gfxline_move(gfxline_t*line, double x, double y)
2613 gfxline_t*l = line = gfxline_clone(line);
2625 //#define NORMALIZE_POLYGON_POSITIONS
2627 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)
2629 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
2630 if(line_is_empty(line))
2632 int type = gfxline_type(line);
2633 int has_dots = gfxline_has_dots(line);
2634 gfxbbox_t r = gfxline_getbbox(line);
2635 int is_outside_page = !is_inside_page(dev, r.xmin, r.ymin) || !is_inside_page(dev, r.xmax, r.ymax);
2637 /* TODO: * split line into segments, and perform this check for all segments */
2639 if(i->config_disable_polygon_conversion || /*type>=5 ||*/
2641 (width <= i->config_caplinewidth
2642 || (cap_style == gfx_capRound && joint_style == gfx_joinRound)
2643 || (cap_style == gfx_capRound && type<=2))))
2647 /* convert line to polygon */
2648 msg("<trace> draw as polygon, type=%d dots=%d", type, has_dots);
2650 gfxline_fix_short_edges(line);
2651 /* we need to convert the line into a polygon */
2652 gfxpoly_t* poly = gfxpoly_from_stroke(line, width, cap_style, joint_style, miterLimit, DEFAULT_GRID);
2653 gfxline_t*gfxline = gfxline_from_gfxpoly(poly);
2654 dev->fill(dev, gfxline, color);
2655 gfxline_free(gfxline);
2656 gfxpoly_destroy(poly);
2660 msg("<trace> draw as stroke, type=%d dots=%d", type, has_dots);
2663 if(i->config_normalize_polygon_positions) {
2665 double startx = 0, starty = 0;
2666 if(line && line->type == gfx_moveTo) {
2670 line = gfxline_move(line, -startx, -starty);
2671 i->shapeposx = (int)(startx*20);
2672 i->shapeposy = (int)(starty*20);
2675 swfoutput_setstrokecolor(dev, color->r, color->g, color->b, color->a);
2676 swfoutput_setlinewidth(dev, width);
2679 drawgfxline(dev, line, 0);
2681 if(i->config_normalize_polygon_positions) {
2682 free(line); //account for _move
2687 static void swf_fill(gfxdevice_t*dev, gfxline_t*line, gfxcolor_t*color)
2689 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
2690 if(line_is_empty(line))
2694 gfxbbox_t r = gfxline_getbbox(line);
2695 int is_outside_page = !is_inside_page(dev, r.xmin, r.ymin) || !is_inside_page(dev, r.xmax, r.ymax);
2699 if(!i->config_ignoredraworder)
2702 if(i->config_normalize_polygon_positions) {
2704 double startx = 0, starty = 0;
2705 if(line && line->type == gfx_moveTo) {
2709 line = gfxline_move(line, -startx, -starty);
2710 i->shapeposx = (int)(startx*20);
2711 i->shapeposy = (int)(starty*20);
2714 swfoutput_setfillcolor(dev, color->r, color->g, color->b, color->a);
2717 drawgfxline(dev, line, 1);
2719 if(i->currentswfid==2 && r.xmin==0 && r.ymin==0 && r.xmax==i->max_x && r.ymax==i->max_y) {
2720 if(i->config_watermark) {
2721 draw_watermark(dev, r, 1);
2725 msg("<trace> end of swf_fill (shapeid=%d)", i->shapeid);
2727 if(i->config_normalize_polygon_positions) {
2728 free(line); //account for _move
2732 static GRADIENT* gfxgradient_to_GRADIENT(gfxgradient_t*gradient)
2735 gfxgradient_t*g = gradient;
2740 GRADIENT* swfgradient = malloc(sizeof(GRADIENT));
2741 swfgradient->num = num;
2742 swfgradient->rgba = malloc(sizeof(swfgradient->rgba[0])*num);
2743 swfgradient->ratios = malloc(sizeof(swfgradient->ratios[0])*num);
2748 swfgradient->ratios[num] = g->pos*255;
2749 swfgradient->rgba[num] = *(RGBA*)&g->color;
2756 static void swf_fillgradient(gfxdevice_t*dev, gfxline_t*line, gfxgradient_t*gradient, gfxgradienttype_t type, gfxmatrix_t*matrix)
2758 if(line_is_empty(line))
2760 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
2762 if(line_is_empty(line))
2765 GRADIENT* swfgradient = gfxgradient_to_GRADIENT(gradient);
2772 double f = type==gfxgradient_radial?4:4;
2774 m.sx = (int)(matrix->m00*20*f); m.r1 = (int)(matrix->m10*20*f);
2775 m.r0 = (int)(matrix->m01*20*f); m.sy = (int)(matrix->m11*20*f);
2776 m.tx = (int)(matrix->tx*20);
2777 m.ty = (int)(matrix->ty*20);
2780 int myshapeid = getNewID(dev);
2781 i->tag = swf_InsertTag(i->tag, ST_DEFINESHAPE2);
2783 swf_ShapeNew(&shape);
2784 int fsid = swf_ShapeAddGradientFillStyle(shape,&m,swfgradient,type==gfxgradient_radial);
2785 swf_SetU16(i->tag, myshapeid);
2786 SRECT r = gfxline_getSWFbbox(line);
2787 r = swf_ClipRect(i->pagebbox, r);
2788 swf_SetRect(i->tag,&r);
2789 swf_SetShapeStyles(i->tag,shape);
2790 swf_ShapeCountBits(shape,NULL,NULL);
2791 swf_SetShapeBits(i->tag,shape);
2792 swf_ShapeSetAll(i->tag,shape,UNDEFINED_COORD,UNDEFINED_COORD,0,fsid,0);
2793 i->swflastx = i->swflasty = UNDEFINED_COORD;
2794 drawgfxline(dev, line, 1);
2795 swf_ShapeSetEnd(i->tag);
2796 swf_ShapeFree(shape);
2798 int depth = getNewDepth(dev);
2799 msg("<trace> Placing gradient, shape ID %d, depth %d", myshapeid, depth);
2800 i->tag = swf_InsertTag(i->tag,ST_PLACEOBJECT2);
2801 swf_ObjectPlace(i->tag,myshapeid,depth,&i->page_matrix,NULL,NULL);
2803 swf_FreeGradient(swfgradient);free(swfgradient);
2806 static SWFFONT* gfxfont_to_swffont(gfxfont_t*font, const char* id)
2808 SWFFONT*swffont = (SWFFONT*)rfx_calloc(sizeof(SWFFONT));
2810 SRECT bounds = {0,0,0,0};
2812 swffont->version = 2;
2813 swffont->name = (U8*)strdup(id);
2814 swffont->layout = (SWFLAYOUT*)rfx_calloc(sizeof(SWFLAYOUT));
2815 swffont->layout->ascent = 0;
2816 swffont->layout->descent = 0;
2817 swffont->layout->leading = 0;
2818 swffont->layout->bounds = (SRECT*)rfx_calloc(sizeof(SRECT)*font->num_glyphs);
2819 swffont->encoding = FONT_ENCODING_UNICODE;
2820 swffont->numchars = font->num_glyphs;
2821 swffont->maxascii = font->max_unicode;
2822 swffont->ascii2glyph = (int*)rfx_calloc(sizeof(int)*swffont->maxascii);
2823 swffont->glyph2ascii = (U16*)rfx_calloc(sizeof(U16)*swffont->numchars);
2824 swffont->glyph = (SWFGLYPH*)rfx_calloc(sizeof(SWFGLYPH)*swffont->numchars);
2825 swffont->glyphnames = (char**)rfx_calloc(sizeof(char*)*swffont->numchars);
2826 for(t=0;t<font->max_unicode;t++) {
2827 swffont->ascii2glyph[t] = font->unicode2glyph[t];
2829 SRECT max = {0,0,0,0};
2830 for(t=0;t<font->num_glyphs;t++) {
2834 swffont->glyph2ascii[t] = font->glyphs[t].unicode;
2835 if(swffont->glyph2ascii[t] == 0xffff || swffont->glyph2ascii[t] == 0x0000) {
2836 /* flash 8 flashtype requires unique unicode IDs for each character.
2837 We use the Unicode private user area to assign characters, hoping that
2838 the font doesn't contain more than 2048 glyphs */
2839 swffont->glyph2ascii[t] = 0xe000 + (t&0x1fff);
2842 if(font->glyphs[t].name) {
2843 swffont->glyphnames[t] = strdup(font->glyphs[t].name);
2845 swffont->glyphnames[t] = 0;
2847 advance = font->glyphs[t].advance;
2849 swf_Shape01DrawerInit(&draw, 0);
2850 line = font->glyphs[t].line;
2854 c.x = line->sx * GLYPH_SCALE; c.y = line->sy * GLYPH_SCALE;
2855 to.x = line->x * GLYPH_SCALE; to.y = line->y * GLYPH_SCALE;
2856 if(line->type == gfx_moveTo) {
2857 draw.moveTo(&draw, &to);
2858 } else if(line->type == gfx_lineTo) {
2859 draw.lineTo(&draw, &to);
2860 } else if(line->type == gfx_splineTo) {
2861 draw.splineTo(&draw, &c, &to);
2866 swffont->glyph[t].shape = swf_ShapeDrawerToShape(&draw);
2868 SRECT bbox = swf_ShapeDrawerGetBBox(&draw);
2869 swf_ExpandRect2(&max, &bbox);
2871 swffont->layout->bounds[t] = bbox;
2873 if(advance<32768.0/20) {
2874 swffont->glyph[t].advance = (int)(advance*20);
2876 //msg("<warning> Advance value overflow in glyph %d", t);
2877 swffont->glyph[t].advance = 32767;
2880 draw.dealloc(&draw);
2882 swf_ExpandRect2(&bounds, &swffont->layout->bounds[t]);
2884 for(t=0;t<font->num_glyphs;t++) {
2885 SRECT bbox = swffont->layout->bounds[t];
2887 /* if the glyph doesn't have a bounding box, use the
2888 combined bounding box (necessary e.g. for space characters) */
2889 if(!(bbox.xmin|bbox.ymin|bbox.xmax|bbox.ymax)) {
2890 swffont->layout->bounds[t] = bbox = max;
2893 /* check that the advance value is reasonable, by comparing it
2894 with the bounding box */
2895 if(bbox.xmax>0 && (bbox.xmax*10 < swffont->glyph[t].advance || !swffont->glyph[t].advance)) {
2896 if(swffont->glyph[t].advance)
2897 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);
2898 swffont->glyph[t].advance = bbox.xmax;
2900 //swffont->glyph[t].advance = bbox.xmax - bbox.xmin;
2904 /* Flash player will use the advance value from the char, and the ascent/descent values
2905 from the layout for text selection.
2906 ascent will extend the char into negative y direction, from the baseline, while descent
2907 will extend in positive y direction, also from the baseline.
2908 The baseline is defined as the y-position zero
2911 swffont->layout->ascent = -bounds.ymin;
2912 if(swffont->layout->ascent < 0)
2913 swffont->layout->ascent = 0;
2914 swffont->layout->descent = bounds.ymax;
2915 if(swffont->layout->descent < 0)
2916 swffont->layout->descent = 0;
2917 swffont->layout->leading = bounds.ymax - bounds.ymin;
2919 /* if the font has proper ascent/descent values (>0) and those define
2920 greater line spacing that what we estimated from the bounding boxes,
2921 use the font's parameters */
2922 if(font->ascent*20 > swffont->layout->ascent)
2923 swffont->layout->ascent = font->ascent*20;
2924 if(font->descent*20 > swffont->layout->descent)
2925 swffont->layout->descent = font->descent*20;
2930 static void swf_addfont(gfxdevice_t*dev, gfxfont_t*font)
2932 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
2934 if(i->swffont && i->swffont->name && !strcmp((char*)i->swffont->name,font->id))
2935 return; // the requested font is the current font
2937 fontlist_t*last=0,*l = i->fontlist;
2940 if(!strcmp((char*)l->swffont->name, font->id)) {
2941 return; // we already know this font
2945 l = (fontlist_t*)rfx_calloc(sizeof(fontlist_t));
2946 l->swffont = gfxfont_to_swffont(font, font->id);
2953 swf_FontSetID(l->swffont, getNewID(i->dev));
2955 if(getScreenLogLevel() >= LOGLEVEL_DEBUG) {
2957 // print font information
2958 msg("<debug> Font %s",font->id);
2959 msg("<debug> | ID: %d", l->swffont->id);
2960 msg("<debug> | Version: %d", l->swffont->version);
2961 msg("<debug> | Name: %s", l->swffont->name);
2962 msg("<debug> | Numchars: %d", l->swffont->numchars);
2963 msg("<debug> | Maxascii: %d", l->swffont->maxascii);
2964 msg("<debug> | Style: %d", l->swffont->style);
2965 msg("<debug> | Encoding: %d", l->swffont->encoding);
2966 for(iii=0; iii<l->swffont->numchars;iii++) {
2967 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,
2968 l->swffont->layout->bounds[iii].xmin/20.0,
2969 l->swffont->layout->bounds[iii].ymin/20.0,
2970 l->swffont->layout->bounds[iii].xmax/20.0,
2971 l->swffont->layout->bounds[iii].ymax/20.0
2974 for(t=0;t<l->swffont->maxascii;t++) {
2975 if(l->swffont->ascii2glyph[t] == iii)
2976 msg("<debug> | - maps to %d",t);
2982 static void swf_switchfont(gfxdevice_t*dev, const char*fontid)
2984 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
2986 if(i->swffont && i->swffont->name && !strcmp((char*)i->swffont->name,fontid))
2987 return; // the requested font is the current font
2989 fontlist_t*l = i->fontlist;
2991 if(!strcmp((char*)l->swffont->name, fontid)) {
2992 i->swffont = l->swffont;
2997 msg("<error> Unknown font id: %s", fontid);
3001 static void swf_drawchar(gfxdevice_t*dev, gfxfont_t*font, int glyph, gfxcolor_t*color, gfxmatrix_t*matrix)
3003 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
3005 msg("<error> swf_drawchar called (glyph %d) without font", glyph);
3009 if(i->config_drawonlyshapes) {
3010 gfxglyph_t*g = &font->glyphs[glyph];
3011 gfxline_t*line2 = gfxline_clone(g->line);
3012 gfxline_transform(line2, matrix);
3013 dev->fill(dev, line2, color);
3014 gfxline_free(line2);
3018 if(!i->swffont || !i->swffont->name || strcmp((char*)i->swffont->name,font->id)) // not equal to current font
3020 swf_switchfont(dev, font->id); // set the current font
3024 msg("<warning> swf_drawchar: Font is NULL");
3027 if(glyph<0 || glyph>=i->swffont->numchars) {
3028 msg("<warning> No character %d in font %s (%d chars)", glyph, FIXNULL((char*)i->swffont->name), i->swffont->numchars);
3032 setfontscale(dev, matrix->m00, matrix->m01, matrix->m10, matrix->m11, matrix->tx, matrix->ty, 0);
3034 double det = i->fontmatrix.sx/65536.0 * i->fontmatrix.sy/65536.0 -
3035 i->fontmatrix.r0/65536.0 * i->fontmatrix.r1/65536.0;
3036 if(fabs(det) < 0.0005) {
3037 /* x direction equals y direction- the text is invisible */
3038 msg("<verbose> Not drawing invisible character %d (det=%f, m=[%f %f;%f %f]\n", glyph,
3040 i->fontmatrix.sx/65536.0, i->fontmatrix.r1/65536.0,
3041 i->fontmatrix.r0/65536.0, i->fontmatrix.sy/65536.0);
3045 /*if(i->swffont->glyph[glyph].shape->bitlen <= 16) {
3046 msg("<warning> Glyph %d in current charset (%s, %d characters) is empty",
3047 glyph, FIXNULL((char*)i->swffont->name), i->swffont->numchars);
3051 /* calculate character position with respect to the current font matrix */
3052 double s = 20 * GLYPH_SCALE / det;
3053 double px = matrix->tx - i->fontmatrix.tx/20.0;
3054 double py = matrix->ty - i->fontmatrix.ty/20.0;
3055 int x = (SCOORD)(( px * i->fontmatrix.sy/65536.0 - py * i->fontmatrix.r1/65536.0)*s);
3056 int y = (SCOORD)((- px * i->fontmatrix.r0/65536.0 + py * i->fontmatrix.sx/65536.0)*s);
3057 if(x>32767 || x<-32768 || y>32767 || y<-32768) {
3058 msg("<verbose> Moving character origin to %f %f\n", matrix->tx, matrix->ty);
3060 setfontscale(dev, matrix->m00, matrix->m01, matrix->m10, matrix->m11, matrix->tx, matrix->ty, 1);
3061 /* since we just moved the char origin to the current char's position,
3062 it now has the relative position (0,0) */
3071 msg("<trace> Drawing char %d in font %d at %d,%d in color %02x%02x%02x%02x",
3072 glyph, i->swffont->id, x, y, color->r, color->g, color->b, color->a);
3074 if(color->a == 0 && i->config_invisibletexttofront) {
3075 if(i->config_flashversion>=8) {
3076 // use "multiply" blend mode
3077 color->a = color->r = color->g = color->b = 255;
3079 i->topchardata = charbuffer_append(i->topchardata, i->swffont, glyph, x, y, i->current_font_size, *(RGBA*)color, &i->fontmatrix);
3081 i->chardata = charbuffer_append(i->chardata, i->swffont, glyph, x, y, i->current_font_size, *(RGBA*)color, &i->fontmatrix);
3083 swf_FontUseGlyph(i->swffont, glyph);