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);
1978 const char* name = 0;
1979 if(i->config_linknameurl) {
1980 buf2 = malloc(strlen(type)+strlen(url)+2);
1981 sprintf(buf2, "%s:%s", type, url);
1985 sprintf(buf, "button%d", buttonid);
1988 msg("<trace> Placing link ID %d", buttonid);
1989 i->tag = swf_InsertTag(i->tag,ST_PLACEOBJECT2);
1991 if(posx!=0 || posy!=0) {
1993 p.x = (int)(posx*20);
1994 p.y = (int)(posy*20);
1995 p = swf_TurnPoint(p, &i->page_matrix);
2000 swf_ObjectPlace(i->tag, buttonid, getNewDepth(dev),&m,0,(U8*)name);
2002 swf_ObjectPlace(i->tag, buttonid, getNewDepth(dev),&i->page_matrix,0,(U8*)name);
2012 for(t=0;t<picpos;t++)
2014 if(pic_xids[t] == xid &&
2015 pic_yids[t] == yid) {
2016 width = pic_width[t];
2017 height = pic_height[t];
2021 pic_ids[picpos] = swfoutput_drawimagelosslessN(&output, pic, pal, width, height, x1,y1,x2,y2,x3,y3,x4,y4, numpalette);
2022 pic_xids[picpos] = xid;
2023 pic_yids[picpos] = yid;
2024 pic_width[picpos] = width;
2025 pic_height[picpos] = height;
2028 pic[width*y+x] = buf[0];
2032 xid += pal[1].r*3 + pal[1].g*11 + pal[1].b*17;
2033 yid += pal[1].r*7 + pal[1].g*5 + pal[1].b*23;
2037 xid += x*r+x*b*3+x*g*7+x*a*11;
2038 yid += y*r*3+y*b*17+y*g*19+y*a*11;
2040 for(t=0;t<picpos;t++)
2042 if(pic_xids[t] == xid &&
2043 pic_yids[t] == yid) {
2052 int swf_setparameter(gfxdevice_t*dev, const char*name, const char*value)
2054 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
2056 msg("<trace> swfdevice: %s=%s", name, value);
2057 if(!strcmp(name, "jpegsubpixels")) {
2058 i->config_jpegsubpixels = atof(value);
2059 } else if(!strcmp(name, "ppmsubpixels")) {
2060 i->config_ppmsubpixels = atof(value);
2061 } else if(!strcmp(name, "subpixels")) {
2062 i->config_ppmsubpixels = i->config_jpegsubpixels = atof(value);
2063 } else if(!strcmp(name, "drawonlyshapes")) {
2064 i->config_drawonlyshapes = atoi(value);
2065 } else if(!strcmp(name, "ignoredraworder")) {
2066 i->config_ignoredraworder = atoi(value);
2067 } else if(!strcmp(name, "mark")) {
2068 if(!value || !value[0]) {
2069 if(i->mark) free(i->mark);
2073 i->mark = strdup("...");
2074 for(t=0;t<3;t++) if(value[t]) i->mark[t] = value[t];
2076 } else if(!strcmp(name, "filloverlap")) {
2077 i->config_filloverlap = atoi(value);
2078 } else if(!strcmp(name, "linksopennewwindow")) {
2079 i->config_opennewwindow = atoi(value);
2080 } else if(!strcmp(name, "opennewwindow")) {
2081 i->config_opennewwindow = atoi(value);
2082 } else if(!strcmp(name, "storeallcharacters")) {
2083 i->config_storeallcharacters = atoi(value);
2084 } else if(!strcmp(name, "enablezlib")) {
2085 i->config_enablezlib = atoi(value);
2086 } else if(!strcmp(name, "bboxvars")) {
2087 i->config_bboxvars = atoi(value);
2088 } else if(!strcmp(name, "dots")) {
2089 i->config_dots = atoi(value);
2090 } else if(!strcmp(name, "frameresets")) {
2091 i->config_frameresets = atoi(value);
2092 } else if(!strcmp(name, "showclipshapes")) {
2093 i->config_showclipshapes = atoi(value);
2094 } else if(!strcmp(name, "reordertags")) {
2095 i->config_reordertags = atoi(value);
2096 } else if(!strcmp(name, "internallinkfunction")) {
2097 i->config_internallinkfunction = strdup(value);
2098 } else if(!strcmp(name, "externallinkfunction")) {
2099 i->config_externallinkfunction = strdup(value);
2100 } else if(!strcmp(name, "linkfunction")) { //sets both internallinkfunction and externallinkfunction
2101 i->config_internallinkfunction = strdup(value);
2102 i->config_externallinkfunction = strdup(value);
2103 } else if(!strcmp(name, "disable_polygon_conversion")) {
2104 i->config_disable_polygon_conversion = atoi(value);
2105 } else if(!strcmp(name, "normalize_polygon_positions")) {
2106 i->config_normalize_polygon_positions = atoi(value);
2107 } else if(!strcmp(name, "wxwindowparams")) {
2108 i->config_watermark = atoi(value);
2109 } else if(!strcmp(name, "insertstop")) {
2110 i->config_insertstoptag = atoi(value);
2111 } else if(!strcmp(name, "protect")) {
2112 i->config_protect = atoi(value);
2113 if(i->config_protect && i->tag) {
2114 i->tag = swf_InsertTag(i->tag, ST_PROTECT);
2116 } else if(!strcmp(name, "flashversion")) {
2117 i->config_flashversion = atoi(value);
2119 i->swf->fileVersion = i->config_flashversion;
2121 } else if(!strcmp(name, "framerate")) {
2122 i->config_framerate = atof(value);
2124 i->swf->frameRate = i->config_framerate*0x100;
2126 } else if(!strcmp(name, "minlinewidth")) {
2127 i->config_minlinewidth = atof(value);
2128 } else if(!strcmp(name, "caplinewidth")) {
2129 i->config_caplinewidth = atof(value);
2130 } else if(!strcmp(name, "linktarget")) {
2131 i->config_linktarget = strdup(value);
2132 } else if(!strcmp(name, "invisibletexttofront")) {
2133 i->config_invisibletexttofront = atoi(value);
2134 } else if(!strcmp(name, "noclips")) {
2135 i->config_noclips = atoi(value);
2136 } else if(!strcmp(name, "dumpfonts")) {
2137 i->config_dumpfonts = atoi(value);
2138 } else if(!strcmp(name, "animate")) {
2139 i->config_animate = atoi(value);
2140 } else if(!strcmp(name, "disablelinks")) {
2141 i->config_disablelinks = atoi(value);
2142 } else if(!strcmp(name, "simpleviewer")) {
2143 i->config_simpleviewer = atoi(value);
2144 } else if(!strcmp(name, "next_bitmap_is_jpeg")) {
2146 } else if(!strcmp(name, "jpegquality")) {
2147 int val = atoi(value);
2149 if(val>101) val=101;
2150 i->config_jpegquality = val;
2151 } else if(!strcmp(name, "splinequality")) {
2152 int v = atoi(value);
2153 v = 500-(v*5); // 100% = 0.25 pixel, 0% = 25 pixel
2155 i->config_splinemaxerror = v;
2156 } else if(!strcmp(name, "fontquality")) {
2157 int v = atoi(value);
2158 v = 500-(v*5); // 100% = 0.25 pixel, 0% = 25 pixel
2160 i->config_fontsplinemaxerror = v;
2161 } else if(!strcmp(name, "linkcolor")) {
2162 if(strlen(value)!=8) {
2163 fprintf(stderr, "Unknown format for option 'linkcolor'. (%s <-> RRGGBBAA)\n", value);
2166 # define NIBBLE(s) (((s)>='0' && (s)<='9')?((s)-'0'):((s)&0x0f)+9)
2167 i->config_linkcolor.r = NIBBLE(value[0])<<4 | NIBBLE(value[1]);
2168 i->config_linkcolor.g = NIBBLE(value[2])<<4 | NIBBLE(value[3]);
2169 i->config_linkcolor.b = NIBBLE(value[4])<<4 | NIBBLE(value[5]);
2170 i->config_linkcolor.a = NIBBLE(value[6])<<4 | NIBBLE(value[7]);
2171 } else if(!strcmp(name, "help")) {
2172 printf("\nSWF layer options:\n");
2173 printf("jpegsubpixels=<pixels> resolution adjustment for jpeg images (same as jpegdpi, but in pixels)\n");
2174 printf("ppmsubpixels=<pixels resolution adjustment for lossless images (same as ppmdpi, but in pixels)\n");
2175 printf("subpixels=<pixels> shortcut for setting both jpegsubpixels and ppmsubpixels\n");
2176 printf("drawonlyshapes convert everything to shapes (currently broken)\n");
2177 printf("ignoredraworder allow to perform a few optimizations for creating smaller SWFs\n");
2178 printf("linksopennewwindow make links open a new browser window\n");
2179 printf("linktarget target window name of new links\n");
2180 printf("linkcolor=<color) color of links (format: RRGGBBAA)\n");
2181 printf("linknameurl Link buttons will be named like the URL they refer to (handy for iterating through links with actionscript)\n");
2182 printf("storeallcharacters don't reduce the fonts to used characters in the output file\n");
2183 printf("enablezlib switch on zlib compression (also done if flashversion>=7)\n");
2184 printf("bboxvars store the bounding box of the SWF file in actionscript variables\n");
2185 printf("dots Take care to handle dots correctly\n");
2186 printf("reordertags=0/1 (default: 1) perform some tag optimizations\n");
2187 printf("internallinkfunction=<name> when the user clicks a internal link (to a different page) in the converted file, this actionscript function is called\n");
2188 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");
2189 printf("disable_polygon_conversion never convert strokes to polygons (will remove capstyles and joint styles)\n");
2190 printf("caplinewidth=<width> the minimum thichness a line needs to have so that capstyles become visible (and are converted)\n");
2191 printf("insertstop put an ActionScript \"STOP\" tag in every frame\n");
2192 printf("protect add a \"protect\" tag to the file, to prevent loading in the Flash editor\n");
2193 printf("flashversion=<version> the SWF fileversion (6)\n");
2194 printf("framerate=<fps> SWF framerate\n");
2195 printf("minlinewidth=<width> convert horizontal/vertical boxes smaller than this width to lines (0.05) \n");
2196 printf("simpleviewer Add next/previous buttons to the SWF\n");
2197 printf("animate insert a showframe tag after each placeobject (animate draw order of PDF files)\n");
2198 printf("jpegquality=<quality> set compression quality of jpeg images\n");
2199 printf("splinequality=<value> Set the quality of spline convertion to value (0-100, default: 100).\n");
2200 printf("disablelinks Disable links.\n");
2207 // --------------------------------------------------------------------
2209 static CXFORM gfxcxform_to_cxform(gfxcxform_t* c)
2212 swf_GetCXForm(0, &cx, 1);
2215 if(c->rg!=0 || c->rb!=0 || c->ra!=0 ||
2216 c->gr!=0 || c->gb!=0 || c->ga!=0 ||
2217 c->br!=0 || c->bg!=0 || c->ba!=0 ||
2218 c->ar!=0 || c->ag!=0 || c->ab!=0)
2219 msg("<warning> CXForm not SWF-compatible");
2221 cx.a0 = (S16)(c->aa*256);
2222 cx.r0 = (S16)(c->rr*256);
2223 cx.g0 = (S16)(c->gg*256);
2224 cx.b0 = (S16)(c->bb*256);
2233 static int imageInCache(gfxdevice_t*dev, void*data, int width, int height)
2237 static void addImageToCache(gfxdevice_t*dev, void*data, int width, int height)
2241 static int add_image(swfoutput_internal*i, gfximage_t*img, int targetwidth, int targetheight, int* newwidth, int* newheight)
2243 gfxdevice_t*dev = i->dev;
2245 RGBA*mem = (RGBA*)img->data;
2247 int sizex = img->width;
2248 int sizey = img->height;
2249 int is_jpeg = i->jpeg;
2252 int newsizex=sizex, newsizey=sizey;
2255 if(is_jpeg && i->config_jpegsubpixels) {
2256 newsizex = (int)(targetwidth*i->config_jpegsubpixels + 0.5);
2257 newsizey = (int)(targetheight*i->config_jpegsubpixels + 0.5);
2258 } else if(!is_jpeg && i->config_ppmsubpixels) {
2259 newsizex = (int)(targetwidth*i->config_ppmsubpixels + 0.5);
2260 newsizey = (int)(targetheight*i->config_ppmsubpixels + 0.5);
2264 if(sizex<=0 || sizey<=0)
2271 /* TODO: cache images */
2273 if(newsizex<sizex || newsizey<sizey) {
2274 msg("<verbose> Scaling %dx%d image to %dx%d", sizex, sizey, newsizex, newsizey);
2275 newpic = swf_ImageScale(mem, sizex, sizey, newsizex, newsizey);
2276 *newwidth = sizex = newsizex;
2277 *newheight = sizey = newsizey;
2280 *newwidth = newsizex = sizex;
2281 *newheight = newsizey = sizey;
2284 int num_colors = swf_ImageGetNumberOfPaletteEntries(mem,sizex,sizey,0);
2285 int has_alpha = swf_ImageHasAlpha(mem,sizex,sizey);
2287 msg("<verbose> Drawing %dx%d %s%simage (id %d) at size %dx%d (%dx%d), %s%d colors",
2289 has_alpha?(has_alpha==2?"semi-transparent ":"transparent "):"",
2290 is_jpeg?"jpeg-":"", i->currentswfid+1,
2292 targetwidth, targetheight,
2293 /*newsizex, newsizey,*/
2294 num_colors>256?">":"", num_colors>256?256:num_colors);
2296 /*RGBA* pal = (RGBA*)rfx_alloc(sizeof(RGBA)*num_colors);
2297 swf_ImageGetNumberOfPaletteEntries(mem,sizex,sizey,pal);
2299 for(t=0;t<num_colors;t++) {
2300 printf("%02x%02x%02x%02x ",
2301 pal[t].r, pal[t].g, pal[t].b, pal[t].a);
2308 int cacheid = imageInCache(dev, mem, sizex, sizey);
2311 bitid = getNewID(dev);
2313 i->tag = swf_AddImage(i->tag, bitid, mem, sizex, sizey, i->config_jpegquality);
2314 addImageToCache(dev, mem, sizex, sizey);
2324 static SRECT gfxline_getSWFbbox(gfxline_t*line)
2326 gfxbbox_t bbox = gfxline_getbbox(line);
2328 r.xmin = (int)(bbox.xmin*20);
2329 r.ymin = (int)(bbox.ymin*20);
2330 r.xmax = (int)(bbox.xmax*20);
2331 r.ymax = (int)(bbox.ymax*20);
2335 int line_is_empty(gfxline_t*line)
2338 if(line->type != gfx_moveTo)
2345 static void swf_fillbitmap(gfxdevice_t*dev, gfxline_t*line, gfximage_t*img, gfxmatrix_t*matrix, gfxcxform_t*cxform)
2347 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
2349 if(line_is_empty(line))
2355 int targetx = (int)(sqrt(matrix->m00*matrix->m00 + matrix->m01*matrix->m01)*img->width);
2356 int targety = (int)(sqrt(matrix->m10*matrix->m10 + matrix->m11*matrix->m11)*img->height);
2358 int newwidth=0,newheight=0;
2359 int bitid = add_image(i, img, targetx, targety, &newwidth, &newheight);
2362 double fx = (double)img->width / (double)newwidth;
2363 double fy = (double)img->height / (double)newheight;
2366 m.sx = (int)(65536*20*matrix->m00*fx); m.r1 = (int)(65536*20*matrix->m10*fy);
2367 m.r0 = (int)(65536*20*matrix->m01*fx); m.sy = (int)(65536*20*matrix->m11*fy);
2368 m.tx = (int)(matrix->tx*20);
2369 m.ty = (int)(matrix->ty*20);
2372 int myshapeid = getNewID(dev);
2373 i->tag = swf_InsertTag(i->tag,ST_DEFINESHAPE);
2375 swf_ShapeNew(&shape);
2376 int fsid = swf_ShapeAddBitmapFillStyle(shape,&m,bitid,1);
2377 swf_SetU16(i->tag, myshapeid);
2378 SRECT r = gfxline_getSWFbbox(line);
2379 r = swf_ClipRect(i->pagebbox, r);
2380 swf_SetRect(i->tag,&r);
2381 swf_SetShapeStyles(i->tag,shape);
2382 swf_ShapeCountBits(shape,NULL,NULL);
2383 swf_SetShapeBits(i->tag,shape);
2384 swf_ShapeSetAll(i->tag,shape,UNDEFINED_COORD,UNDEFINED_COORD,0,fsid,0);
2385 i->swflastx = i->swflasty = UNDEFINED_COORD;
2386 drawgfxline(dev, line, 1);
2387 swf_ShapeSetEnd(i->tag);
2388 swf_ShapeFree(shape);
2390 msg("<trace> Placing image, shape ID %d, bitmap ID %d", myshapeid, bitid);
2391 i->tag = swf_InsertTag(i->tag,ST_PLACEOBJECT2);
2392 CXFORM cxform2 = gfxcxform_to_cxform(cxform);
2393 swf_ObjectPlace(i->tag,myshapeid,getNewDepth(dev),&i->page_matrix,&cxform2,NULL);
2396 static RGBA col_black = {255,0,0,0};
2398 static void drawoutline(gfxdevice_t*dev, gfxline_t*line)
2400 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
2402 int myshapeid = getNewID(dev);
2403 i->tag = swf_InsertTag(i->tag,ST_DEFINESHAPE3);
2406 swf_ShapeNew(&shape);
2407 int lsid = swf_ShapeAddLineStyle(shape,1,&col_black);
2409 swf_SetU16(i->tag,myshapeid);
2410 SRECT r = gfxline_getSWFbbox(line);
2411 r = swf_ClipRect(i->pagebbox, r);
2412 swf_SetRect(i->tag,&r);
2413 swf_SetShapeStyles(i->tag,shape);
2414 swf_ShapeCountBits(shape,NULL,NULL);
2415 swf_SetShapeBits(i->tag,shape);
2416 swf_ShapeSetAll(i->tag,shape,UNDEFINED_COORD,UNDEFINED_COORD,lsid,0,0);
2417 drawgfxline(dev, line, 1);
2418 swf_ShapeSetEnd(i->tag);
2419 swf_ShapeFree(shape);
2421 i->tag = swf_InsertTag(i->tag,ST_PLACEOBJECT2);
2422 swf_ObjectPlace(i->tag, myshapeid, getNewDepth(dev), 0,0,0);
2425 static void swf_startclip(gfxdevice_t*dev, gfxline_t*line)
2427 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
2428 if(i->config_noclips)
2434 if(i->clippos >= 127)
2436 msg("<warning> Too many clip levels.");
2440 if(i->config_showclipshapes)
2441 drawoutline(dev, line);
2443 int myshapeid = getNewID(dev);
2444 i->tag = swf_InsertTag(i->tag,ST_DEFINESHAPE3);
2446 memset(&col, 0, sizeof(RGBA));
2449 swf_ShapeNew(&shape);
2450 int fsid = swf_ShapeAddSolidFillStyle(shape,&col);
2452 RGBA markcol = {0,i->mark[0],i->mark[1],i->mark[2]};
2453 swf_ShapeAddSolidFillStyle(shape,&markcol);
2455 swf_SetU16(i->tag,myshapeid);
2456 SRECT r = gfxline_getSWFbbox(line);
2457 r = swf_ClipRect(i->pagebbox, r);
2458 swf_SetRect(i->tag,&r);
2459 swf_SetShapeStyles(i->tag,shape);
2460 swf_ShapeCountBits(shape,NULL,NULL);
2461 swf_SetShapeBits(i->tag,shape);
2462 swf_ShapeSetAll(i->tag,shape,UNDEFINED_COORD,UNDEFINED_COORD,0,fsid,0);
2463 i->swflastx = i->swflasty = UNDEFINED_COORD;
2464 i->shapeisempty = 1;
2465 drawgfxline(dev, line, 1);
2466 if(i->shapeisempty) {
2467 /* an empty clip shape is equivalent to a shape with no area */
2468 int x = line?line->x:0;
2469 int y = line?line->y:0;
2470 moveto(dev, i->tag, x,y);
2471 lineto(dev, i->tag, x,y);
2472 lineto(dev, i->tag, x,y);
2474 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)) {
2475 if(i->config_watermark) {
2476 gfxbbox_t r; r.xmin = r.ymin = 0;r.xmax = i->max_x;r.ymax = i->max_y;
2477 draw_watermark(dev, r, 1);
2480 swf_ShapeSetEnd(i->tag);
2481 swf_ShapeFree(shape);
2483 /* TODO: remember the bbox, and check all shapes against it */
2485 msg("<trace> Placing clip ID %d", myshapeid);
2486 i->tag = swf_InsertTag(i->tag,ST_PLACEOBJECT2);
2487 i->cliptags[i->clippos] = i->tag;
2488 i->clipshapes[i->clippos] = myshapeid;
2489 i->clipdepths[i->clippos] = getNewDepth(dev);
2493 static void swf_endclip(gfxdevice_t*dev)
2495 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
2496 if(i->config_noclips)
2504 msg("<error> Invalid end of clipping region");
2508 /*swf_ObjectPlaceClip(i->cliptags[i->clippos],i->clipshapes[i->clippos],i->clipdepths[i->clippos],&i->page_matrix,NULL,NULL,
2509 / * clip to depth: * / i->depth <= i->clipdepths[i->clippos]? i->depth : i->depth - 1);
2511 swf_ObjectPlaceClip(i->cliptags[i->clippos],i->clipshapes[i->clippos],i->clipdepths[i->clippos],&i->page_matrix,NULL,NULL,i->depth);
2513 static int gfxline_type(gfxline_t*line)
2519 int haszerosegments=0;
2522 if(line->type == gfx_moveTo) {
2525 } else if(line->type == gfx_lineTo) {
2529 } else if(line->type == gfx_splineTo) {
2531 if(tmpsplines>lines)
2539 if(lines==0 && splines==0) return 0;
2540 else if(lines==1 && splines==0) return 1;
2541 else if(lines==0 && splines==1) return 2;
2542 else if(splines==0) return 3;
2546 static int gfxline_has_dots(gfxline_t*line)
2554 if(line->type == gfx_moveTo) {
2555 /* test the length of the preceding line, and assume it is a dot if
2556 it's length is less than 1.0. But *only* if there's a noticable
2557 gap between the previous line and the next moveTo. (I've come
2558 across a PDF where thousands of "dots" were stringed together,
2560 int last_short_gap = short_gap;
2561 if((fabs(line->x - x) + fabs(line->y - y)) < 1.0) {
2566 if(isline && dist < 1 && !short_gap && !last_short_gap) {
2571 } else if(line->type == gfx_lineTo) {
2572 dist += fabs(line->x - x) + fabs(line->y - y);
2574 } else if(line->type == gfx_splineTo) {
2575 dist += fabs(line->sx - x) + fabs(line->sy - y) +
2576 fabs(line->x - line->sx) + fabs(line->y - line->sy);
2583 if(isline && dist < 1 && !short_gap) {
2589 static int gfxline_fix_short_edges(gfxline_t*line)
2593 if(line->type == gfx_lineTo) {
2594 if(fabs(line->x - x) + fabs(line->y - y) < 0.01) {
2597 } else if(line->type == gfx_splineTo) {
2598 if(fabs(line->sx - x) + fabs(line->sy - y) +
2599 fabs(line->x - line->sx) + fabs(line->y - line->sy) < 0.01) {
2610 static char is_inside_page(gfxdevice_t*dev, gfxcoord_t x, gfxcoord_t y)
2612 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
2613 if(x<i->min_x || x>i->max_x) return 0;
2614 if(y<i->min_y || y>i->max_y) return 0;
2618 gfxline_t* gfxline_move(gfxline_t*line, double x, double y)
2620 gfxline_t*l = line = gfxline_clone(line);
2632 //#define NORMALIZE_POLYGON_POSITIONS
2634 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)
2636 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
2637 if(line_is_empty(line))
2639 int type = gfxline_type(line);
2640 int has_dots = gfxline_has_dots(line);
2641 gfxbbox_t r = gfxline_getbbox(line);
2642 int is_outside_page = !is_inside_page(dev, r.xmin, r.ymin) || !is_inside_page(dev, r.xmax, r.ymax);
2644 /* TODO: * split line into segments, and perform this check for all segments */
2646 if(i->config_disable_polygon_conversion || /*type>=5 ||*/
2648 (width <= i->config_caplinewidth
2649 || (cap_style == gfx_capRound && joint_style == gfx_joinRound)
2650 || (cap_style == gfx_capRound && type<=2))))
2654 /* convert line to polygon */
2655 msg("<trace> draw as polygon, type=%d dots=%d", type, has_dots);
2657 gfxline_fix_short_edges(line);
2658 /* we need to convert the line into a polygon */
2659 gfxpoly_t* poly = gfxpoly_from_stroke(line, width, cap_style, joint_style, miterLimit, DEFAULT_GRID);
2660 gfxline_t*gfxline = gfxline_from_gfxpoly(poly);
2661 dev->fill(dev, gfxline, color);
2662 gfxline_free(gfxline);
2663 gfxpoly_destroy(poly);
2667 msg("<trace> draw as stroke, type=%d dots=%d", type, has_dots);
2670 if(i->config_normalize_polygon_positions) {
2672 double startx = 0, starty = 0;
2673 if(line && line->type == gfx_moveTo) {
2677 line = gfxline_move(line, -startx, -starty);
2678 i->shapeposx = (int)(startx*20);
2679 i->shapeposy = (int)(starty*20);
2682 swfoutput_setstrokecolor(dev, color->r, color->g, color->b, color->a);
2683 swfoutput_setlinewidth(dev, width);
2686 drawgfxline(dev, line, 0);
2688 if(i->config_normalize_polygon_positions) {
2689 free(line); //account for _move
2694 static void swf_fill(gfxdevice_t*dev, gfxline_t*line, gfxcolor_t*color)
2696 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
2697 if(line_is_empty(line))
2701 gfxbbox_t r = gfxline_getbbox(line);
2702 int is_outside_page = !is_inside_page(dev, r.xmin, r.ymin) || !is_inside_page(dev, r.xmax, r.ymax);
2706 if(!i->config_ignoredraworder)
2709 if(i->config_normalize_polygon_positions) {
2711 double startx = 0, starty = 0;
2712 if(line && line->type == gfx_moveTo) {
2716 line = gfxline_move(line, -startx, -starty);
2717 i->shapeposx = (int)(startx*20);
2718 i->shapeposy = (int)(starty*20);
2721 swfoutput_setfillcolor(dev, color->r, color->g, color->b, color->a);
2724 drawgfxline(dev, line, 1);
2726 if(i->currentswfid==2 && r.xmin==0 && r.ymin==0 && r.xmax==i->max_x && r.ymax==i->max_y) {
2727 if(i->config_watermark) {
2728 draw_watermark(dev, r, 1);
2732 msg("<trace> end of swf_fill (shapeid=%d)", i->shapeid);
2734 if(i->config_normalize_polygon_positions) {
2735 free(line); //account for _move
2739 static GRADIENT* gfxgradient_to_GRADIENT(gfxgradient_t*gradient)
2742 gfxgradient_t*g = gradient;
2747 GRADIENT* swfgradient = malloc(sizeof(GRADIENT));
2748 swfgradient->num = num;
2749 swfgradient->rgba = malloc(sizeof(swfgradient->rgba[0])*num);
2750 swfgradient->ratios = malloc(sizeof(swfgradient->ratios[0])*num);
2755 swfgradient->ratios[num] = g->pos*255;
2756 swfgradient->rgba[num] = *(RGBA*)&g->color;
2763 static void swf_fillgradient(gfxdevice_t*dev, gfxline_t*line, gfxgradient_t*gradient, gfxgradienttype_t type, gfxmatrix_t*matrix)
2765 if(line_is_empty(line))
2767 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
2769 if(line_is_empty(line))
2772 GRADIENT* swfgradient = gfxgradient_to_GRADIENT(gradient);
2779 double f = type==gfxgradient_radial?4:4;
2781 m.sx = (int)(matrix->m00*20*f); m.r1 = (int)(matrix->m10*20*f);
2782 m.r0 = (int)(matrix->m01*20*f); m.sy = (int)(matrix->m11*20*f);
2783 m.tx = (int)(matrix->tx*20);
2784 m.ty = (int)(matrix->ty*20);
2787 int myshapeid = getNewID(dev);
2788 i->tag = swf_InsertTag(i->tag, ST_DEFINESHAPE2);
2790 swf_ShapeNew(&shape);
2791 int fsid = swf_ShapeAddGradientFillStyle(shape,&m,swfgradient,type==gfxgradient_radial);
2792 swf_SetU16(i->tag, myshapeid);
2793 SRECT r = gfxline_getSWFbbox(line);
2794 r = swf_ClipRect(i->pagebbox, r);
2795 swf_SetRect(i->tag,&r);
2796 swf_SetShapeStyles(i->tag,shape);
2797 swf_ShapeCountBits(shape,NULL,NULL);
2798 swf_SetShapeBits(i->tag,shape);
2799 swf_ShapeSetAll(i->tag,shape,UNDEFINED_COORD,UNDEFINED_COORD,0,fsid,0);
2800 i->swflastx = i->swflasty = UNDEFINED_COORD;
2801 drawgfxline(dev, line, 1);
2802 swf_ShapeSetEnd(i->tag);
2803 swf_ShapeFree(shape);
2805 int depth = getNewDepth(dev);
2806 msg("<trace> Placing gradient, shape ID %d, depth %d", myshapeid, depth);
2807 i->tag = swf_InsertTag(i->tag,ST_PLACEOBJECT2);
2808 swf_ObjectPlace(i->tag,myshapeid,depth,&i->page_matrix,NULL,NULL);
2810 swf_FreeGradient(swfgradient);free(swfgradient);
2813 static SWFFONT* gfxfont_to_swffont(gfxfont_t*font, const char* id)
2815 SWFFONT*swffont = (SWFFONT*)rfx_calloc(sizeof(SWFFONT));
2817 SRECT bounds = {0,0,0,0};
2819 swffont->version = 2;
2820 swffont->name = (U8*)strdup(id);
2821 swffont->layout = (SWFLAYOUT*)rfx_calloc(sizeof(SWFLAYOUT));
2822 swffont->layout->ascent = 0;
2823 swffont->layout->descent = 0;
2824 swffont->layout->leading = 0;
2825 swffont->layout->bounds = (SRECT*)rfx_calloc(sizeof(SRECT)*font->num_glyphs);
2826 swffont->encoding = FONT_ENCODING_UNICODE;
2827 swffont->numchars = font->num_glyphs;
2828 swffont->maxascii = font->max_unicode;
2829 swffont->ascii2glyph = (int*)rfx_calloc(sizeof(int)*swffont->maxascii);
2830 swffont->glyph2ascii = (U16*)rfx_calloc(sizeof(U16)*swffont->numchars);
2831 swffont->glyph = (SWFGLYPH*)rfx_calloc(sizeof(SWFGLYPH)*swffont->numchars);
2832 swffont->glyphnames = (char**)rfx_calloc(sizeof(char*)*swffont->numchars);
2833 for(t=0;t<font->max_unicode;t++) {
2834 swffont->ascii2glyph[t] = font->unicode2glyph[t];
2836 SRECT max = {0,0,0,0};
2837 for(t=0;t<font->num_glyphs;t++) {
2841 swffont->glyph2ascii[t] = font->glyphs[t].unicode;
2842 if(swffont->glyph2ascii[t] == 0xffff || swffont->glyph2ascii[t] == 0x0000) {
2843 /* flash 8 flashtype requires unique unicode IDs for each character.
2844 We use the Unicode private user area to assign characters, hoping that
2845 the font doesn't contain more than 2048 glyphs */
2846 swffont->glyph2ascii[t] = 0xe000 + (t&0x1fff);
2849 if(font->glyphs[t].name) {
2850 swffont->glyphnames[t] = strdup(font->glyphs[t].name);
2852 swffont->glyphnames[t] = 0;
2854 advance = font->glyphs[t].advance;
2856 swf_Shape01DrawerInit(&draw, 0);
2857 line = font->glyphs[t].line;
2861 c.x = line->sx * GLYPH_SCALE; c.y = line->sy * GLYPH_SCALE;
2862 to.x = line->x * GLYPH_SCALE; to.y = line->y * GLYPH_SCALE;
2863 if(line->type == gfx_moveTo) {
2864 draw.moveTo(&draw, &to);
2865 } else if(line->type == gfx_lineTo) {
2866 draw.lineTo(&draw, &to);
2867 } else if(line->type == gfx_splineTo) {
2868 draw.splineTo(&draw, &c, &to);
2873 swffont->glyph[t].shape = swf_ShapeDrawerToShape(&draw);
2875 SRECT bbox = swf_ShapeDrawerGetBBox(&draw);
2876 swf_ExpandRect2(&max, &bbox);
2878 swffont->layout->bounds[t] = bbox;
2880 if(advance<32768.0/20) {
2881 swffont->glyph[t].advance = (int)(advance*20);
2883 //msg("<warning> Advance value overflow in glyph %d", t);
2884 swffont->glyph[t].advance = 32767;
2887 draw.dealloc(&draw);
2889 swf_ExpandRect2(&bounds, &swffont->layout->bounds[t]);
2891 for(t=0;t<font->num_glyphs;t++) {
2892 SRECT bbox = swffont->layout->bounds[t];
2894 /* if the glyph doesn't have a bounding box, use the
2895 combined bounding box (necessary e.g. for space characters) */
2896 if(!(bbox.xmin|bbox.ymin|bbox.xmax|bbox.ymax)) {
2897 swffont->layout->bounds[t] = bbox = max;
2900 /* check that the advance value is reasonable, by comparing it
2901 with the bounding box */
2902 if(bbox.xmax>0 && (bbox.xmax*10 < swffont->glyph[t].advance || !swffont->glyph[t].advance)) {
2903 if(swffont->glyph[t].advance)
2904 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);
2905 swffont->glyph[t].advance = bbox.xmax;
2907 //swffont->glyph[t].advance = bbox.xmax - bbox.xmin;
2911 /* Flash player will use the advance value from the char, and the ascent/descent values
2912 from the layout for text selection.
2913 ascent will extend the char into negative y direction, from the baseline, while descent
2914 will extend in positive y direction, also from the baseline.
2915 The baseline is defined as the y-position zero
2918 swffont->layout->ascent = -bounds.ymin;
2919 if(swffont->layout->ascent < 0)
2920 swffont->layout->ascent = 0;
2921 swffont->layout->descent = bounds.ymax;
2922 if(swffont->layout->descent < 0)
2923 swffont->layout->descent = 0;
2924 swffont->layout->leading = bounds.ymax - bounds.ymin;
2926 /* if the font has proper ascent/descent values (>0) and those define
2927 greater line spacing that what we estimated from the bounding boxes,
2928 use the font's parameters */
2929 if(font->ascent*20 > swffont->layout->ascent)
2930 swffont->layout->ascent = font->ascent*20;
2931 if(font->descent*20 > swffont->layout->descent)
2932 swffont->layout->descent = font->descent*20;
2937 static void swf_addfont(gfxdevice_t*dev, gfxfont_t*font)
2939 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
2941 if(i->swffont && i->swffont->name && !strcmp((char*)i->swffont->name,font->id))
2942 return; // the requested font is the current font
2944 fontlist_t*last=0,*l = i->fontlist;
2947 if(!strcmp((char*)l->swffont->name, font->id)) {
2948 return; // we already know this font
2952 l = (fontlist_t*)rfx_calloc(sizeof(fontlist_t));
2953 l->swffont = gfxfont_to_swffont(font, font->id);
2960 swf_FontSetID(l->swffont, getNewID(i->dev));
2962 if(getScreenLogLevel() >= LOGLEVEL_DEBUG) {
2964 // print font information
2965 msg("<debug> Font %s",font->id);
2966 msg("<debug> | ID: %d", l->swffont->id);
2967 msg("<debug> | Version: %d", l->swffont->version);
2968 msg("<debug> | Name: %s", l->swffont->name);
2969 msg("<debug> | Numchars: %d", l->swffont->numchars);
2970 msg("<debug> | Maxascii: %d", l->swffont->maxascii);
2971 msg("<debug> | Style: %d", l->swffont->style);
2972 msg("<debug> | Encoding: %d", l->swffont->encoding);
2973 for(iii=0; iii<l->swffont->numchars;iii++) {
2974 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,
2975 l->swffont->layout->bounds[iii].xmin/20.0,
2976 l->swffont->layout->bounds[iii].ymin/20.0,
2977 l->swffont->layout->bounds[iii].xmax/20.0,
2978 l->swffont->layout->bounds[iii].ymax/20.0
2981 for(t=0;t<l->swffont->maxascii;t++) {
2982 if(l->swffont->ascii2glyph[t] == iii)
2983 msg("<debug> | - maps to %d",t);
2989 static void swf_switchfont(gfxdevice_t*dev, const char*fontid)
2991 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
2993 if(i->swffont && i->swffont->name && !strcmp((char*)i->swffont->name,fontid))
2994 return; // the requested font is the current font
2996 fontlist_t*l = i->fontlist;
2998 if(!strcmp((char*)l->swffont->name, fontid)) {
2999 i->swffont = l->swffont;
3004 msg("<error> Unknown font id: %s", fontid);
3008 static void swf_drawchar(gfxdevice_t*dev, gfxfont_t*font, int glyph, gfxcolor_t*color, gfxmatrix_t*matrix)
3010 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
3012 msg("<error> swf_drawchar called (glyph %d) without font", glyph);
3016 if(i->config_drawonlyshapes) {
3017 gfxglyph_t*g = &font->glyphs[glyph];
3018 gfxline_t*line2 = gfxline_clone(g->line);
3019 gfxline_transform(line2, matrix);
3020 dev->fill(dev, line2, color);
3021 gfxline_free(line2);
3025 if(!i->swffont || !i->swffont->name || strcmp((char*)i->swffont->name,font->id)) // not equal to current font
3027 swf_switchfont(dev, font->id); // set the current font
3031 msg("<warning> swf_drawchar: Font is NULL");
3034 if(glyph<0 || glyph>=i->swffont->numchars) {
3035 msg("<warning> No character %d in font %s (%d chars)", glyph, FIXNULL((char*)i->swffont->name), i->swffont->numchars);
3039 setfontscale(dev, matrix->m00, matrix->m01, matrix->m10, matrix->m11, matrix->tx, matrix->ty, 0);
3041 double det = i->fontmatrix.sx/65536.0 * i->fontmatrix.sy/65536.0 -
3042 i->fontmatrix.r0/65536.0 * i->fontmatrix.r1/65536.0;
3043 if(fabs(det) < 0.0005) {
3044 /* x direction equals y direction- the text is invisible */
3045 msg("<verbose> Not drawing invisible character %d (det=%f, m=[%f %f;%f %f]\n", glyph,
3047 i->fontmatrix.sx/65536.0, i->fontmatrix.r1/65536.0,
3048 i->fontmatrix.r0/65536.0, i->fontmatrix.sy/65536.0);
3052 /*if(i->swffont->glyph[glyph].shape->bitlen <= 16) {
3053 msg("<warning> Glyph %d in current charset (%s, %d characters) is empty",
3054 glyph, FIXNULL((char*)i->swffont->name), i->swffont->numchars);
3058 /* calculate character position with respect to the current font matrix */
3059 double s = 20 * GLYPH_SCALE / det;
3060 double px = matrix->tx - i->fontmatrix.tx/20.0;
3061 double py = matrix->ty - i->fontmatrix.ty/20.0;
3062 int x = (SCOORD)(( px * i->fontmatrix.sy/65536.0 - py * i->fontmatrix.r1/65536.0)*s);
3063 int y = (SCOORD)((- px * i->fontmatrix.r0/65536.0 + py * i->fontmatrix.sx/65536.0)*s);
3064 if(x>32767 || x<-32768 || y>32767 || y<-32768) {
3065 msg("<verbose> Moving character origin to %f %f\n", matrix->tx, matrix->ty);
3067 setfontscale(dev, matrix->m00, matrix->m01, matrix->m10, matrix->m11, matrix->tx, matrix->ty, 1);
3068 /* since we just moved the char origin to the current char's position,
3069 it now has the relative position (0,0) */
3078 msg("<trace> Drawing char %d in font %d at %d,%d in color %02x%02x%02x%02x",
3079 glyph, i->swffont->id, x, y, color->r, color->g, color->b, color->a);
3081 if(color->a == 0 && i->config_invisibletexttofront) {
3082 if(i->config_flashversion>=8) {
3083 // use "multiply" blend mode
3084 color->a = color->r = color->g = color->b = 255;
3086 i->topchardata = charbuffer_append(i->topchardata, i->swffont, glyph, x, y, i->current_font_size, *(RGBA*)color, &i->fontmatrix);
3088 i->chardata = charbuffer_append(i->chardata, i->swffont, glyph, x, y, i->current_font_size, *(RGBA*)color, &i->fontmatrix);
3090 swf_FontUseGlyph(i->swffont, glyph);