3 Part of the swftools package.
5 Copyright (c) 2001,2002,2003,2004,2005 Matthias Kramm <kramm@quiss.org>
7 Swftools is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 2 of the License, or
10 (at your option) any later version.
12 Swftools is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
17 You should have received a copy of the GNU General Public License
18 along with swftools; if not, write to the Free Software
19 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
24 #include "../../config.h"
40 #include "../rfxswf.h"
41 #include "../gfxdevice.h"
42 #include "../gfxtools.h"
44 #include "../gfxpoly.h"
47 #define CHARDATAMAX 8192
51 typedef struct _chardata {
53 int fontid; /* TODO: use a SWFFONT instead */
60 typedef struct _fontlist
63 struct _fontlist*next;
66 typedef long int twip;
68 typedef struct _swfmatrix {
69 double m11,m12,m21,m22,m31,m32;
72 typedef struct _swfoutput_internal
74 gfxdevice_t*dev; // the gfxdevice object where this internal struct resides
76 double config_dumpfonts;
77 double config_ppmsubpixels;
78 double config_jpegsubpixels;
81 int config_simpleviewer;
82 int config_opennewwindow;
83 int config_ignoredraworder;
84 int config_drawonlyshapes;
85 int config_frameresets;
86 int config_linknameurl;
87 int config_jpegquality;
88 int config_storeallcharacters;
89 int config_enablezlib;
90 int config_insertstoptag;
93 int config_flashversion;
94 int config_reordertags;
95 int config_showclipshapes;
96 int config_splinemaxerror;
97 int config_fontsplinemaxerror;
98 int config_filloverlap;
101 int config_disable_polygon_conversion;
102 int config_normalize_polygon_positions;
103 char config_disablelinks;
104 RGBA config_linkcolor;
105 float config_minlinewidth;
106 double config_caplinewidth;
107 char* config_linktarget;
108 char*config_internallinkfunction;
109 char*config_externallinkfunction;
111 double config_framerate;
115 fontlist_t* fontlist;
154 int pic_height[1024];
161 char fillstylechanged;
163 int jpeg; //next image type
170 chardata_t chardata[CHARDATAMAX];
177 int current_font_size;
179 double lastfontm11,lastfontm12,lastfontm21,lastfontm22;
190 } swfoutput_internal;
192 static void swf_fillbitmap(gfxdevice_t*driver, gfxline_t*line, gfximage_t*img, gfxmatrix_t*move, gfxcxform_t*cxform);
193 static int swf_setparameter(gfxdevice_t*driver, const char*key, const char*value);
194 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);
195 static void swf_startclip(gfxdevice_t*dev, gfxline_t*line);
196 static void swf_endclip(gfxdevice_t*dev);
197 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);
198 static void swf_fill(gfxdevice_t*dev, gfxline_t*line, gfxcolor_t*color);
199 static void swf_fillbitmap(gfxdevice_t*dev, gfxline_t*line, gfximage_t*img, gfxmatrix_t*matrix, gfxcxform_t*cxform);
200 static void swf_fillgradient(gfxdevice_t*dev, gfxline_t*line, gfxgradient_t*gradient, gfxgradienttype_t type, gfxmatrix_t*matrix);
201 static void swf_drawchar(gfxdevice_t*dev, gfxfont_t*font, int glyph, gfxcolor_t*color, gfxmatrix_t*matrix);
202 static void swf_addfont(gfxdevice_t*dev, gfxfont_t*font);
203 static void swf_drawlink(gfxdevice_t*dev, gfxline_t*line, const char*action);
204 static void swf_startframe(gfxdevice_t*dev, int width, int height);
205 static void swf_endframe(gfxdevice_t*dev);
206 static gfxresult_t* swf_finish(gfxdevice_t*driver);
208 static swfoutput_internal* init_internal_struct()
210 swfoutput_internal*i = (swfoutput_internal*)malloc(sizeof(swfoutput_internal));
211 memset(i, 0, sizeof(swfoutput_internal));
235 i->fillstylechanged = 0;
242 i->config_disablelinks=0;
243 i->config_dumpfonts=0;
244 i->config_ppmsubpixels=0;
245 i->config_jpegsubpixels=0;
246 i->config_opennewwindow=1;
247 i->config_ignoredraworder=0;
248 i->config_drawonlyshapes=0;
249 i->config_jpegquality=85;
250 i->config_storeallcharacters=0;
252 i->config_enablezlib=0;
253 i->config_insertstoptag=0;
254 i->config_flashversion=6;
255 i->config_framerate=0.25;
256 i->config_splinemaxerror=1;
257 i->config_fontsplinemaxerror=1;
258 i->config_filloverlap=0;
260 i->config_bboxvars=0;
261 i->config_showclipshapes=0;
262 i->config_minlinewidth=0.05;
263 i->config_caplinewidth=1;
264 i->config_linktarget=0;
265 i->config_internallinkfunction=0;
266 i->config_externallinkfunction=0;
267 i->config_reordertags=1;
268 i->config_linknameurl=0;
270 i->config_linkcolor.r = i->config_linkcolor.g = i->config_linkcolor.b = 255;
271 i->config_linkcolor.a = 0x40;
276 static int id_error = 0;
278 static U16 getNewID(gfxdevice_t* dev)
280 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
281 if(i->currentswfid == 65535) {
283 msg("<error> ID Table overflow");
284 msg("<error> This file is too complex to render- SWF only supports 65536 shapes at once");
290 return ++i->currentswfid;
292 static U16 getNewDepth(gfxdevice_t* dev)
294 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
295 if(i->depth == 65520) {
297 msg("<error> Depth Table overflow");
298 msg("<error> This file is too complex to render- SWF only supports 65536 shapes at once");
307 static void startshape(gfxdevice_t* dev);
308 static void starttext(gfxdevice_t* dev);
309 static void endshape(gfxdevice_t* dev);
310 static void endtext(gfxdevice_t* dev);
312 typedef struct _plotxy
317 static inline int twipsnap(double f)
319 /* if(f < -0x40000000/20.0) {
320 fprintf(stderr, "Warning: Coordinate underflow (%f)\n", f);
321 f = -0x40000000/20.0;
322 } else if(f>0x3fffffff/20.0) {
323 fprintf(stderr, "Warning: Coordinate overflow (%f)\n", f);
327 /* clamp coordinates to a rectangle with the property that we
328 can represent a line from the upper left corner to the upper
329 right corner using no more than 64 strokes */
330 const double min = -(1<<(18+4))/20.0;
331 const double max = ((1<<(18+4))-1)/20.0;
333 fprintf(stderr, "Warning: Coordinate underflow (%f)\n", f);
336 fprintf(stderr, "Warning: Coordinate overflow (%f)\n", f);
343 // write a move-to command into the swf
344 static int movetoxy(gfxdevice_t*dev, TAG*tag, plotxy_t p0)
346 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
347 int rx = twipsnap(p0.x);
348 int ry = twipsnap(p0.y);
349 if(rx!=i->swflastx || ry!=i->swflasty || i->fillstylechanged) {
350 swf_ShapeSetMove (tag, i->shape, rx,ry);
351 i->fillstylechanged = 0;
358 static int moveto(gfxdevice_t*dev, TAG*tag, double x, double y)
360 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
364 return movetoxy(dev, tag, p);
366 static void addPointToBBox(gfxdevice_t*dev, int px, int py)
368 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
374 swf_ExpandRect(&i->bboxrect, p);
376 swf_ExpandRect3(&i->bboxrect, p, i->linewidth*3/2);
380 /*static void plot(gfxdevice_t*dev, int x, int y, TAG*tag)
382 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
383 int width = i->linewidth/4;
387 //swf_ShapeSetLine(tag, i->shape,-width,-width);
388 //swf_ShapeSetLine(tag, i->shape,width*2,0);
389 //swf_ShapeSetLine(tag, i->shape,0,width*2);
390 //swf_ShapeSetLine(tag, i->shape,-width*2,0);
391 //swf_ShapeSetLine(tag, i->shape,0,-width*2);
392 //swf_ShapeSetLine(tag, i->shape,width,width);
395 swf_ShapeSetLine(tag, i->shape,-width,0);
396 swf_ShapeSetLine(tag, i->shape,width,-width);
397 swf_ShapeSetLine(tag, i->shape,width,width);
398 swf_ShapeSetLine(tag, i->shape,-width,width);
399 swf_ShapeSetLine(tag, i->shape,-width,-width);
400 swf_ShapeSetLine(tag, i->shape,width,0);
402 addPointToBBox(dev, x-width ,y-width);
403 addPointToBBox(dev, x+width ,y+width);
406 // write a line-to command into the swf
407 static void linetoxy(gfxdevice_t*dev, TAG*tag, plotxy_t p0)
409 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
410 int px = twipsnap(p0.x);
411 int py = twipsnap(p0.y);
412 int rx = (px-i->swflastx);
413 int ry = (py-i->swflasty);
415 swf_ShapeSetLine (tag, i->shape, rx,ry);
416 addPointToBBox(dev, i->swflastx,i->swflasty);
417 addPointToBBox(dev, px,py);
418 } /* this is a nice idea, but doesn't work with current flash
419 players (the pixel will be invisible if they're not
420 precisely on a pixel boundary)
421 Besides, we should only do this if this lineto itself
422 is again followed by a "move".
423 else if(!i->fill && i->config_dots) {
424 // treat lines of length 0 as plots, making them
425 // at least 1 twip wide so Flash will display them
426 //plot(dev, i->swflastx, i->swflasty, tag);
427 swf_ShapeSetLine (tag, i->shape, rx+1,ry);
434 static void lineto(gfxdevice_t*dev, TAG*tag, double x, double y)
439 linetoxy(dev,tag, p);
442 // write a spline-to command into the swf
443 static void splineto(gfxdevice_t*dev, TAG*tag, plotxy_t control,plotxy_t end)
445 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
446 int lastlastx = i->swflastx;
447 int lastlasty = i->swflasty;
449 int cx = (twipsnap(control.x)-i->swflastx);
450 int cy = (twipsnap(control.y)-i->swflasty);
453 int ex = (twipsnap(end.x)-i->swflastx);
454 int ey = (twipsnap(end.y)-i->swflasty);
458 if((cx || cy) && (ex || ey)) {
459 swf_ShapeSetCurve(tag, i->shape, cx,cy,ex,ey);
460 addPointToBBox(dev, lastlastx ,lastlasty );
461 addPointToBBox(dev, lastlastx+cx,lastlasty+cy);
462 addPointToBBox(dev, lastlastx+cx+ex,lastlasty+cy+ey);
463 } else if(cx || cy || ex || ey) {
464 swf_ShapeSetLine(tag, i->shape, cx+ex,cy+ey);
465 addPointToBBox(dev, lastlastx ,lastlasty );
466 addPointToBBox(dev, lastlastx+cx,lastlasty+cy);
467 addPointToBBox(dev, lastlastx+cx+ex,lastlasty+cy+ey);
473 /* write a line, given two points and the transformation
475 /*static void line(gfxdevice_t*dev, TAG*tag, plotxy_t p0, plotxy_t p1)
477 moveto(dev, tag, p0);
478 lineto(dev, tag, p1);
481 void resetdrawer(gfxdevice_t*dev)
483 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
488 static void stopFill(gfxdevice_t*dev)
490 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
491 if(i->lastwasfill!=0)
493 swf_ShapeSetStyle(i->tag,i->shape,i->linestyleid,0x8000,0);
494 i->fillstylechanged = 1;
498 static void startFill(gfxdevice_t*dev)
500 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
501 if(i->lastwasfill!=1)
503 swf_ShapeSetStyle(i->tag,i->shape,0x8000,i->fillstyleid,0);
504 i->fillstylechanged = 1;
509 static inline int colorcompare(gfxdevice_t*dev, RGBA*a,RGBA*b)
521 static SRECT getcharacterbbox(gfxdevice_t*dev, SWFFONT*font, MATRIX* m)
523 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
526 memset(&r, 0, sizeof(r));
529 if(debug) printf("\n");
530 for(t=0;t<i->chardatapos;t++)
532 if(i->chardata[t].fontid != font->id) {
533 msg("<error> Internal error: fontid %d != fontid %d", i->chardata[t].fontid, font->id);
536 SRECT b = font->layout->bounds[i->chardata[t].charid];
537 b.xmin *= i->chardata[t].size;
538 b.ymin *= i->chardata[t].size;
539 b.xmax *= i->chardata[t].size;
540 b.ymax *= i->chardata[t].size;
542 /* divide by 1024, rounding xmax/ymax up */
543 b.xmax += 1023; b.ymax += 1023; b.xmin /= 1024; b.ymin /= 1024; b.xmax /= 1024; b.ymax /= 1024;
545 b.xmin += i->chardata[t].x;
546 b.ymin += i->chardata[t].y;
547 b.xmax += i->chardata[t].x;
548 b.ymax += i->chardata[t].y;
550 /* until we solve the INTERNAL_SCALING problem (see below)
551 make sure the bounding box is big enough */
557 b = swf_TurnRect(b, m);
559 if(debug) printf("(%f,%f,%f,%f) -> (%f,%f,%f,%f) [font %d/%d, char %d]\n",
560 font->layout->bounds[i->chardata[t].charid].xmin/20.0,
561 font->layout->bounds[i->chardata[t].charid].ymin/20.0,
562 font->layout->bounds[i->chardata[t].charid].xmax/20.0,
563 font->layout->bounds[i->chardata[t].charid].ymax/20.0,
568 i->chardata[t].fontid,
570 i->chardata[t].charid
572 swf_ExpandRect2(&r, &b);
574 if(debug) printf("-----> (%f,%f,%f,%f)\n",
582 static void putcharacters(gfxdevice_t*dev, TAG*tag)
584 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
588 color.r = i->chardata[0].color.r^255;
597 int charadvance[128];
600 int glyphbits=1; //TODO: can this be zero?
603 if(tag->id != ST_DEFINETEXT &&
604 tag->id != ST_DEFINETEXT2) {
605 msg("<error> internal error: putcharacters needs an text tag, not %d\n",tag->id);
608 if(!i->chardatapos) {
609 msg("<warning> putcharacters called with zero characters");
612 for(pass = 0; pass < 2; pass++)
622 advancebits++; // add sign bit
623 swf_SetU8(tag, glyphbits);
624 swf_SetU8(tag, advancebits);
627 for(t=0;t<=i->chardatapos;t++)
629 if(lastfontid != i->chardata[t].fontid ||
630 lastx!=i->chardata[t].x ||
631 lasty!=i->chardata[t].y ||
632 !colorcompare(dev,&color, &i->chardata[t].color) ||
634 lastsize != i->chardata[t].size ||
637 if(charstorepos && pass==0)
640 for(s=0;s<charstorepos;s++)
642 while(charids[s]>=(1<<glyphbits))
644 while(charadvance[s]>=(1<<advancebits))
648 if(charstorepos && pass==1)
650 tag->writeBit = 0; // Q&D
651 swf_SetBits(tag, 0, 1); // GLYPH Record
652 swf_SetBits(tag, charstorepos, 7); // number of glyphs
654 for(s=0;s<charstorepos;s++)
656 swf_SetBits(tag, charids[s], glyphbits);
657 swf_SetBits(tag, charadvance[s], advancebits);
662 if(pass == 1 && t<i->chardatapos)
668 if(lastx != i->chardata[t].x ||
669 lasty != i->chardata[t].y)
671 newx = i->chardata[t].x;
672 newy = i->chardata[t].y;
678 if(!colorcompare(dev,&color, &i->chardata[t].color))
680 color = i->chardata[t].color;
683 font.id = i->chardata[t].fontid;
684 if(lastfontid != i->chardata[t].fontid || lastsize != i->chardata[t].size)
687 tag->writeBit = 0; // Q&D
688 swf_TextSetInfoRecord(tag, newfont, i->chardata[t].size, newcolor, newx,newy);
691 lastfontid = i->chardata[t].fontid;
692 lastx = i->chardata[t].x;
693 lasty = i->chardata[t].y;
694 lastsize = i->chardata[t].size;
697 if(t==i->chardatapos)
701 int nextt = t==i->chardatapos-1?t:t+1;
702 int rel = i->chardata[nextt].x-i->chardata[t].x;
703 if(rel>=0 && (rel<(1<<(advancebits-1)) || pass==0)) {
705 lastx=i->chardata[nextt].x;
709 lastx=i->chardata[t].x;
711 charids[charstorepos] = i->chardata[t].charid;
712 charadvance[charstorepos] = advance;
719 static void putcharacter(gfxdevice_t*dev, int fontid, int charid, int x,int y, int size, RGBA color)
721 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
722 if(i->chardatapos == CHARDATAMAX)
724 msg("<warning> Character buffer too small. SWF will be slightly bigger");
728 i->chardata[i->chardatapos].fontid = fontid;
729 i->chardata[i->chardatapos].charid = charid;
730 i->chardata[i->chardatapos].x = x;
731 i->chardata[i->chardatapos].y = y;
732 i->chardata[i->chardatapos].color = color;
733 i->chardata[i->chardatapos].size = size;
737 /* Notice: we can only put chars in the range -1639,1638 (-32768/20,32768/20).
738 So if we set this value to high, the char coordinates will overflow.
739 If we set it to low, however, the char positions will be inaccurate */
740 #define GLYPH_SCALE 1
742 static void endtext(gfxdevice_t*dev)
744 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
748 i->tag = swf_InsertTag(i->tag,ST_DEFINETEXT2);
749 swf_SetU16(i->tag, i->textid);
752 r = getcharacterbbox(dev, i->swffont, &i->fontmatrix);
753 r = swf_ClipRect(i->pagebbox, r);
754 swf_SetRect(i->tag,&r);
756 swf_SetMatrix(i->tag,&i->fontmatrix);
758 msg("<trace> Placing text (%d characters) as ID %d", i->chardatapos, i->textid);
760 putcharacters(dev, i->tag);
763 if(i->swf->fileVersion >= 8) {
764 i->tag = swf_InsertTag(i->tag, ST_CSMTEXTSETTINGS);
765 swf_SetU16(i->tag, i->textid);
767 //swf_SetU8(i->tag, /*subpixel grid*/(2<<3)|/*flashtype*/0x40);
768 swf_SetU8(i->tag, /*grid*/(1<<3)|/*flashtype*/0x40);
769 //swf_SetU8(i->tag, /*no grid*/(0<<3)|/*flashtype*/0x40);
771 swf_SetU32(i->tag, 0);//thickness
772 swf_SetU32(i->tag, 0);//sharpness
773 swf_SetU8(i->tag, 0);//reserved
775 i->tag = swf_InsertTag(i->tag,ST_PLACEOBJECT2);
777 swf_ObjectPlace(i->tag,i->textid,getNewDepth(dev),&i->page_matrix,NULL,NULL);
781 /* set's the matrix which is to be applied to characters drawn by swfoutput_drawchar() */
782 static void setfontscale(gfxdevice_t*dev,double m11,double m12, double m21,double m22,double x, double y, char force)
788 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
789 if(i->lastfontm11 == m11 &&
790 i->lastfontm12 == m12 &&
791 i->lastfontm21 == m21 &&
792 i->lastfontm22 == m22 && !force)
797 i->lastfontm11 = m11;
798 i->lastfontm12 = m12;
799 i->lastfontm21 = m21;
800 i->lastfontm22 = m22;
802 double xsize = sqrt(m11*m11 + m12*m12);
803 double ysize = sqrt(m21*m21 + m22*m22);
804 i->current_font_size = (xsize>ysize?xsize:ysize)*1;
805 if(i->current_font_size < 1)
806 i->current_font_size = 1;
807 double ifs = 1.0 / (i->current_font_size*GLYPH_SCALE);
810 m.sx = (S32)((m11*ifs)*65536); m.r1 = (S32)((m21*ifs)*65536);
811 m.r0 = (S32)((m12*ifs)*65536); m.sy = (S32)((m22*ifs)*65536);
812 /* this is the position of the first char to set a new fontmatrix-
813 we hope that it's close enough to all other characters using the
814 font, so we use its position as origin for the matrix */
820 static int watermark2_width=47;
821 static int watermark2_height=11;
822 static int watermark2[47] = {95,1989,71,0,2015,337,1678,0,2015,5,1921,320,1938,25,2006,1024,
823 1042,21,13,960,1039,976,8,2000,1359,1088,31,1989,321,1728,0,1152,
824 1344,832,0,1984,0,896,1088,1088,896,0,1984,128,256,512,1984};
826 static void draw_watermark(gfxdevice_t*dev, gfxbbox_t r, char drawall)
828 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
829 double wx = r.xmax / 5.0;
830 double tx = r.xmax*4.0 / 5.0;
831 double ty = r.ymax-wx*watermark2_height/watermark2_width;
832 double sx = (r.xmax - tx) / watermark2_width;
833 double sy = (r.ymax - ty) / watermark2_height;
836 if(ty > 0 && px > 1.0 && py > 1.0) {
838 for(y=0;y<watermark2_height;y++)
839 for(x=0;x<watermark2_width;x++) {
840 if(((watermark2[x]>>y)&1)) {
841 if(!drawall && rand()%5)
843 unsigned int b = rand();
844 moveto(dev, i->tag, x*sx+tx+((b>>1)&1)/20.0, y*sy+ty+((b>>3)&1)/20.0);
845 lineto(dev, i->tag, x*sx+px+tx+((b>>2)&1)/20.0, y*sy+ty+((b>>3)&1)/20.0);
846 lineto(dev, i->tag, x*sx+px+tx+((b>>2)&1)/20.0, y*sy+py+ty+((b>>4)&1)/20.0);
847 lineto(dev, i->tag, x*sx+tx+((b>>1)&1)/20.0, y*sy+py+ty+((b>>4)&1)/20.0);
848 lineto(dev, i->tag, x*sx+tx+((b>>1)&1)/20.0, y*sy+ty+((b>>3)&1)/20.0);
854 static void swfoutput_setfillcolor(gfxdevice_t* dev, U8 r, U8 g, U8 b, U8 a)
856 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
857 if(i->fillrgb.r == r &&
860 i->fillrgb.a == a) return;
869 static void insert_watermark(gfxdevice_t*dev, char drawall)
871 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
872 if(!drawall && i->watermarks>20)
878 swfoutput_setfillcolor(dev, 0,0,255,192);
880 swfoutput_setfillcolor(dev, rand(),rand(),rand(),(rand()&127)+128);
885 gfxbbox_t r; r.xmin = r.ymin = 0;
888 draw_watermark(dev, r, drawall);
894 static void endpage(gfxdevice_t*dev)
896 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
907 if(i->config_watermark) {
908 insert_watermark(dev, 1);
914 static void addViewer(gfxdevice_t* dev)
916 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
919 RGBA button_colors[3]= {{0xbf,0x00,0x00,0x80},{0xbf,0x20,0x20,0xc0}, {0xbf,0xc0,0xc0,0xff}};
921 int button_sizex = 20;
922 int button_sizey = 20;
924 RGBA black = {255,0,0,0};
926 i->tag = swf_InsertTag(i->tag,ST_DEFINESHAPE3);
928 int ls1 = swf_ShapeAddLineStyle(s,40,&black);
929 int fs1 = swf_ShapeAddSolidFillStyle(s,&button_colors[t/2]);
930 int shapeid = ids[t] = getNewID(dev);
931 swf_SetU16(i->tag,shapeid);
933 r.xmin = -20*button_sizex;
934 r.xmax = 20*button_sizex;
936 r.ymax = 40*button_sizey;
937 swf_SetRect(i->tag,&r); // set shape bounds
938 swf_SetShapeHeader(i->tag,s); // write all styles to tag
939 swf_ShapeSetAll(i->tag,s,0*button_sizex,0,ls1,fs1,0);
940 swf_ShapeSetLine(i->tag,s,(1-(t&1)*2)*20*button_sizex,20*button_sizey);
941 swf_ShapeSetLine(i->tag,s,-(1-(t&1)*2)*20*button_sizex,20*button_sizey);
942 swf_ShapeSetLine(i->tag,s,0,-40*button_sizey);
943 swf_ShapeSetEnd(i->tag); // finish drawing
944 swf_ShapeFree(s); // clean shape structure (which isn't needed anymore after writing the tag)
946 ActionTAG*a1=0,*a2=0,*a3=0;
947 a1 = action_NextFrame(a1);
948 a1 = action_Stop(a1);
951 a2 = action_PreviousFrame(a2);
952 a2 = action_Stop(a2);
955 a3 = action_Stop(a3);
958 i->tag = swf_InsertTag(i->tag, ST_DOACTION);
959 swf_ActionSet(i->tag,a3);
961 i->tag = swf_InsertTag(i->tag,ST_DEFINEBUTTON);
962 int buttonid1 = getNewID(dev);
963 swf_SetU16(i->tag, buttonid1);
964 swf_ButtonSetRecord(i->tag,BS_UP|BS_HIT,ids[0],1,NULL,NULL);
965 swf_ButtonSetRecord(i->tag,BS_OVER,ids[2],1,NULL,NULL);
966 swf_ButtonSetRecord(i->tag,BS_DOWN,ids[4],1,NULL,NULL);
967 swf_SetU8(i->tag,0); // end of button records
968 swf_ActionSet(i->tag,a1);
970 i->tag = swf_InsertTag(i->tag,ST_DEFINEBUTTON);
971 int buttonid2 = getNewID(dev);
972 swf_SetU16(i->tag, buttonid2);
973 swf_ButtonSetRecord(i->tag,BS_UP|BS_HIT,ids[1],1,NULL,NULL);
974 swf_ButtonSetRecord(i->tag,BS_OVER,ids[3],1,NULL,NULL);
975 swf_ButtonSetRecord(i->tag,BS_DOWN,ids[5],1,NULL,NULL);
976 swf_SetU8(i->tag,0); // end of button records
977 swf_ActionSet(i->tag,a2);
979 i->tag = swf_InsertTag(i->tag,ST_PLACEOBJECT2);
981 swf_GetMatrix(0, &m);
982 m.tx = button_sizex*20+200;
983 swf_ObjectPlace(i->tag, buttonid2, 65534,&m,0,0);
984 i->tag = swf_InsertTag(i->tag,ST_PLACEOBJECT2);
985 m.tx = button_sizex*20+200+200;
986 swf_ObjectPlace(i->tag, buttonid1, 65535,&m,0,0);
990 void swf_startframe(gfxdevice_t*dev, int width, int height)
992 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
994 if(i->config_protect) {
995 i->tag = swf_InsertTag(i->tag, ST_PROTECT);
996 i->config_protect = 0;
998 if(i->config_simpleviewer) {
1003 if(!i->firstpage && !i->pagefinished)
1006 msg("<verbose> Starting new SWF page of size %dx%d", width, height);
1008 swf_GetMatrix(0, &i->page_matrix);
1009 i->page_matrix.tx = 0;
1010 i->page_matrix.ty = 0;
1017 /* create a bbox structure with the page size. This is used
1018 for clipping shape and text bounding boxes. As we don't want to
1019 generate bounding boxes which extend beyond the movie size (in
1020 order to not confuse Flash), we clip everything against i->pagebbox */
1021 i->pagebbox.xmin = 0;
1022 i->pagebbox.ymin = 0;
1023 i->pagebbox.xmax = width*20;
1024 i->pagebbox.ymax = height*20;
1026 /* increase SWF's bounding box */
1027 swf_ExpandRect2(&i->swf->movieSize, &i->pagebbox);
1029 i->lastframeno = i->frameno;
1031 i->pagefinished = 0;
1034 void swf_endframe(gfxdevice_t*dev)
1036 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1038 if(!i->pagefinished)
1041 if( (i->swf->fileVersion <= 8) && (i->config_insertstoptag) ) {
1043 atag = action_Stop(atag);
1044 atag = action_End(atag);
1045 i->tag = swf_InsertTag(i->tag,ST_DOACTION);
1046 swf_ActionSet(i->tag,atag);
1048 i->tag = swf_InsertTag(i->tag,ST_SHOWFRAME);
1051 for(i->depth;i->depth>i->startdepth;i->depth--) {
1052 i->tag = swf_InsertTag(i->tag,ST_REMOVEOBJECT2);
1053 swf_SetU16(i->tag,i->depth);
1055 i->depth = i->startdepth;
1057 if(i->config_frameresets) {
1058 for(i->currentswfid;i->currentswfid>i->startids;i->currentswfid--) {
1059 i->tag = swf_InsertTag(i->tag,ST_FREECHARACTER);
1060 swf_SetU16(i->tag,i->currentswfid);
1062 i->currentswfid = i->startids;
1066 static void setBackground(gfxdevice_t*dev, int x1, int y1, int x2, int y2)
1068 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1070 rgb.a = rgb.r = rgb.g = rgb.b = 0xff;
1074 int shapeid = getNewID(dev);
1079 i->tag = swf_InsertTag(i->tag, ST_DEFINESHAPE);
1081 fs1 = swf_ShapeAddSolidFillStyle(s, &rgb);
1082 swf_SetU16(i->tag,shapeid);
1083 swf_SetRect(i->tag,&r);
1084 swf_SetShapeHeader(i->tag,s);
1085 swf_ShapeSetAll(i->tag,s,x1,y1,ls1,fs1,0);
1086 swf_ShapeSetLine(i->tag,s,(x2-x1),0);
1087 swf_ShapeSetLine(i->tag,s,0,(y2-y1));
1088 swf_ShapeSetLine(i->tag,s,(x1-x2),0);
1089 swf_ShapeSetLine(i->tag,s,0,(y1-y2));
1090 swf_ShapeSetEnd(i->tag);
1092 i->tag = swf_InsertTag(i->tag, ST_PLACEOBJECT2);
1093 swf_ObjectPlace(i->tag,shapeid,getNewDepth(dev),0,0,0);
1094 i->tag = swf_InsertTag(i->tag, ST_PLACEOBJECT2);
1095 swf_ObjectPlaceClip(i->tag,shapeid,getNewDepth(dev),0,0,0,65535);
1098 /* initialize the swf writer */
1099 void gfxdevice_swf_init(gfxdevice_t* dev)
1101 memset(dev, 0, sizeof(gfxdevice_t));
1105 dev->internal = init_internal_struct(); // set config to default values
1107 dev->startpage = swf_startframe;
1108 dev->endpage = swf_endframe;
1109 dev->finish = swf_finish;
1110 dev->fillbitmap = swf_fillbitmap;
1111 dev->setparameter = swf_setparameter;
1112 dev->stroke = swf_stroke;
1113 dev->startclip = swf_startclip;
1114 dev->endclip = swf_endclip;
1115 dev->fill = swf_fill;
1116 dev->fillbitmap = swf_fillbitmap;
1117 dev->fillgradient = swf_fillgradient;
1118 dev->addfont = swf_addfont;
1119 dev->drawchar = swf_drawchar;
1120 dev->drawlink = swf_drawlink;
1122 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1125 msg("<verbose> initializing swf output\n", i->max_x,i->max_y);
1129 i->swf = (SWF*)rfx_calloc(sizeof(SWF));
1130 i->swf->fileVersion = 0;
1131 i->swf->frameRate = 0x80;
1132 i->swf->movieSize.xmin = 0;
1133 i->swf->movieSize.ymin = 0;
1134 i->swf->movieSize.xmax = 0;
1135 i->swf->movieSize.ymax = 0;
1136 i->swf->fileAttributes = 9; // as3, local-with-network
1138 i->swf->firstTag = swf_InsertTag(NULL,ST_SETBACKGROUNDCOLOR);
1139 i->tag = i->swf->firstTag;
1141 rgb.a = rgb.r = rgb.g = rgb.b = 0xff;
1143 swf_SetRGB(i->tag,&rgb);
1145 i->startdepth = i->depth = 0;
1146 i->startids = i->currentswfid = 0;
1149 static void startshape(gfxdevice_t*dev)
1151 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1156 //if(i->chardatapos && i->chardata[i->chardatapos-1].color.a)
1159 i->tag = swf_InsertTag(i->tag,ST_DEFINESHAPE3);
1161 swf_ShapeNew(&i->shape);
1162 i->linestyleid = swf_ShapeAddLineStyle(i->shape,i->linewidth,&i->strokergb);
1163 i->fillstyleid = swf_ShapeAddSolidFillStyle(i->shape,&i->fillrgb);
1165 RGBA markcol = {0,i->mark[0],i->mark[1],i->mark[2]};
1166 swf_ShapeAddSolidFillStyle(i->shape,&markcol);
1169 i->shapeid = getNewID(dev);
1171 msg("<debug> Using shape id %d", i->shapeid);
1173 swf_SetU16(i->tag,i->shapeid); // ID
1175 i->bboxrectpos = i->tag->len;
1177 swf_SetRect(i->tag,&i->pagebbox);
1179 memset(&i->bboxrect, 0, sizeof(i->bboxrect));
1181 swf_SetShapeStyles(i->tag,i->shape);
1182 swf_ShapeCountBits(i->shape,NULL,NULL);
1183 swf_SetShapeBits(i->tag,i->shape);
1185 /* TODO: do we really need this? */
1186 //swf_ShapeSetAll(i->tag,i->shape,/*x*/0,/*y*/0,i->linestyleid,0,0);
1187 //swf_ShapeSetAll(i->tag,i->shape,/*x*/UNDEFINED_COORD,/*y*/UNDEFINED_COORD,i->linestyleid,0,0);
1188 i->swflastx=i->swflasty=UNDEFINED_COORD;
1189 i->lastwasfill = -1;
1190 i->shapeisempty = 1;
1193 static void starttext(gfxdevice_t*dev)
1195 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1199 if(i->config_watermark) {
1200 insert_watermark(dev, 0);
1202 i->textid = getNewID(dev);
1203 i->swflastx=i->swflasty=0;
1207 /* TODO: move to ../lib/rfxswf */
1208 void changeRect(gfxdevice_t*dev, TAG*tag, int pos, SRECT*newrect)
1210 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1211 /* determine length of old rect */
1215 swf_GetRect(tag, &old);
1216 swf_ResetReadBits(tag);
1217 int pos_end = tag->pos;
1219 int len = tag->len - pos_end;
1220 U8*data = (U8*)malloc(len);
1221 memcpy(data, &tag->data[pos_end], len);
1224 swf_SetRect(tag, newrect);
1225 swf_SetBlock(tag, data, len);
1227 tag->pos = tag->readBit = 0;
1230 void cancelshape(gfxdevice_t*dev)
1232 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1233 /* delete old shape tag */
1235 i->tag = i->tag->prev;
1236 swf_DeleteTag(0, todel);
1237 if(i->shape) {swf_ShapeFree(i->shape);i->shape=0;}
1239 i->bboxrectpos = -1;
1241 // i->currentswfid--; // doesn't work, for some reason
1244 void fixAreas(gfxdevice_t*dev)
1246 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1247 if(!i->shapeisempty && i->fill &&
1248 (i->bboxrect.xmin == i->bboxrect.xmax ||
1249 i->bboxrect.ymin == i->bboxrect.ymax) &&
1250 i->config_minlinewidth >= 0.001
1252 msg("<debug> Shape has size 0: width=%.2f height=%.2f",
1253 (i->bboxrect.xmax-i->bboxrect.xmin)/20.0,
1254 (i->bboxrect.ymax-i->bboxrect.ymin)/20.0
1257 SRECT r = i->bboxrect;
1259 if(r.xmin == r.xmax && r.ymin == r.ymax) {
1260 /* this thing comes down to a single dot- nothing to fix here */
1266 RGBA save_col = i->strokergb;
1267 int save_width = i->linewidth;
1269 i->strokergb = i->fillrgb;
1270 i->linewidth = (int)(i->config_minlinewidth*20);
1271 if(i->linewidth==0) i->linewidth = 1;
1276 moveto(dev, i->tag, r.xmin/20.0,r.ymin/20.0);
1277 lineto(dev, i->tag, r.xmax/20.0,r.ymax/20.0);
1279 i->strokergb = save_col;
1280 i->linewidth = save_width;
1285 static void endshape_noput(gfxdevice_t*dev)
1287 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1290 //changeRect(dev, i->tag, i->bboxrectpos, &i->bboxrect);
1293 swf_ShapeFree(i->shape);
1301 static void endshape(gfxdevice_t*dev)
1303 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1309 if(i->shapeisempty ||
1311 (i->bboxrect.xmin == i->bboxrect.xmax &&
1312 i->bboxrect.ymin == i->bboxrect.ymax))
1314 // delete the shape again, we didn't do anything
1315 msg("<debug> cancelling shape: bbox is (%f,%f,%f,%f)",
1316 i->bboxrect.xmin /20.0,
1317 i->bboxrect.ymin /20.0,
1318 i->bboxrect.xmax /20.0,
1319 i->bboxrect.ymax /20.0
1325 swf_ShapeSetEnd(i->tag);
1327 SRECT r = swf_ClipRect(i->pagebbox, i->bboxrect);
1328 changeRect(dev, i->tag, i->bboxrectpos, &r);
1330 msg("<trace> Placing shape ID %d", i->shapeid);
1332 i->tag = swf_InsertTag(i->tag,ST_PLACEOBJECT2);
1333 MATRIX m = i->page_matrix;
1334 m.tx += i->shapeposx;
1335 m.ty += i->shapeposy;
1336 swf_ObjectPlace(i->tag,i->shapeid,getNewDepth(dev),&m,NULL,NULL);
1338 if(i->config_animate) {
1339 i->tag = swf_InsertTag(i->tag,ST_SHOWFRAME);
1342 swf_ShapeFree(i->shape);
1345 i->bboxrectpos = -1;
1352 void wipeSWF(SWF*swf)
1354 TAG*tag = swf->firstTag;
1356 TAG*next = tag->next;
1357 if(tag->id != ST_SETBACKGROUNDCOLOR &&
1358 tag->id != ST_END &&
1359 tag->id != ST_DOACTION &&
1360 tag->id != ST_SHOWFRAME) {
1361 swf_DeleteTag(swf, tag);
1367 void swfoutput_finalize(gfxdevice_t*dev)
1369 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1371 if(i->tag && i->tag->id == ST_END)
1372 return; //already done
1374 i->swf->fileVersion = i->config_flashversion;
1375 i->swf->frameRate = i->config_framerate*0x100;
1377 if(i->config_bboxvars) {
1378 TAG* tag = swf_InsertTag(i->swf->firstTag, ST_DOACTION);
1380 a = action_PushString(a, "xmin");
1381 a = action_PushFloat(a, i->swf->movieSize.xmin / 20.0);
1382 a = action_SetVariable(a);
1383 a = action_PushString(a, "ymin");
1384 a = action_PushFloat(a, i->swf->movieSize.ymin / 20.0);
1385 a = action_SetVariable(a);
1386 a = action_PushString(a, "xmax");
1387 a = action_PushFloat(a, i->swf->movieSize.xmax / 20.0);
1388 a = action_SetVariable(a);
1389 a = action_PushString(a, "ymax");
1390 a = action_PushFloat(a, i->swf->movieSize.ymax / 20.0);
1391 a = action_SetVariable(a);
1392 a = action_PushString(a, "width");
1393 a = action_PushFloat(a, (i->swf->movieSize.xmax - i->swf->movieSize.xmin) / 20.0);
1394 a = action_SetVariable(a);
1395 a = action_PushString(a, "height");
1396 a = action_PushFloat(a, (i->swf->movieSize.ymax - i->swf->movieSize.ymin) / 20.0);
1397 a = action_SetVariable(a);
1399 swf_ActionSet(tag, a);
1404 free(i->mark);i->mark = 0;
1408 fontlist_t *iterator = i->fontlist;
1410 TAG*mtag = i->swf->firstTag;
1411 if(iterator->swffont) {
1412 if(!i->config_storeallcharacters) {
1413 msg("<debug> Reducing font %s", iterator->swffont->name);
1414 swf_FontReduce(iterator->swffont);
1416 int used = iterator->swffont->use && iterator->swffont->use->used_glyphs;
1418 mtag = swf_InsertTag(mtag, ST_DEFINEFONT2);
1419 swf_FontSetDefine2(mtag, iterator->swffont);
1423 iterator = iterator->next;
1426 i->tag = swf_InsertTag(i->tag,ST_END);
1427 TAG* tag = i->tag->prev;
1429 /* remove the removeobject2 tags between the last ST_SHOWFRAME
1430 and the ST_END- they confuse the flash player */
1431 while(tag->id == ST_REMOVEOBJECT2) {
1432 TAG* prev = tag->prev;
1433 swf_DeleteTag(i->swf, tag);
1440 if(i->config_enablezlib || i->config_flashversion>=6) {
1441 i->swf->compressed = 1;
1444 /* Add AVM2 actionscript */
1445 if(i->config_flashversion>=9 &&
1446 (i->config_insertstoptag || i->hasbuttons)) {
1447 swf_AddButtonLinks(i->swf, i->config_insertstoptag,
1448 i->config_internallinkfunction||i->config_externallinkfunction);
1450 // if(i->config_reordertags)
1451 // swf_Optimize(i->swf);
1454 int swfresult_save(gfxresult_t*gfx, const char*filename)
1456 SWF*swf = (SWF*)gfx->internal;
1459 fi = open(filename, O_BINARY|O_CREAT|O_TRUNC|O_WRONLY, 0777);
1464 msg("<fatal> Could not create \"%s\". ", FIXNULL(filename));
1468 if FAILED(swf_WriteSWF(fi,swf))
1469 msg("<error> WriteSWF() failed.\n");
1475 void* swfresult_get(gfxresult_t*gfx, const char*name)
1477 SWF*swf = (SWF*)gfx->internal;
1478 if(!strcmp(name, "swf")) {
1479 return (void*)swf_CopySWF(swf);
1480 } else if(!strcmp(name, "xmin")) {
1481 return (void*)(ptroff_t)(swf->movieSize.xmin/20);
1482 } else if(!strcmp(name, "ymin")) {
1483 return (void*)(ptroff_t)(swf->movieSize.ymin/20);
1484 } else if(!strcmp(name, "xmax")) {
1485 return (void*)(ptroff_t)(swf->movieSize.xmax/20);
1486 } else if(!strcmp(name, "ymax")) {
1487 return (void*)(ptroff_t)(swf->movieSize.ymax/20);
1488 } else if(!strcmp(name, "width")) {
1489 return (void*)(ptroff_t)((swf->movieSize.xmax - swf->movieSize.xmin)/20);
1490 } else if(!strcmp(name, "height")) {
1491 return (void*)(ptroff_t)((swf->movieSize.ymax - swf->movieSize.ymin)/20);
1495 void swfresult_destroy(gfxresult_t*gfx)
1498 swf_FreeTags((SWF*)gfx->internal);
1499 free(gfx->internal);
1502 memset(gfx, 0, sizeof(gfxresult_t));
1506 static void swfoutput_destroy(gfxdevice_t* dev);
1508 gfxresult_t* swf_finish(gfxdevice_t* dev)
1510 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1513 if(i->config_linktarget) {
1514 free(i->config_linktarget);
1515 i->config_linktarget = 0;
1518 swfoutput_finalize(dev);
1519 SWF* swf = i->swf;i->swf = 0;
1520 swfoutput_destroy(dev);
1522 result = (gfxresult_t*)rfx_calloc(sizeof(gfxresult_t));
1523 result->internal = swf;
1524 result->save = swfresult_save;
1526 result->get = swfresult_get;
1527 result->destroy = swfresult_destroy;
1531 /* Perform cleaning up */
1532 static void swfoutput_destroy(gfxdevice_t* dev)
1534 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1536 /* not initialized yet- nothing to destroy */
1540 fontlist_t *tmp,*iterator = i->fontlist;
1542 if(iterator->swffont) {
1543 swf_FontFree(iterator->swffont);iterator->swffont=0;
1546 iterator = iterator->next;
1549 if(i->swf) {swf_FreeTags(i->swf);free(i->swf);i->swf = 0;}
1552 memset(dev, 0, sizeof(gfxdevice_t));
1555 static void swfoutput_setstrokecolor(gfxdevice_t* dev, U8 r, U8 g, U8 b, U8 a)
1557 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1558 if(i->strokergb.r == r &&
1559 i->strokergb.g == g &&
1560 i->strokergb.b == b &&
1561 i->strokergb.a == a) return;
1571 //#define ROUND_UP 19
1572 //#define ROUND_UP 10
1574 static void swfoutput_setlinewidth(gfxdevice_t*dev, double _linewidth)
1576 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1577 if(i->linewidth == (U16)(_linewidth*20+19.0/20.0))
1581 i->linewidth = (U16)(_linewidth*20+19.0/20.0);
1585 static void drawlink(gfxdevice_t*dev, ActionTAG*,ActionTAG*, gfxline_t*points, char mouseover, const char*url);
1586 static void swfoutput_namedlink(gfxdevice_t*dev, char*name, gfxline_t*points);
1587 static void swfoutput_linktopage(gfxdevice_t*dev, int page, gfxline_t*points);
1588 static void swfoutput_linktourl(gfxdevice_t*dev, const char*url, gfxline_t*points);
1590 /*void swfoutput_drawlink(gfxdevice_t*dev, char*url, gfxline_t*points)
1592 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1593 dev->drawlink(dev, points, url);
1596 void swf_drawlink(gfxdevice_t*dev, gfxline_t*points, const char*url)
1598 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1600 if(i->config_disablelinks)
1603 if(!strncmp("http://pdf2swf:", url, 15)) {
1604 char*tmp = strdup(url);
1605 int l = strlen(tmp);
1608 swfoutput_namedlink(dev, tmp+15, points);
1611 } else if(!strncmp("page", url, 4)) {
1614 if(url[t]<'0' || url[t]>'9')
1617 int page = atoi(&url[4]);
1618 if(page<0) page = 0;
1619 swfoutput_linktopage(dev, page, points);
1622 swfoutput_linktourl(dev, url, points);
1625 void swfoutput_linktourl(gfxdevice_t*dev, const char*url, gfxline_t*points)
1627 ActionTAG* actions = 0;
1628 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1634 /* TODO: escape special characters in url */
1636 if(i->config_externallinkfunction && i->config_flashversion<=8) {
1637 actions = action_PushString(actions, url); //parameter
1638 actions = action_PushInt(actions, 1); //number of parameters (1)
1639 actions = action_PushString(actions, i->config_externallinkfunction); //function name
1640 actions = action_CallFunction(actions);
1641 } else if(!i->config_linktarget) {
1642 if(!i->config_opennewwindow)
1643 actions = action_GetUrl(actions, url, "_parent");
1645 actions = action_GetUrl(actions, url, "_this");
1647 actions = action_GetUrl(actions, url, i->config_linktarget);
1649 actions = action_End(actions);
1651 drawlink(dev, actions, 0, points, 0, url);
1653 swf_ActionFree(actions);
1655 void swfoutput_linktopage(gfxdevice_t*dev, int page, gfxline_t*points)
1657 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1658 ActionTAG* actions = 0;
1665 if(!i->config_internallinkfunction || i->config_flashversion>=9) {
1666 actions = action_GotoFrame(actions, page-1);
1667 actions = action_End(actions);
1669 actions = action_PushInt(actions, page); //parameter
1670 actions = action_PushInt(actions, 1); //number of parameters (1)
1671 actions = action_PushString(actions, i->config_internallinkfunction); //function name
1672 actions = action_CallFunction(actions);
1673 actions = action_End(actions);
1677 sprintf(name, "page%d", page);
1679 drawlink(dev, actions, 0, points, 0, name);
1681 swf_ActionFree(actions);
1684 /* Named Links (a.k.a. Acrobatmenu) are used to implement various gadgets
1685 of the viewer objects, like subtitles, index elements etc.
1687 void swfoutput_namedlink(gfxdevice_t*dev, char*name, gfxline_t*points)
1689 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1690 ActionTAG *actions1,*actions2;
1691 char*tmp = strdup(name);
1699 if(!strncmp(tmp, "call:", 5))
1701 char*x = strchr(&tmp[5], ':');
1703 actions1 = action_PushInt(0, 0); //number of parameters (0)
1704 actions1 = action_PushString(actions1, &tmp[5]); //function name
1705 actions1 = action_CallFunction(actions1);
1706 actions1 = action_End(actions1);
1709 actions1 = action_PushString(0, x+1); //parameter
1710 actions1 = action_PushInt(actions1, 1); //number of parameters (1)
1711 actions1 = action_PushString(actions1, &tmp[5]); //function name
1712 actions1 = action_CallFunction(actions1);
1713 actions1 = action_End(actions1);
1715 actions2 = action_End(0);
1720 actions1 = action_PushString(0, "/:subtitle");
1721 actions1 = action_PushString(actions1, name);
1722 actions1 = action_SetVariable(actions1);
1723 actions1 = action_End(actions1);
1725 actions2 = action_PushString(0, "/:subtitle");
1726 actions2 = action_PushString(actions2, "");
1727 actions2 = action_SetVariable(actions2);
1728 actions2 = action_End(actions2);
1731 drawlink(dev, actions1, actions2, points, mouseover, name);
1733 swf_ActionFree(actions1);
1734 swf_ActionFree(actions2);
1738 static void drawgfxline(gfxdevice_t*dev, gfxline_t*line, int fill)
1740 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1741 gfxcoord_t lastx=0,lasty=0,px=0,py=0;
1743 int lines= 0, splines=0;
1750 /* check whether the next segment is zero */
1751 if(line->type == gfx_moveTo) {
1752 moveto(dev, i->tag, line->x, line->y);
1753 px = lastx = line->x;
1754 py = lasty = line->y;
1756 } if(line->type == gfx_lineTo) {
1757 lineto(dev, i->tag, line->x, line->y);
1762 } else if(line->type == gfx_splineTo) {
1764 s.x = line->sx;p.x = line->x;
1765 s.y = line->sy;p.y = line->y;
1766 splineto(dev, i->tag, s, p);
1774 msg("<trace> drawgfxline, %d lines, %d splines", lines, splines);
1778 static void drawlink(gfxdevice_t*dev, ActionTAG*actions1, ActionTAG*actions2, gfxline_t*points, char mouseover, const char*url)
1780 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1789 int buttonid = getNewID(dev);
1790 gfxbbox_t bbox = gfxline_getbbox(points);
1795 myshapeid = getNewID(dev);
1796 i->tag = swf_InsertTag(i->tag,ST_DEFINESHAPE3);
1797 swf_ShapeNew(&i->shape);
1798 rgb.r = rgb.b = rgb.a = rgb.g = 0;
1799 fsid = swf_ShapeAddSolidFillStyle(i->shape,&rgb);
1800 swf_SetU16(i->tag, myshapeid);
1801 r.xmin = (int)(bbox.xmin*20);
1802 r.ymin = (int)(bbox.ymin*20);
1803 r.xmax = (int)(bbox.xmax*20);
1804 r.ymax = (int)(bbox.ymax*20);
1805 r = swf_ClipRect(i->pagebbox, r);
1806 swf_SetRect(i->tag,&r);
1807 swf_SetShapeStyles(i->tag,i->shape);
1808 swf_ShapeCountBits(i->shape,NULL,NULL);
1809 swf_SetShapeBits(i->tag,i->shape);
1810 swf_ShapeSetAll(i->tag,i->shape,/*x*/0,/*y*/0,0,fsid,0);
1811 i->swflastx = i->swflasty = 0;
1812 drawgfxline(dev, points, 1);
1813 swf_ShapeSetEnd(i->tag);
1814 swf_ShapeFree(i->shape);
1817 myshapeid2 = getNewID(dev);
1818 i->tag = swf_InsertTag(i->tag,ST_DEFINESHAPE3);
1819 swf_ShapeNew(&i->shape);
1821 rgb = i->config_linkcolor;
1823 fsid = swf_ShapeAddSolidFillStyle(i->shape,&rgb);
1824 swf_SetU16(i->tag, myshapeid2);
1825 r.xmin = (int)(bbox.xmin*20);
1826 r.ymin = (int)(bbox.ymin*20);
1827 r.xmax = (int)(bbox.xmax*20);
1828 r.ymax = (int)(bbox.ymax*20);
1829 r = swf_ClipRect(i->pagebbox, r);
1830 swf_SetRect(i->tag,&r);
1831 swf_SetShapeStyles(i->tag,i->shape);
1832 swf_ShapeCountBits(i->shape,NULL,NULL);
1833 swf_SetShapeBits(i->tag,i->shape);
1834 swf_ShapeSetAll(i->tag,i->shape,/*x*/0,/*y*/0,0,fsid,0);
1835 i->swflastx = i->swflasty = 0;
1836 drawgfxline(dev, points, 1);
1837 swf_ShapeSetEnd(i->tag);
1838 swf_ShapeFree(i->shape);
1842 i->tag = swf_InsertTag(i->tag,ST_DEFINEBUTTON);
1843 swf_SetU16(i->tag,buttonid); //id
1844 swf_ButtonSetFlags(i->tag, 0); //menu=no
1845 swf_ButtonSetRecord(i->tag,0x01,myshapeid,i->depth,0,0);
1846 swf_ButtonSetRecord(i->tag,0x02,myshapeid2,i->depth,0,0);
1847 swf_ButtonSetRecord(i->tag,0x04,myshapeid2,i->depth,0,0);
1848 swf_ButtonSetRecord(i->tag,0x08,myshapeid,i->depth,0,0);
1849 swf_SetU8(i->tag,0);
1850 swf_ActionSet(i->tag,actions1);
1851 swf_SetU8(i->tag,0);
1855 i->tag = swf_InsertTag(i->tag,ST_DEFINEBUTTON2);
1856 swf_SetU16(i->tag,buttonid); //id
1857 swf_ButtonSetFlags(i->tag, 0); //menu=no
1858 swf_ButtonSetRecord(i->tag,0x01,myshapeid,i->depth,0,0);
1859 swf_ButtonSetRecord(i->tag,0x02,myshapeid2,i->depth,0,0);
1860 swf_ButtonSetRecord(i->tag,0x04,myshapeid2,i->depth,0,0);
1861 swf_ButtonSetRecord(i->tag,0x08,myshapeid,i->depth,0,0);
1862 swf_SetU8(i->tag,0); // end of button records
1863 swf_ButtonSetCondition(i->tag, BC_IDLE_OVERUP);
1864 swf_ActionSet(i->tag,actions1);
1866 swf_ButtonSetCondition(i->tag, BC_OVERUP_IDLE);
1867 swf_ActionSet(i->tag,actions2);
1868 swf_SetU8(i->tag,0);
1869 swf_ButtonPostProcess(i->tag, 2);
1871 swf_SetU8(i->tag,0);
1872 swf_ButtonPostProcess(i->tag, 1);
1876 const char* name = 0;
1877 if(i->config_linknameurl) {
1881 sprintf(buf, "button%d", buttonid);
1884 msg("<trace> Placing link ID %d", buttonid);
1885 i->tag = swf_InsertTag(i->tag,ST_PLACEOBJECT2);
1887 if(posx!=0 || posy!=0) {
1889 p.x = (int)(posx*20);
1890 p.y = (int)(posy*20);
1891 p = swf_TurnPoint(p, &i->page_matrix);
1896 swf_ObjectPlace(i->tag, buttonid, getNewDepth(dev),&m,0,(U8*)name);
1898 swf_ObjectPlace(i->tag, buttonid, getNewDepth(dev),&i->page_matrix,0,(U8*)name);
1905 for(t=0;t<picpos;t++)
1907 if(pic_xids[t] == xid &&
1908 pic_yids[t] == yid) {
1909 width = pic_width[t];
1910 height = pic_height[t];
1914 pic_ids[picpos] = swfoutput_drawimagelosslessN(&output, pic, pal, width, height, x1,y1,x2,y2,x3,y3,x4,y4, numpalette);
1915 pic_xids[picpos] = xid;
1916 pic_yids[picpos] = yid;
1917 pic_width[picpos] = width;
1918 pic_height[picpos] = height;
1921 pic[width*y+x] = buf[0];
1925 xid += pal[1].r*3 + pal[1].g*11 + pal[1].b*17;
1926 yid += pal[1].r*7 + pal[1].g*5 + pal[1].b*23;
1930 xid += x*r+x*b*3+x*g*7+x*a*11;
1931 yid += y*r*3+y*b*17+y*g*19+y*a*11;
1933 for(t=0;t<picpos;t++)
1935 if(pic_xids[t] == xid &&
1936 pic_yids[t] == yid) {
1945 int swf_setparameter(gfxdevice_t*dev, const char*name, const char*value)
1947 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1949 msg("<trace> swfdevice: %s=%s", name, value);
1950 if(!strcmp(name, "jpegsubpixels")) {
1951 i->config_jpegsubpixels = atof(value);
1952 } else if(!strcmp(name, "ppmsubpixels")) {
1953 i->config_ppmsubpixels = atof(value);
1954 } else if(!strcmp(name, "subpixels")) {
1955 i->config_ppmsubpixels = i->config_jpegsubpixels = atof(value);
1956 } else if(!strcmp(name, "drawonlyshapes")) {
1957 i->config_drawonlyshapes = atoi(value);
1958 } else if(!strcmp(name, "ignoredraworder")) {
1959 i->config_ignoredraworder = atoi(value);
1960 } else if(!strcmp(name, "mark")) {
1961 if(!value || !value[0]) {
1962 if(i->mark) free(i->mark);
1966 i->mark = strdup("...");
1967 for(t=0;t<3;t++) if(value[t]) i->mark[t] = value[t];
1969 } else if(!strcmp(name, "filloverlap")) {
1970 i->config_filloverlap = atoi(value);
1971 } else if(!strcmp(name, "linksopennewwindow")) {
1972 i->config_opennewwindow = atoi(value);
1973 } else if(!strcmp(name, "opennewwindow")) {
1974 i->config_opennewwindow = atoi(value);
1975 } else if(!strcmp(name, "storeallcharacters")) {
1976 i->config_storeallcharacters = atoi(value);
1977 } else if(!strcmp(name, "enablezlib")) {
1978 i->config_enablezlib = atoi(value);
1979 } else if(!strcmp(name, "bboxvars")) {
1980 i->config_bboxvars = atoi(value);
1981 } else if(!strcmp(name, "dots")) {
1982 i->config_dots = atoi(value);
1983 } else if(!strcmp(name, "frameresets")) {
1984 i->config_frameresets = atoi(value);
1985 } else if(!strcmp(name, "showclipshapes")) {
1986 i->config_showclipshapes = atoi(value);
1987 } else if(!strcmp(name, "reordertags")) {
1988 i->config_reordertags = atoi(value);
1989 } else if(!strcmp(name, "internallinkfunction")) {
1990 i->config_internallinkfunction = strdup(value);
1991 } else if(!strcmp(name, "externallinkfunction")) {
1992 i->config_externallinkfunction = strdup(value);
1993 } else if(!strcmp(name, "linkfunction")) { //sets both internallinkfunction and externallinkfunction
1994 i->config_internallinkfunction = strdup(value);
1995 i->config_externallinkfunction = strdup(value);
1996 } else if(!strcmp(name, "disable_polygon_conversion")) {
1997 i->config_disable_polygon_conversion = atoi(value);
1998 } else if(!strcmp(name, "normalize_polygon_positions")) {
1999 i->config_normalize_polygon_positions = atoi(value);
2000 } else if(!strcmp(name, "wxwindowparams")) {
2001 i->config_watermark = atoi(value);
2002 } else if(!strcmp(name, "insertstop")) {
2003 i->config_insertstoptag = atoi(value);
2004 } else if(!strcmp(name, "protect")) {
2005 i->config_protect = atoi(value);
2006 if(i->config_protect && i->tag) {
2007 i->tag = swf_InsertTag(i->tag, ST_PROTECT);
2009 } else if(!strcmp(name, "flashversion")) {
2010 i->config_flashversion = atoi(value);
2012 i->swf->fileVersion = i->config_flashversion;
2014 } else if(!strcmp(name, "framerate")) {
2015 i->config_framerate = atof(value);
2017 i->swf->frameRate = i->config_framerate*0x100;
2019 } else if(!strcmp(name, "minlinewidth")) {
2020 i->config_minlinewidth = atof(value);
2021 } else if(!strcmp(name, "caplinewidth")) {
2022 i->config_caplinewidth = atof(value);
2023 } else if(!strcmp(name, "linktarget")) {
2024 i->config_linktarget = strdup(value);
2025 } else if(!strcmp(name, "noclips")) {
2026 i->config_noclips = atoi(value);
2027 } else if(!strcmp(name, "dumpfonts")) {
2028 i->config_dumpfonts = atoi(value);
2029 } else if(!strcmp(name, "animate")) {
2030 i->config_animate = atoi(value);
2031 } else if(!strcmp(name, "disablelinks")) {
2032 i->config_disablelinks = atoi(value);
2033 } else if(!strcmp(name, "simpleviewer")) {
2034 i->config_simpleviewer = atoi(value);
2035 } else if(!strcmp(name, "next_bitmap_is_jpeg")) {
2037 } else if(!strcmp(name, "jpegquality")) {
2038 int val = atoi(value);
2040 if(val>101) val=101;
2041 i->config_jpegquality = val;
2042 } else if(!strcmp(name, "splinequality")) {
2043 int v = atoi(value);
2044 v = 500-(v*5); // 100% = 0.25 pixel, 0% = 25 pixel
2046 i->config_splinemaxerror = v;
2047 } else if(!strcmp(name, "fontquality")) {
2048 int v = atoi(value);
2049 v = 500-(v*5); // 100% = 0.25 pixel, 0% = 25 pixel
2051 i->config_fontsplinemaxerror = v;
2052 } else if(!strcmp(name, "linkcolor")) {
2053 if(strlen(value)!=8) {
2054 fprintf(stderr, "Unknown format for option 'linkcolor'. (%s <-> RRGGBBAA)\n", value);
2057 # define NIBBLE(s) (((s)>='0' && (s)<='9')?((s)-'0'):((s)&0x0f)+9)
2058 i->config_linkcolor.r = NIBBLE(value[0])<<4 | NIBBLE(value[1]);
2059 i->config_linkcolor.g = NIBBLE(value[2])<<4 | NIBBLE(value[3]);
2060 i->config_linkcolor.b = NIBBLE(value[4])<<4 | NIBBLE(value[5]);
2061 i->config_linkcolor.a = NIBBLE(value[6])<<4 | NIBBLE(value[7]);
2062 } else if(!strcmp(name, "help")) {
2063 printf("\nSWF layer options:\n");
2064 printf("jpegsubpixels=<pixels> resolution adjustment for jpeg images (same as jpegdpi, but in pixels)\n");
2065 printf("ppmsubpixels=<pixels resolution adjustment for lossless images (same as ppmdpi, but in pixels)\n");
2066 printf("subpixels=<pixels> shortcut for setting both jpegsubpixels and ppmsubpixels\n");
2067 printf("drawonlyshapes convert everything to shapes (currently broken)\n");
2068 printf("ignoredraworder allow to perform a few optimizations for creating smaller SWFs\n");
2069 printf("linksopennewwindow make links open a new browser window\n");
2070 printf("linktarget target window name of new links\n");
2071 printf("linkcolor=<color) color of links (format: RRGGBBAA)\n");
2072 printf("linknameurl Link buttons will be named like the URL they refer to (handy for iterating through links with actionscript)\n");
2073 printf("storeallcharacters don't reduce the fonts to used characters in the output file\n");
2074 printf("enablezlib switch on zlib compression (also done if flashversion>=7)\n");
2075 printf("bboxvars store the bounding box of the SWF file in actionscript variables\n");
2076 printf("dots Take care to handle dots correctly\n");
2077 printf("reordertags=0/1 (default: 1) perform some tag optimizations\n");
2078 printf("internallinkfunction=<name> when the user clicks a internal link (to a different page) in the converted file, this actionscript function is called\n");
2079 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");
2080 printf("disable_polygon_conversion never convert strokes to polygons (will remove capstyles and joint styles)\n");
2081 printf("caplinewidth=<width> the minimum thichness a line needs to have so that capstyles become visible (and are converted)\n");
2082 printf("insertstop put an ActionScript \"STOP\" tag in every frame\n");
2083 printf("protect add a \"protect\" tag to the file, to prevent loading in the Flash editor\n");
2084 printf("flashversion=<version> the SWF fileversion (6)\n");
2085 printf("framerate=<fps> SWF framerate\n");
2086 printf("minlinewidth=<width> convert horizontal/vertical boxes smaller than this width to lines (0.05) \n");
2087 printf("simpleviewer Add next/previous buttons to the SWF\n");
2088 printf("animate insert a showframe tag after each placeobject (animate draw order of PDF files)\n");
2089 printf("jpegquality=<quality> set compression quality of jpeg images\n");
2090 printf("splinequality=<value> Set the quality of spline convertion to value (0-100, default: 100).\n");
2091 printf("disablelinks Disable links.\n");
2098 // --------------------------------------------------------------------
2100 static CXFORM gfxcxform_to_cxform(gfxcxform_t* c)
2103 swf_GetCXForm(0, &cx, 1);
2106 if(c->rg!=0 || c->rb!=0 || c->ra!=0 ||
2107 c->gr!=0 || c->gb!=0 || c->ga!=0 ||
2108 c->br!=0 || c->bg!=0 || c->ba!=0 ||
2109 c->ar!=0 || c->ag!=0 || c->ab!=0)
2110 msg("<warning> CXForm not SWF-compatible");
2112 cx.a0 = (S16)(c->aa*256);
2113 cx.r0 = (S16)(c->rr*256);
2114 cx.g0 = (S16)(c->gg*256);
2115 cx.b0 = (S16)(c->bb*256);
2124 static int imageInCache(gfxdevice_t*dev, void*data, int width, int height)
2128 static void addImageToCache(gfxdevice_t*dev, void*data, int width, int height)
2132 static int add_image(swfoutput_internal*i, gfximage_t*img, int targetwidth, int targetheight, int* newwidth, int* newheight)
2134 gfxdevice_t*dev = i->dev;
2136 RGBA*mem = (RGBA*)img->data;
2138 int sizex = img->width;
2139 int sizey = img->height;
2140 int is_jpeg = i->jpeg;
2143 int newsizex=sizex, newsizey=sizey;
2146 if(is_jpeg && i->config_jpegsubpixels) {
2147 newsizex = (int)(targetwidth*i->config_jpegsubpixels + 0.5);
2148 newsizey = (int)(targetheight*i->config_jpegsubpixels + 0.5);
2149 } else if(!is_jpeg && i->config_ppmsubpixels) {
2150 newsizex = (int)(targetwidth*i->config_ppmsubpixels + 0.5);
2151 newsizey = (int)(targetheight*i->config_ppmsubpixels + 0.5);
2155 if(sizex<=0 || sizey<=0)
2162 /* TODO: cache images */
2164 if(newsizex<sizex || newsizey<sizey) {
2165 msg("<verbose> Scaling %dx%d image to %dx%d", sizex, sizey, newsizex, newsizey);
2166 newpic = swf_ImageScale(mem, sizex, sizey, newsizex, newsizey);
2167 *newwidth = sizex = newsizex;
2168 *newheight = sizey = newsizey;
2171 *newwidth = newsizex = sizex;
2172 *newheight = newsizey = sizey;
2175 int num_colors = swf_ImageGetNumberOfPaletteEntries(mem,sizex,sizey,0);
2176 int has_alpha = swf_ImageHasAlpha(mem,sizex,sizey);
2178 msg("<verbose> Drawing %dx%d %s%simage (id %d) at size %dx%d (%dx%d), %s%d colors",
2180 has_alpha?(has_alpha==2?"semi-transparent ":"transparent "):"",
2181 is_jpeg?"jpeg-":"", i->currentswfid+1,
2183 targetwidth, targetheight,
2184 /*newsizex, newsizey,*/
2185 num_colors>256?">":"", num_colors>256?256:num_colors);
2187 /*RGBA* pal = (RGBA*)rfx_alloc(sizeof(RGBA)*num_colors);
2188 swf_ImageGetNumberOfPaletteEntries(mem,sizex,sizey,pal);
2190 for(t=0;t<num_colors;t++) {
2191 printf("%02x%02x%02x%02x ",
2192 pal[t].r, pal[t].g, pal[t].b, pal[t].a);
2199 int cacheid = imageInCache(dev, mem, sizex, sizey);
2202 bitid = getNewID(dev);
2204 i->tag = swf_AddImage(i->tag, bitid, mem, sizex, sizey, i->config_jpegquality);
2205 addImageToCache(dev, mem, sizex, sizey);
2215 static SRECT gfxline_getSWFbbox(gfxline_t*line)
2217 gfxbbox_t bbox = gfxline_getbbox(line);
2219 r.xmin = (int)(bbox.xmin*20);
2220 r.ymin = (int)(bbox.ymin*20);
2221 r.xmax = (int)(bbox.xmax*20);
2222 r.ymax = (int)(bbox.ymax*20);
2226 int line_is_empty(gfxline_t*line)
2229 if(line->type != gfx_moveTo)
2236 static void swf_fillbitmap(gfxdevice_t*dev, gfxline_t*line, gfximage_t*img, gfxmatrix_t*matrix, gfxcxform_t*cxform)
2238 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
2240 if(line_is_empty(line))
2246 int targetx = (int)(sqrt(matrix->m00*matrix->m00 + matrix->m01*matrix->m01)*img->width);
2247 int targety = (int)(sqrt(matrix->m10*matrix->m10 + matrix->m11*matrix->m11)*img->height);
2249 int newwidth=0,newheight=0;
2250 int bitid = add_image(i, img, targetx, targety, &newwidth, &newheight);
2253 double fx = (double)img->width / (double)newwidth;
2254 double fy = (double)img->height / (double)newheight;
2257 m.sx = (int)(65536*20*matrix->m00*fx); m.r1 = (int)(65536*20*matrix->m10*fy);
2258 m.r0 = (int)(65536*20*matrix->m01*fx); m.sy = (int)(65536*20*matrix->m11*fy);
2259 m.tx = (int)(matrix->tx*20);
2260 m.ty = (int)(matrix->ty*20);
2263 int myshapeid = getNewID(dev);
2264 i->tag = swf_InsertTag(i->tag,ST_DEFINESHAPE);
2266 swf_ShapeNew(&shape);
2267 int fsid = swf_ShapeAddBitmapFillStyle(shape,&m,bitid,1);
2268 swf_SetU16(i->tag, myshapeid);
2269 SRECT r = gfxline_getSWFbbox(line);
2270 r = swf_ClipRect(i->pagebbox, r);
2271 swf_SetRect(i->tag,&r);
2272 swf_SetShapeStyles(i->tag,shape);
2273 swf_ShapeCountBits(shape,NULL,NULL);
2274 swf_SetShapeBits(i->tag,shape);
2275 swf_ShapeSetAll(i->tag,shape,UNDEFINED_COORD,UNDEFINED_COORD,0,fsid,0);
2276 i->swflastx = i->swflasty = UNDEFINED_COORD;
2277 drawgfxline(dev, line, 1);
2278 swf_ShapeSetEnd(i->tag);
2279 swf_ShapeFree(shape);
2281 msg("<trace> Placing image, shape ID %d, bitmap ID %d", myshapeid, bitid);
2282 i->tag = swf_InsertTag(i->tag,ST_PLACEOBJECT2);
2283 CXFORM cxform2 = gfxcxform_to_cxform(cxform);
2284 swf_ObjectPlace(i->tag,myshapeid,getNewDepth(dev),&i->page_matrix,&cxform2,NULL);
2287 static RGBA col_black = {255,0,0,0};
2289 static void drawoutline(gfxdevice_t*dev, gfxline_t*line)
2291 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
2293 int myshapeid = getNewID(dev);
2294 i->tag = swf_InsertTag(i->tag,ST_DEFINESHAPE3);
2297 swf_ShapeNew(&shape);
2298 int lsid = swf_ShapeAddLineStyle(shape,1,&col_black);
2300 swf_SetU16(i->tag,myshapeid);
2301 SRECT r = gfxline_getSWFbbox(line);
2302 r = swf_ClipRect(i->pagebbox, r);
2303 swf_SetRect(i->tag,&r);
2304 swf_SetShapeStyles(i->tag,shape);
2305 swf_ShapeCountBits(shape,NULL,NULL);
2306 swf_SetShapeBits(i->tag,shape);
2307 swf_ShapeSetAll(i->tag,shape,UNDEFINED_COORD,UNDEFINED_COORD,lsid,0,0);
2308 drawgfxline(dev, line, 1);
2309 swf_ShapeSetEnd(i->tag);
2310 swf_ShapeFree(shape);
2312 i->tag = swf_InsertTag(i->tag,ST_PLACEOBJECT2);
2313 swf_ObjectPlace(i->tag, myshapeid, getNewDepth(dev), 0,0,0);
2316 static void swf_startclip(gfxdevice_t*dev, gfxline_t*line)
2318 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
2319 if(i->config_noclips)
2325 if(i->clippos >= 127)
2327 msg("<warning> Too many clip levels.");
2331 if(i->config_showclipshapes)
2332 drawoutline(dev, line);
2334 int myshapeid = getNewID(dev);
2335 i->tag = swf_InsertTag(i->tag,ST_DEFINESHAPE3);
2337 memset(&col, 0, sizeof(RGBA));
2340 swf_ShapeNew(&shape);
2341 int fsid = swf_ShapeAddSolidFillStyle(shape,&col);
2343 RGBA markcol = {0,i->mark[0],i->mark[1],i->mark[2]};
2344 swf_ShapeAddSolidFillStyle(shape,&markcol);
2346 swf_SetU16(i->tag,myshapeid);
2347 SRECT r = gfxline_getSWFbbox(line);
2348 r = swf_ClipRect(i->pagebbox, r);
2349 swf_SetRect(i->tag,&r);
2350 swf_SetShapeStyles(i->tag,shape);
2351 swf_ShapeCountBits(shape,NULL,NULL);
2352 swf_SetShapeBits(i->tag,shape);
2353 swf_ShapeSetAll(i->tag,shape,UNDEFINED_COORD,UNDEFINED_COORD,0,fsid,0);
2354 i->swflastx = i->swflasty = UNDEFINED_COORD;
2355 i->shapeisempty = 1;
2356 drawgfxline(dev, line, 1);
2357 if(i->shapeisempty) {
2358 /* an empty clip shape is equivalent to a shape with no area */
2359 int x = line?line->x:0;
2360 int y = line?line->y:0;
2361 moveto(dev, i->tag, x,y);
2362 lineto(dev, i->tag, x,y);
2363 lineto(dev, i->tag, x,y);
2365 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)) {
2366 if(i->config_watermark) {
2367 gfxbbox_t r; r.xmin = r.ymin = 0;r.xmax = i->max_x;r.ymax = i->max_y;
2368 draw_watermark(dev, r, 1);
2371 swf_ShapeSetEnd(i->tag);
2372 swf_ShapeFree(shape);
2374 /* TODO: remember the bbox, and check all shapes against it */
2376 msg("<trace> Placing clip ID %d", myshapeid);
2377 i->tag = swf_InsertTag(i->tag,ST_PLACEOBJECT2);
2378 i->cliptags[i->clippos] = i->tag;
2379 i->clipshapes[i->clippos] = myshapeid;
2380 i->clipdepths[i->clippos] = getNewDepth(dev);
2384 static void swf_endclip(gfxdevice_t*dev)
2386 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
2387 if(i->config_noclips)
2395 msg("<error> Invalid end of clipping region");
2399 /*swf_ObjectPlaceClip(i->cliptags[i->clippos],i->clipshapes[i->clippos],i->clipdepths[i->clippos],&i->page_matrix,NULL,NULL,
2400 / * clip to depth: * / i->depth <= i->clipdepths[i->clippos]? i->depth : i->depth - 1);
2402 swf_ObjectPlaceClip(i->cliptags[i->clippos],i->clipshapes[i->clippos],i->clipdepths[i->clippos],&i->page_matrix,NULL,NULL,i->depth);
2404 static int gfxline_type(gfxline_t*line)
2410 int haszerosegments=0;
2413 if(line->type == gfx_moveTo) {
2416 } else if(line->type == gfx_lineTo) {
2420 } else if(line->type == gfx_splineTo) {
2422 if(tmpsplines>lines)
2430 if(lines==0 && splines==0) return 0;
2431 else if(lines==1 && splines==0) return 1;
2432 else if(lines==0 && splines==1) return 2;
2433 else if(splines==0) return 3;
2437 static int gfxline_has_dots(gfxline_t*line)
2445 if(line->type == gfx_moveTo) {
2446 /* test the length of the preceding line, and assume it is a dot if
2447 it's length is less than 1.0. But *only* if there's a noticable
2448 gap between the previous line and the next moveTo. (I've come
2449 across a PDF where thousands of "dots" were stringed together,
2451 int last_short_gap = short_gap;
2452 if((fabs(line->x - x) + fabs(line->y - y)) < 1.0) {
2457 if(isline && dist < 1 && !short_gap && !last_short_gap) {
2462 } else if(line->type == gfx_lineTo) {
2463 dist += fabs(line->x - x) + fabs(line->y - y);
2465 } else if(line->type == gfx_splineTo) {
2466 dist += fabs(line->sx - x) + fabs(line->sy - y) +
2467 fabs(line->x - line->sx) + fabs(line->y - line->sy);
2474 if(isline && dist < 1 && !short_gap) {
2480 static int gfxline_fix_short_edges(gfxline_t*line)
2484 if(line->type == gfx_lineTo) {
2485 if(fabs(line->x - x) + fabs(line->y - y) < 0.01) {
2488 } else if(line->type == gfx_splineTo) {
2489 if(fabs(line->sx - x) + fabs(line->sy - y) +
2490 fabs(line->x - line->sx) + fabs(line->y - line->sy) < 0.01) {
2501 static char is_inside_page(gfxdevice_t*dev, gfxcoord_t x, gfxcoord_t y)
2503 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
2504 if(x<i->min_x || x>i->max_x) return 0;
2505 if(y<i->min_y || y>i->max_y) return 0;
2509 gfxline_t* gfxline_move(gfxline_t*line, double x, double y)
2511 gfxline_t*l = line = gfxline_clone(line);
2523 //#define NORMALIZE_POLYGON_POSITIONS
2525 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)
2527 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
2528 if(line_is_empty(line))
2530 int type = gfxline_type(line);
2531 int has_dots = gfxline_has_dots(line);
2532 gfxbbox_t r = gfxline_getbbox(line);
2533 int is_outside_page = !is_inside_page(dev, r.xmin, r.ymin) || !is_inside_page(dev, r.xmax, r.ymax);
2535 /* TODO: * split line into segments, and perform this check for all segments */
2537 if(i->config_disable_polygon_conversion || /*type>=5 ||*/
2539 (width <= i->config_caplinewidth
2540 || (cap_style == gfx_capRound && joint_style == gfx_joinRound)
2541 || (cap_style == gfx_capRound && type<=2))))
2545 /* convert line to polygon */
2546 msg("<trace> draw as polygon, type=%d dots=%d", type, has_dots);
2548 gfxline_fix_short_edges(line);
2549 /* we need to convert the line into a polygon */
2550 gfxpoly_t* poly = gfxpoly_from_stroke(line, width, cap_style, joint_style, miterLimit, DEFAULT_GRID);
2551 gfxline_t*gfxline = gfxline_from_gfxpoly(poly);
2552 dev->fill(dev, gfxline, color);
2553 gfxline_free(gfxline);
2554 gfxpoly_destroy(poly);
2558 msg("<trace> draw as stroke, type=%d dots=%d", type, has_dots);
2561 if(i->config_normalize_polygon_positions) {
2563 double startx = 0, starty = 0;
2564 if(line && line->type == gfx_moveTo) {
2568 line = gfxline_move(line, -startx, -starty);
2569 i->shapeposx = (int)(startx*20);
2570 i->shapeposy = (int)(starty*20);
2573 swfoutput_setstrokecolor(dev, color->r, color->g, color->b, color->a);
2574 swfoutput_setlinewidth(dev, width);
2577 drawgfxline(dev, line, 0);
2579 if(i->config_normalize_polygon_positions) {
2580 free(line); //account for _move
2585 static void swf_fill(gfxdevice_t*dev, gfxline_t*line, gfxcolor_t*color)
2587 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
2588 if(line_is_empty(line))
2592 gfxbbox_t r = gfxline_getbbox(line);
2593 int is_outside_page = !is_inside_page(dev, r.xmin, r.ymin) || !is_inside_page(dev, r.xmax, r.ymax);
2595 //if(i->chardatapos && i->chardata[i->chardatapos-1].color.a) {
2598 if(!i->config_ignoredraworder)
2601 if(i->config_normalize_polygon_positions) {
2603 double startx = 0, starty = 0;
2604 if(line && line->type == gfx_moveTo) {
2608 line = gfxline_move(line, -startx, -starty);
2609 i->shapeposx = (int)(startx*20);
2610 i->shapeposy = (int)(starty*20);
2613 swfoutput_setfillcolor(dev, color->r, color->g, color->b, color->a);
2616 drawgfxline(dev, line, 1);
2618 if(i->currentswfid==2 && r.xmin==0 && r.ymin==0 && r.xmax==i->max_x && r.ymax==i->max_y) {
2619 if(i->config_watermark) {
2620 draw_watermark(dev, r, 1);
2624 msg("<trace> end of swf_fill (shapeid=%d)", i->shapeid);
2626 if(i->config_normalize_polygon_positions) {
2627 free(line); //account for _move
2631 static GRADIENT* gfxgradient_to_GRADIENT(gfxgradient_t*gradient)
2634 gfxgradient_t*g = gradient;
2639 GRADIENT* swfgradient = malloc(sizeof(GRADIENT));
2640 swfgradient->num = num;
2641 swfgradient->rgba = malloc(sizeof(swfgradient->rgba[0])*num);
2642 swfgradient->ratios = malloc(sizeof(swfgradient->ratios[0])*num);
2647 swfgradient->ratios[num] = g->pos*255;
2648 swfgradient->rgba[num] = *(RGBA*)&g->color;
2655 static void swf_fillgradient(gfxdevice_t*dev, gfxline_t*line, gfxgradient_t*gradient, gfxgradienttype_t type, gfxmatrix_t*matrix)
2657 if(line_is_empty(line))
2659 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
2661 if(line_is_empty(line))
2664 GRADIENT* swfgradient = gfxgradient_to_GRADIENT(gradient);
2671 double f = type==gfxgradient_radial?4:4;
2673 m.sx = (int)(matrix->m00*20*f); m.r1 = (int)(matrix->m10*20*f);
2674 m.r0 = (int)(matrix->m01*20*f); m.sy = (int)(matrix->m11*20*f);
2675 m.tx = (int)(matrix->tx*20);
2676 m.ty = (int)(matrix->ty*20);
2679 int myshapeid = getNewID(dev);
2680 i->tag = swf_InsertTag(i->tag, ST_DEFINESHAPE2);
2682 swf_ShapeNew(&shape);
2683 int fsid = swf_ShapeAddGradientFillStyle(shape,&m,swfgradient,type==gfxgradient_radial);
2684 swf_SetU16(i->tag, myshapeid);
2685 SRECT r = gfxline_getSWFbbox(line);
2686 r = swf_ClipRect(i->pagebbox, r);
2687 swf_SetRect(i->tag,&r);
2688 swf_SetShapeStyles(i->tag,shape);
2689 swf_ShapeCountBits(shape,NULL,NULL);
2690 swf_SetShapeBits(i->tag,shape);
2691 swf_ShapeSetAll(i->tag,shape,UNDEFINED_COORD,UNDEFINED_COORD,0,fsid,0);
2692 i->swflastx = i->swflasty = UNDEFINED_COORD;
2693 drawgfxline(dev, line, 1);
2694 swf_ShapeSetEnd(i->tag);
2695 swf_ShapeFree(shape);
2697 int depth = getNewDepth(dev);
2698 msg("<trace> Placing gradient, shape ID %d, depth %d", myshapeid, depth);
2699 i->tag = swf_InsertTag(i->tag,ST_PLACEOBJECT2);
2700 swf_ObjectPlace(i->tag,myshapeid,depth,&i->page_matrix,NULL,NULL);
2702 swf_FreeGradient(swfgradient);free(swfgradient);
2705 static SWFFONT* gfxfont_to_swffont(gfxfont_t*font, const char* id)
2707 SWFFONT*swffont = (SWFFONT*)rfx_calloc(sizeof(SWFFONT));
2709 SRECT bounds = {0,0,0,0};
2711 swffont->version = 2;
2712 swffont->name = (U8*)strdup(id);
2713 swffont->layout = (SWFLAYOUT*)rfx_calloc(sizeof(SWFLAYOUT));
2714 swffont->layout->ascent = 0;
2715 swffont->layout->descent = 0;
2716 swffont->layout->leading = 0;
2717 swffont->layout->bounds = (SRECT*)rfx_calloc(sizeof(SRECT)*font->num_glyphs);
2718 swffont->encoding = FONT_ENCODING_UNICODE;
2719 swffont->numchars = font->num_glyphs;
2720 swffont->maxascii = font->max_unicode;
2721 swffont->ascii2glyph = (int*)rfx_calloc(sizeof(int)*swffont->maxascii);
2722 swffont->glyph2ascii = (U16*)rfx_calloc(sizeof(U16)*swffont->numchars);
2723 swffont->glyph = (SWFGLYPH*)rfx_calloc(sizeof(SWFGLYPH)*swffont->numchars);
2724 swffont->glyphnames = (char**)rfx_calloc(sizeof(char*)*swffont->numchars);
2725 for(t=0;t<font->max_unicode;t++) {
2726 swffont->ascii2glyph[t] = font->unicode2glyph[t];
2728 SRECT max = {0,0,0,0};
2729 for(t=0;t<font->num_glyphs;t++) {
2733 swffont->glyph2ascii[t] = font->glyphs[t].unicode;
2734 if(swffont->glyph2ascii[t] == 0xffff || swffont->glyph2ascii[t] == 0x0000) {
2735 /* flash 8 flashtype requires unique unicode IDs for each character.
2736 We use the Unicode private user area to assign characters, hoping that
2737 the font doesn't contain more than 2048 glyphs */
2738 swffont->glyph2ascii[t] = 0xe000 + (t&0x1fff);
2741 if(font->glyphs[t].name) {
2742 swffont->glyphnames[t] = strdup(font->glyphs[t].name);
2744 swffont->glyphnames[t] = 0;
2746 advance = font->glyphs[t].advance;
2748 swf_Shape01DrawerInit(&draw, 0);
2749 line = font->glyphs[t].line;
2752 c.x = line->sx * GLYPH_SCALE; c.y = line->sy * GLYPH_SCALE;
2753 to.x = line->x * GLYPH_SCALE; to.y = line->y * GLYPH_SCALE;
2754 if(line->type == gfx_moveTo) {
2755 draw.moveTo(&draw, &to);
2756 } else if(line->type == gfx_lineTo) {
2757 draw.lineTo(&draw, &to);
2758 } else if(line->type == gfx_splineTo) {
2759 draw.splineTo(&draw, &c, &to);
2764 swffont->glyph[t].shape = swf_ShapeDrawerToShape(&draw);
2766 SRECT bbox = swf_ShapeDrawerGetBBox(&draw);
2767 swf_ExpandRect2(&max, &bbox);
2769 swffont->layout->bounds[t] = bbox;
2771 if(advance<32768.0/20) {
2772 swffont->glyph[t].advance = (int)(advance*20);
2774 //msg("<warning> Advance value overflow in glyph %d", t);
2775 swffont->glyph[t].advance = 32767;
2778 draw.dealloc(&draw);
2780 swf_ExpandRect2(&bounds, &swffont->layout->bounds[t]);
2782 for(t=0;t<font->num_glyphs;t++) {
2783 SRECT bbox = swffont->layout->bounds[t];
2785 /* if the glyph doesn't have a bounding box, use the
2786 combined bounding box (necessary e.g. for space characters) */
2787 if(!(bbox.xmin|bbox.ymin|bbox.xmax|bbox.ymax)) {
2788 swffont->layout->bounds[t] = bbox = max;
2791 /* check that the advance value is reasonable, by comparing it
2792 with the bounding box */
2793 if(bbox.xmax>0 && (bbox.xmax*10 < swffont->glyph[t].advance || !swffont->glyph[t].advance)) {
2794 if(swffont->glyph[t].advance)
2795 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);
2796 swffont->glyph[t].advance = bbox.xmax;
2798 //swffont->glyph[t].advance = bbox.xmax - bbox.xmin;
2802 /* Flash player will use the advance value from the char, and the ascent/descent values
2803 from the layout for text selection.
2804 ascent will extend the char into negative y direction, from the baseline, while descent
2805 will extend in positive y direction, also from the baseline.
2806 The baseline is defined as the y-position zero
2809 swffont->layout->ascent = -bounds.ymin;
2810 if(swffont->layout->ascent < 0)
2811 swffont->layout->ascent = 0;
2812 swffont->layout->descent = bounds.ymax;
2813 if(swffont->layout->descent < 0)
2814 swffont->layout->descent = 0;
2815 swffont->layout->leading = bounds.ymax - bounds.ymin;
2817 /* if the font has proper ascent/descent values (>0) and those define
2818 greater line spacing that what we estimated from the bounding boxes,
2819 use the font's parameters */
2820 if(font->ascent*20 > swffont->layout->ascent)
2821 swffont->layout->ascent = font->ascent*20;
2822 if(font->descent*20 > swffont->layout->descent)
2823 swffont->layout->descent = font->descent*20;
2828 static void swf_addfont(gfxdevice_t*dev, gfxfont_t*font)
2830 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
2832 if(i->swffont && i->swffont->name && !strcmp((char*)i->swffont->name,font->id))
2833 return; // the requested font is the current font
2835 fontlist_t*last=0,*l = i->fontlist;
2838 if(!strcmp((char*)l->swffont->name, font->id)) {
2839 return; // we already know this font
2843 l = (fontlist_t*)rfx_calloc(sizeof(fontlist_t));
2844 l->swffont = gfxfont_to_swffont(font, font->id);
2851 swf_FontSetID(l->swffont, getNewID(i->dev));
2853 if(getScreenLogLevel() >= LOGLEVEL_DEBUG) {
2855 // print font information
2856 msg("<debug> Font %s",font->id);
2857 msg("<debug> | ID: %d", l->swffont->id);
2858 msg("<debug> | Version: %d", l->swffont->version);
2859 msg("<debug> | Name: %s", l->swffont->name);
2860 msg("<debug> | Numchars: %d", l->swffont->numchars);
2861 msg("<debug> | Maxascii: %d", l->swffont->maxascii);
2862 msg("<debug> | Style: %d", l->swffont->style);
2863 msg("<debug> | Encoding: %d", l->swffont->encoding);
2864 for(iii=0; iii<l->swffont->numchars;iii++) {
2865 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,
2866 l->swffont->layout->bounds[iii].xmin/20.0,
2867 l->swffont->layout->bounds[iii].ymin/20.0,
2868 l->swffont->layout->bounds[iii].xmax/20.0,
2869 l->swffont->layout->bounds[iii].ymax/20.0
2872 for(t=0;t<l->swffont->maxascii;t++) {
2873 if(l->swffont->ascii2glyph[t] == iii)
2874 msg("<debug> | - maps to %d",t);
2880 static void swf_switchfont(gfxdevice_t*dev, const char*fontid)
2882 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
2884 if(i->swffont && i->swffont->name && !strcmp((char*)i->swffont->name,fontid))
2885 return; // the requested font is the current font
2887 fontlist_t*l = i->fontlist;
2889 if(!strcmp((char*)l->swffont->name, fontid)) {
2890 i->swffont = l->swffont;
2895 msg("<error> Unknown font id: %s", fontid);
2899 static void swf_drawchar(gfxdevice_t*dev, gfxfont_t*font, int glyph, gfxcolor_t*color, gfxmatrix_t*matrix)
2901 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
2903 msg("<error> swf_drawchar called (glyph %d) without font", glyph);
2907 if(i->config_drawonlyshapes) {
2908 gfxglyph_t*g = &font->glyphs[glyph];
2909 gfxline_t*line2 = gfxline_clone(g->line);
2910 gfxline_transform(line2, matrix);
2911 dev->fill(dev, line2, color);
2912 gfxline_free(line2);
2916 if(!i->swffont || !i->swffont->name || strcmp((char*)i->swffont->name,font->id)) // not equal to current font
2918 /* TODO: remove the need for this (enhance getcharacterbbox so that it can cope
2919 with multiple fonts */
2921 swf_switchfont(dev, font->id); // set the current font
2924 msg("<warning> swf_drawchar: Font is NULL");
2927 if(glyph<0 || glyph>=i->swffont->numchars) {
2928 msg("<warning> No character %d in font %s (%d chars)", glyph, FIXNULL((char*)i->swffont->name), i->swffont->numchars);
2932 setfontscale(dev, matrix->m00, matrix->m01, matrix->m10, matrix->m11, matrix->tx, matrix->ty, 0);
2934 double det = i->fontmatrix.sx/65536.0 * i->fontmatrix.sy/65536.0 -
2935 i->fontmatrix.r0/65536.0 * i->fontmatrix.r1/65536.0;
2936 if(fabs(det) < 0.0005) {
2937 /* x direction equals y direction- the text is invisible */
2938 msg("<verbose> Not drawing invisible character character %d (det=%f, m=[%f %f;%f %f]\n", glyph,
2940 i->fontmatrix.sx/65536.0, i->fontmatrix.r1/65536.0,
2941 i->fontmatrix.r0/65536.0, i->fontmatrix.sy/65536.0);
2945 /*if(i->swffont->glyph[glyph].shape->bitlen <= 16) {
2946 msg("<warning> Glyph %d in current charset (%s, %d characters) is empty",
2947 glyph, FIXNULL((char*)i->swffont->name), i->swffont->numchars);
2951 /* calculate character position with respect to the current font matrix */
2952 double s = 20 * GLYPH_SCALE / det;
2953 double px = matrix->tx - i->fontmatrix.tx/20.0;
2954 double py = matrix->ty - i->fontmatrix.ty/20.0;
2955 int x = (SCOORD)(( px * i->fontmatrix.sy/65536.0 - py * i->fontmatrix.r1/65536.0)*s);
2956 int y = (SCOORD)((- px * i->fontmatrix.r0/65536.0 + py * i->fontmatrix.sx/65536.0)*s);
2957 if(x>32767 || x<-32768 || y>32767 || y<-32768) {
2958 msg("<verbose> Moving character origin to %f %f\n", matrix->tx, matrix->ty);
2960 setfontscale(dev, matrix->m00, matrix->m01, matrix->m10, matrix->m11, matrix->tx, matrix->ty, 1);
2961 /* since we just moved the char origin to the current char's position,
2962 it now has the relative position (0,0) */
2971 msg("<trace> Drawing char %d in font %d at %d,%d in color %02x%02x%02x%02x",
2972 glyph, i->swffont->id, x, y, color->r, color->g, color->b, color->a);
2974 putcharacter(dev, i->swffont->id, glyph, x, y, i->current_font_size, *(RGBA*)color);
2975 swf_FontUseGlyph(i->swffont, glyph);