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;
92 int config_flashversion;
93 int config_reordertags;
94 int config_showclipshapes;
95 int config_splinemaxerror;
96 int config_fontsplinemaxerror;
97 int config_filloverlap;
100 int config_disable_polygon_conversion;
101 int config_normalize_polygon_positions;
102 char config_disablelinks;
103 RGBA config_linkcolor;
104 float config_minlinewidth;
105 double config_caplinewidth;
106 char* config_linktarget;
107 char*config_internallinkfunction;
108 char*config_externallinkfunction;
110 double config_framerate;
114 fontlist_t* fontlist;
153 int pic_height[1024];
160 char fillstylechanged;
162 int jpeg; //next image type
169 chardata_t chardata[CHARDATAMAX];
176 int current_font_size;
178 double lastfontm11,lastfontm12,lastfontm21,lastfontm22;
189 } swfoutput_internal;
191 static void swf_fillbitmap(gfxdevice_t*driver, gfxline_t*line, gfximage_t*img, gfxmatrix_t*move, gfxcxform_t*cxform);
192 static int swf_setparameter(gfxdevice_t*driver, const char*key, const char*value);
193 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);
194 static void swf_startclip(gfxdevice_t*dev, gfxline_t*line);
195 static void swf_endclip(gfxdevice_t*dev);
196 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);
197 static void swf_fill(gfxdevice_t*dev, gfxline_t*line, gfxcolor_t*color);
198 static void swf_fillbitmap(gfxdevice_t*dev, gfxline_t*line, gfximage_t*img, gfxmatrix_t*matrix, gfxcxform_t*cxform);
199 static void swf_fillgradient(gfxdevice_t*dev, gfxline_t*line, gfxgradient_t*gradient, gfxgradienttype_t type, gfxmatrix_t*matrix);
200 static void swf_drawchar(gfxdevice_t*dev, gfxfont_t*font, int glyph, gfxcolor_t*color, gfxmatrix_t*matrix);
201 static void swf_addfont(gfxdevice_t*dev, gfxfont_t*font);
202 static void swf_drawlink(gfxdevice_t*dev, gfxline_t*line, const char*action);
203 static void swf_startframe(gfxdevice_t*dev, int width, int height);
204 static void swf_endframe(gfxdevice_t*dev);
205 static gfxresult_t* swf_finish(gfxdevice_t*driver);
207 static swfoutput_internal* init_internal_struct()
209 swfoutput_internal*i = (swfoutput_internal*)malloc(sizeof(swfoutput_internal));
210 memset(i, 0, sizeof(swfoutput_internal));
234 i->fillstylechanged = 0;
241 i->config_disablelinks=0;
242 i->config_dumpfonts=0;
243 i->config_ppmsubpixels=0;
244 i->config_jpegsubpixels=0;
245 i->config_opennewwindow=1;
246 i->config_ignoredraworder=0;
247 i->config_drawonlyshapes=0;
248 i->config_jpegquality=85;
249 i->config_storeallcharacters=0;
251 i->config_enablezlib=0;
252 i->config_insertstoptag=0;
253 i->config_flashversion=6;
254 i->config_framerate=0.25;
255 i->config_splinemaxerror=1;
256 i->config_fontsplinemaxerror=1;
257 i->config_filloverlap=0;
259 i->config_bboxvars=0;
260 i->config_showclipshapes=0;
261 i->config_minlinewidth=0.05;
262 i->config_caplinewidth=1;
263 i->config_linktarget=0;
264 i->config_internallinkfunction=0;
265 i->config_externallinkfunction=0;
266 i->config_reordertags=1;
267 i->config_linknameurl=0;
269 i->config_linkcolor.r = i->config_linkcolor.g = i->config_linkcolor.b = 255;
270 i->config_linkcolor.a = 0x40;
275 static int id_error = 0;
277 static U16 getNewID(gfxdevice_t* dev)
279 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
280 if(i->currentswfid == 65535) {
282 msg("<error> ID Table overflow");
283 msg("<error> This file is too complex to render- SWF only supports 65536 shapes at once");
289 return ++i->currentswfid;
291 static U16 getNewDepth(gfxdevice_t* dev)
293 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
294 if(i->depth == 65520) {
296 msg("<error> Depth Table overflow");
297 msg("<error> This file is too complex to render- SWF only supports 65536 shapes at once");
306 static void startshape(gfxdevice_t* dev);
307 static void starttext(gfxdevice_t* dev);
308 static void endshape(gfxdevice_t* dev);
309 static void endtext(gfxdevice_t* dev);
311 typedef struct _plotxy
316 static inline int twipsnap(double f)
318 /* if(f < -0x40000000/20.0) {
319 fprintf(stderr, "Warning: Coordinate underflow (%f)\n", f);
320 f = -0x40000000/20.0;
321 } else if(f>0x3fffffff/20.0) {
322 fprintf(stderr, "Warning: Coordinate overflow (%f)\n", f);
326 /* clamp coordinates to a rectangle with the property that we
327 can represent a line from the upper left corner to the upper
328 right corner using no more than 64 strokes */
329 const double min = -(1<<(18+4))/20.0;
330 const double max = ((1<<(18+4))-1)/20.0;
332 fprintf(stderr, "Warning: Coordinate underflow (%f)\n", f);
335 fprintf(stderr, "Warning: Coordinate overflow (%f)\n", f);
342 // write a move-to command into the swf
343 static int movetoxy(gfxdevice_t*dev, TAG*tag, plotxy_t p0)
345 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
346 int rx = twipsnap(p0.x);
347 int ry = twipsnap(p0.y);
348 if(rx!=i->swflastx || ry!=i->swflasty || i->fillstylechanged) {
349 swf_ShapeSetMove (tag, i->shape, rx,ry);
350 i->fillstylechanged = 0;
357 static int moveto(gfxdevice_t*dev, TAG*tag, double x, double y)
359 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
363 return movetoxy(dev, tag, p);
365 static void addPointToBBox(gfxdevice_t*dev, int px, int py)
367 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
373 swf_ExpandRect(&i->bboxrect, p);
375 swf_ExpandRect3(&i->bboxrect, p, i->linewidth*3/2);
379 /*static void plot(gfxdevice_t*dev, int x, int y, TAG*tag)
381 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
382 int width = i->linewidth/4;
386 //swf_ShapeSetLine(tag, i->shape,-width,-width);
387 //swf_ShapeSetLine(tag, i->shape,width*2,0);
388 //swf_ShapeSetLine(tag, i->shape,0,width*2);
389 //swf_ShapeSetLine(tag, i->shape,-width*2,0);
390 //swf_ShapeSetLine(tag, i->shape,0,-width*2);
391 //swf_ShapeSetLine(tag, i->shape,width,width);
394 swf_ShapeSetLine(tag, i->shape,-width,0);
395 swf_ShapeSetLine(tag, i->shape,width,-width);
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,0);
401 addPointToBBox(dev, x-width ,y-width);
402 addPointToBBox(dev, x+width ,y+width);
405 // write a line-to command into the swf
406 static void linetoxy(gfxdevice_t*dev, TAG*tag, plotxy_t p0)
408 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
409 int px = twipsnap(p0.x);
410 int py = twipsnap(p0.y);
411 int rx = (px-i->swflastx);
412 int ry = (py-i->swflasty);
414 swf_ShapeSetLine (tag, i->shape, rx,ry);
415 addPointToBBox(dev, i->swflastx,i->swflasty);
416 addPointToBBox(dev, px,py);
417 } /* this is a nice idea, but doesn't work with current flash
418 players (the pixel will be invisible if they're not
419 precisely on a pixel boundary)
420 Besides, we should only do this if this lineto itself
421 is again followed by a "move".
422 else if(!i->fill && i->config_dots) {
423 // treat lines of length 0 as plots, making them
424 // at least 1 twip wide so Flash will display them
425 //plot(dev, i->swflastx, i->swflasty, tag);
426 swf_ShapeSetLine (tag, i->shape, rx+1,ry);
433 static void lineto(gfxdevice_t*dev, TAG*tag, double x, double y)
438 linetoxy(dev,tag, p);
441 // write a spline-to command into the swf
442 static void splineto(gfxdevice_t*dev, TAG*tag, plotxy_t control,plotxy_t end)
444 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
445 int lastlastx = i->swflastx;
446 int lastlasty = i->swflasty;
448 int cx = (twipsnap(control.x)-i->swflastx);
449 int cy = (twipsnap(control.y)-i->swflasty);
452 int ex = (twipsnap(end.x)-i->swflastx);
453 int ey = (twipsnap(end.y)-i->swflasty);
457 if((cx || cy) && (ex || ey)) {
458 swf_ShapeSetCurve(tag, i->shape, cx,cy,ex,ey);
459 addPointToBBox(dev, lastlastx ,lastlasty );
460 addPointToBBox(dev, lastlastx+cx,lastlasty+cy);
461 addPointToBBox(dev, lastlastx+cx+ex,lastlasty+cy+ey);
462 } else if(cx || cy || ex || ey) {
463 swf_ShapeSetLine(tag, i->shape, cx+ex,cy+ey);
464 addPointToBBox(dev, lastlastx ,lastlasty );
465 addPointToBBox(dev, lastlastx+cx,lastlasty+cy);
466 addPointToBBox(dev, lastlastx+cx+ex,lastlasty+cy+ey);
472 /* write a line, given two points and the transformation
474 /*static void line(gfxdevice_t*dev, TAG*tag, plotxy_t p0, plotxy_t p1)
476 moveto(dev, tag, p0);
477 lineto(dev, tag, p1);
480 void resetdrawer(gfxdevice_t*dev)
482 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
487 static void stopFill(gfxdevice_t*dev)
489 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
490 if(i->lastwasfill!=0)
492 swf_ShapeSetStyle(i->tag,i->shape,i->linestyleid,0x8000,0);
493 i->fillstylechanged = 1;
497 static void startFill(gfxdevice_t*dev)
499 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
500 if(i->lastwasfill!=1)
502 swf_ShapeSetStyle(i->tag,i->shape,0x8000,i->fillstyleid,0);
503 i->fillstylechanged = 1;
508 static inline int colorcompare(gfxdevice_t*dev, RGBA*a,RGBA*b)
520 static SRECT getcharacterbbox(gfxdevice_t*dev, SWFFONT*font, MATRIX* m)
522 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
525 memset(&r, 0, sizeof(r));
528 if(debug) printf("\n");
529 for(t=0;t<i->chardatapos;t++)
531 if(i->chardata[t].fontid != font->id) {
532 msg("<error> Internal error: fontid %d != fontid %d", i->chardata[t].fontid, font->id);
535 SRECT b = font->layout->bounds[i->chardata[t].charid];
536 b.xmin *= i->chardata[t].size;
537 b.ymin *= i->chardata[t].size;
538 b.xmax *= i->chardata[t].size;
539 b.ymax *= i->chardata[t].size;
541 /* divide by 1024, rounding xmax/ymax up */
542 b.xmax += 1023; b.ymax += 1023; b.xmin /= 1024; b.ymin /= 1024; b.xmax /= 1024; b.ymax /= 1024;
544 b.xmin += i->chardata[t].x;
545 b.ymin += i->chardata[t].y;
546 b.xmax += i->chardata[t].x;
547 b.ymax += i->chardata[t].y;
549 /* until we solve the INTERNAL_SCALING problem (see below)
550 make sure the bounding box is big enough */
556 b = swf_TurnRect(b, m);
558 if(debug) printf("(%f,%f,%f,%f) -> (%f,%f,%f,%f) [font %d/%d, char %d]\n",
559 font->layout->bounds[i->chardata[t].charid].xmin/20.0,
560 font->layout->bounds[i->chardata[t].charid].ymin/20.0,
561 font->layout->bounds[i->chardata[t].charid].xmax/20.0,
562 font->layout->bounds[i->chardata[t].charid].ymax/20.0,
567 i->chardata[t].fontid,
569 i->chardata[t].charid
571 swf_ExpandRect2(&r, &b);
573 if(debug) printf("-----> (%f,%f,%f,%f)\n",
581 static void putcharacters(gfxdevice_t*dev, TAG*tag)
583 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
587 color.r = i->chardata[0].color.r^255;
596 int charadvance[128];
599 int glyphbits=1; //TODO: can this be zero?
602 if(tag->id != ST_DEFINETEXT &&
603 tag->id != ST_DEFINETEXT2) {
604 msg("<error> internal error: putcharacters needs an text tag, not %d\n",tag->id);
607 if(!i->chardatapos) {
608 msg("<warning> putcharacters called with zero characters");
611 for(pass = 0; pass < 2; pass++)
621 advancebits++; // add sign bit
622 swf_SetU8(tag, glyphbits);
623 swf_SetU8(tag, advancebits);
626 for(t=0;t<=i->chardatapos;t++)
628 if(lastfontid != i->chardata[t].fontid ||
629 lastx!=i->chardata[t].x ||
630 lasty!=i->chardata[t].y ||
631 !colorcompare(dev,&color, &i->chardata[t].color) ||
633 lastsize != i->chardata[t].size ||
636 if(charstorepos && pass==0)
639 for(s=0;s<charstorepos;s++)
641 while(charids[s]>=(1<<glyphbits))
643 while(charadvance[s]>=(1<<advancebits))
647 if(charstorepos && pass==1)
649 tag->writeBit = 0; // Q&D
650 swf_SetBits(tag, 0, 1); // GLYPH Record
651 swf_SetBits(tag, charstorepos, 7); // number of glyphs
653 for(s=0;s<charstorepos;s++)
655 swf_SetBits(tag, charids[s], glyphbits);
656 swf_SetBits(tag, charadvance[s], advancebits);
661 if(pass == 1 && t<i->chardatapos)
667 if(lastx != i->chardata[t].x ||
668 lasty != i->chardata[t].y)
670 newx = i->chardata[t].x;
671 newy = i->chardata[t].y;
677 if(!colorcompare(dev,&color, &i->chardata[t].color))
679 color = i->chardata[t].color;
682 font.id = i->chardata[t].fontid;
683 if(lastfontid != i->chardata[t].fontid || lastsize != i->chardata[t].size)
686 tag->writeBit = 0; // Q&D
687 swf_TextSetInfoRecord(tag, newfont, i->chardata[t].size, newcolor, newx,newy);
690 lastfontid = i->chardata[t].fontid;
691 lastx = i->chardata[t].x;
692 lasty = i->chardata[t].y;
693 lastsize = i->chardata[t].size;
696 if(t==i->chardatapos)
700 int nextt = t==i->chardatapos-1?t:t+1;
701 int rel = i->chardata[nextt].x-i->chardata[t].x;
702 if(rel>=0 && (rel<(1<<(advancebits-1)) || pass==0)) {
704 lastx=i->chardata[nextt].x;
708 lastx=i->chardata[t].x;
710 charids[charstorepos] = i->chardata[t].charid;
711 charadvance[charstorepos] = advance;
718 static void putcharacter(gfxdevice_t*dev, int fontid, int charid, int x,int y, int size, RGBA color)
720 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
721 if(i->chardatapos == CHARDATAMAX)
723 msg("<warning> Character buffer too small. SWF will be slightly bigger");
727 i->chardata[i->chardatapos].fontid = fontid;
728 i->chardata[i->chardatapos].charid = charid;
729 i->chardata[i->chardatapos].x = x;
730 i->chardata[i->chardatapos].y = y;
731 i->chardata[i->chardatapos].color = color;
732 i->chardata[i->chardatapos].size = size;
736 /* Notice: we can only put chars in the range -1639,1638 (-32768/20,32768/20).
737 So if we set this value to high, the char coordinates will overflow.
738 If we set it to low, however, the char positions will be inaccurate */
739 #define GLYPH_SCALE 1
741 static void endtext(gfxdevice_t*dev)
743 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
747 i->tag = swf_InsertTag(i->tag,ST_DEFINETEXT2);
748 swf_SetU16(i->tag, i->textid);
751 r = getcharacterbbox(dev, i->swffont, &i->fontmatrix);
752 r = swf_ClipRect(i->pagebbox, r);
753 swf_SetRect(i->tag,&r);
755 swf_SetMatrix(i->tag,&i->fontmatrix);
757 msg("<trace> Placing text (%d characters) as ID %d", i->chardatapos, i->textid);
759 putcharacters(dev, i->tag);
762 if(i->swf->fileVersion >= 8) {
763 i->tag = swf_InsertTag(i->tag, ST_CSMTEXTSETTINGS);
764 swf_SetU16(i->tag, i->textid);
766 //swf_SetU8(i->tag, /*subpixel grid*/(2<<3)|/*flashtype*/0x40);
767 swf_SetU8(i->tag, /*grid*/(1<<3)|/*flashtype*/0x40);
768 //swf_SetU8(i->tag, /*no grid*/(0<<3)|/*flashtype*/0x40);
770 swf_SetU32(i->tag, 0);//thickness
771 swf_SetU32(i->tag, 0);//sharpness
772 swf_SetU8(i->tag, 0);//reserved
774 i->tag = swf_InsertTag(i->tag,ST_PLACEOBJECT2);
776 swf_ObjectPlace(i->tag,i->textid,getNewDepth(dev),&i->page_matrix,NULL,NULL);
780 /* set's the matrix which is to be applied to characters drawn by swfoutput_drawchar() */
781 static void setfontscale(gfxdevice_t*dev,double m11,double m12, double m21,double m22,double x, double y, char force)
787 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
788 if(i->lastfontm11 == m11 &&
789 i->lastfontm12 == m12 &&
790 i->lastfontm21 == m21 &&
791 i->lastfontm22 == m22 && !force)
796 i->lastfontm11 = m11;
797 i->lastfontm12 = m12;
798 i->lastfontm21 = m21;
799 i->lastfontm22 = m22;
801 double xsize = sqrt(m11*m11 + m12*m12);
802 double ysize = sqrt(m21*m21 + m22*m22);
803 i->current_font_size = (xsize>ysize?xsize:ysize)*1;
804 if(i->current_font_size < 1)
805 i->current_font_size = 1;
806 double ifs = 1.0 / (i->current_font_size*GLYPH_SCALE);
809 m.sx = (S32)((m11*ifs)*65536); m.r1 = (S32)((m21*ifs)*65536);
810 m.r0 = (S32)((m12*ifs)*65536); m.sy = (S32)((m22*ifs)*65536);
811 /* this is the position of the first char to set a new fontmatrix-
812 we hope that it's close enough to all other characters using the
813 font, so we use its position as origin for the matrix */
819 static int watermark2_width=47;
820 static int watermark2_height=11;
821 static int watermark2[47] = {95,1989,71,0,2015,337,1678,0,2015,5,1921,320,1938,25,2006,1024,
822 1042,21,13,960,1039,976,8,2000,1359,1088,31,1989,321,1728,0,1152,
823 1344,832,0,1984,0,896,1088,1088,896,0,1984,128,256,512,1984};
825 static void draw_watermark(gfxdevice_t*dev, gfxbbox_t r, char drawall)
827 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
828 double wx = r.xmax / 5.0;
829 double tx = r.xmax*4.0 / 5.0;
830 double ty = r.ymax-wx*watermark2_height/watermark2_width;
831 double sx = (r.xmax - tx) / watermark2_width;
832 double sy = (r.ymax - ty) / watermark2_height;
835 if(ty > 0 && px > 1.0 && py > 1.0) {
837 for(y=0;y<watermark2_height;y++)
838 for(x=0;x<watermark2_width;x++) {
839 if(((watermark2[x]>>y)&1)) {
840 if(!drawall && rand()%5)
842 unsigned int b = rand();
843 moveto(dev, i->tag, x*sx+tx+((b>>1)&1)/20.0, y*sy+ty+((b>>3)&1)/20.0);
844 lineto(dev, i->tag, x*sx+px+tx+((b>>2)&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+py+ty+((b>>4)&1)/20.0);
846 lineto(dev, i->tag, x*sx+tx+((b>>1)&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+ty+((b>>3)&1)/20.0);
853 static void swfoutput_setfillcolor(gfxdevice_t* dev, U8 r, U8 g, U8 b, U8 a)
855 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
856 if(i->fillrgb.r == r &&
859 i->fillrgb.a == a) return;
868 static void insert_watermark(gfxdevice_t*dev, char drawall)
870 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
871 if(!drawall && i->watermarks>20)
877 swfoutput_setfillcolor(dev, 0,0,255,192);
879 swfoutput_setfillcolor(dev, rand(),rand(),rand(),(rand()&127)+128);
884 gfxbbox_t r; r.xmin = r.ymin = 0;
887 draw_watermark(dev, r, drawall);
893 static void endpage(gfxdevice_t*dev)
895 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
906 if(i->config_watermark) {
907 insert_watermark(dev, 1);
913 static void addViewer(gfxdevice_t* dev)
915 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
918 RGBA button_colors[3]= {{0xbf,0x00,0x00,0x80},{0xbf,0x20,0x20,0xc0}, {0xbf,0xc0,0xc0,0xff}};
920 int button_sizex = 20;
921 int button_sizey = 20;
923 RGBA black = {255,0,0,0};
925 i->tag = swf_InsertTag(i->tag,ST_DEFINESHAPE3);
927 int ls1 = swf_ShapeAddLineStyle(s,40,&black);
928 int fs1 = swf_ShapeAddSolidFillStyle(s,&button_colors[t/2]);
929 int shapeid = ids[t] = getNewID(dev);
930 swf_SetU16(i->tag,shapeid);
932 r.xmin = -20*button_sizex;
933 r.xmax = 20*button_sizex;
935 r.ymax = 40*button_sizey;
936 swf_SetRect(i->tag,&r); // set shape bounds
937 swf_SetShapeHeader(i->tag,s); // write all styles to tag
938 swf_ShapeSetAll(i->tag,s,0*button_sizex,0,ls1,fs1,0);
939 swf_ShapeSetLine(i->tag,s,(1-(t&1)*2)*20*button_sizex,20*button_sizey);
940 swf_ShapeSetLine(i->tag,s,-(1-(t&1)*2)*20*button_sizex,20*button_sizey);
941 swf_ShapeSetLine(i->tag,s,0,-40*button_sizey);
942 swf_ShapeSetEnd(i->tag); // finish drawing
943 swf_ShapeFree(s); // clean shape structure (which isn't needed anymore after writing the tag)
945 ActionTAG*a1=0,*a2=0,*a3=0;
946 a1 = action_NextFrame(a1);
947 a1 = action_Stop(a1);
950 a2 = action_PreviousFrame(a2);
951 a2 = action_Stop(a2);
954 a3 = action_Stop(a3);
957 i->tag = swf_InsertTag(i->tag, ST_DOACTION);
958 swf_ActionSet(i->tag,a3);
960 i->tag = swf_InsertTag(i->tag,ST_DEFINEBUTTON);
961 int buttonid1 = getNewID(dev);
962 swf_SetU16(i->tag, buttonid1);
963 swf_ButtonSetRecord(i->tag,BS_UP|BS_HIT,ids[0],1,NULL,NULL);
964 swf_ButtonSetRecord(i->tag,BS_OVER,ids[2],1,NULL,NULL);
965 swf_ButtonSetRecord(i->tag,BS_DOWN,ids[4],1,NULL,NULL);
966 swf_SetU8(i->tag,0); // end of button records
967 swf_ActionSet(i->tag,a1);
969 i->tag = swf_InsertTag(i->tag,ST_DEFINEBUTTON);
970 int buttonid2 = getNewID(dev);
971 swf_SetU16(i->tag, buttonid2);
972 swf_ButtonSetRecord(i->tag,BS_UP|BS_HIT,ids[1],1,NULL,NULL);
973 swf_ButtonSetRecord(i->tag,BS_OVER,ids[3],1,NULL,NULL);
974 swf_ButtonSetRecord(i->tag,BS_DOWN,ids[5],1,NULL,NULL);
975 swf_SetU8(i->tag,0); // end of button records
976 swf_ActionSet(i->tag,a2);
978 i->tag = swf_InsertTag(i->tag,ST_PLACEOBJECT2);
980 swf_GetMatrix(0, &m);
981 m.tx = button_sizex*20+200;
982 swf_ObjectPlace(i->tag, buttonid2, 65534,&m,0,0);
983 i->tag = swf_InsertTag(i->tag,ST_PLACEOBJECT2);
984 m.tx = button_sizex*20+200+200;
985 swf_ObjectPlace(i->tag, buttonid1, 65535,&m,0,0);
989 void swf_startframe(gfxdevice_t*dev, int width, int height)
991 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
993 if(i->config_protect) {
994 i->tag = swf_InsertTag(i->tag, ST_PROTECT);
995 i->config_protect = 0;
997 if(i->config_simpleviewer) {
1002 if(!i->firstpage && !i->pagefinished)
1005 msg("<verbose> Starting new SWF page of size %dx%d", width, height);
1007 swf_GetMatrix(0, &i->page_matrix);
1008 i->page_matrix.tx = 0;
1009 i->page_matrix.ty = 0;
1016 /* create a bbox structure with the page size. This is used
1017 for clipping shape and text bounding boxes. As we don't want to
1018 generate bounding boxes which extend beyond the movie size (in
1019 order to not confuse Flash), we clip everything against i->pagebbox */
1020 i->pagebbox.xmin = 0;
1021 i->pagebbox.ymin = 0;
1022 i->pagebbox.xmax = width*20;
1023 i->pagebbox.ymax = height*20;
1025 /* increase SWF's bounding box */
1026 swf_ExpandRect2(&i->swf->movieSize, &i->pagebbox);
1028 i->lastframeno = i->frameno;
1030 i->pagefinished = 0;
1033 void swf_endframe(gfxdevice_t*dev)
1035 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1037 if(!i->pagefinished)
1040 if( (i->swf->fileVersion <= 8) && (i->config_insertstoptag) ) {
1042 atag = action_Stop(atag);
1043 atag = action_End(atag);
1044 i->tag = swf_InsertTag(i->tag,ST_DOACTION);
1045 swf_ActionSet(i->tag,atag);
1047 i->tag = swf_InsertTag(i->tag,ST_SHOWFRAME);
1050 for(i->depth;i->depth>i->startdepth;i->depth--) {
1051 i->tag = swf_InsertTag(i->tag,ST_REMOVEOBJECT2);
1052 swf_SetU16(i->tag,i->depth);
1054 i->depth = i->startdepth;
1056 if(i->config_frameresets) {
1057 for(i->currentswfid;i->currentswfid>i->startids;i->currentswfid--) {
1058 i->tag = swf_InsertTag(i->tag,ST_FREECHARACTER);
1059 swf_SetU16(i->tag,i->currentswfid);
1061 i->currentswfid = i->startids;
1065 static void setBackground(gfxdevice_t*dev, int x1, int y1, int x2, int y2)
1067 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1069 rgb.a = rgb.r = rgb.g = rgb.b = 0xff;
1073 int shapeid = getNewID(dev);
1078 i->tag = swf_InsertTag(i->tag, ST_DEFINESHAPE);
1080 fs1 = swf_ShapeAddSolidFillStyle(s, &rgb);
1081 swf_SetU16(i->tag,shapeid);
1082 swf_SetRect(i->tag,&r);
1083 swf_SetShapeHeader(i->tag,s);
1084 swf_ShapeSetAll(i->tag,s,x1,y1,ls1,fs1,0);
1085 swf_ShapeSetLine(i->tag,s,(x2-x1),0);
1086 swf_ShapeSetLine(i->tag,s,0,(y2-y1));
1087 swf_ShapeSetLine(i->tag,s,(x1-x2),0);
1088 swf_ShapeSetLine(i->tag,s,0,(y1-y2));
1089 swf_ShapeSetEnd(i->tag);
1091 i->tag = swf_InsertTag(i->tag, ST_PLACEOBJECT2);
1092 swf_ObjectPlace(i->tag,shapeid,getNewDepth(dev),0,0,0);
1093 i->tag = swf_InsertTag(i->tag, ST_PLACEOBJECT2);
1094 swf_ObjectPlaceClip(i->tag,shapeid,getNewDepth(dev),0,0,0,65535);
1097 /* initialize the swf writer */
1098 void gfxdevice_swf_init(gfxdevice_t* dev)
1100 memset(dev, 0, sizeof(gfxdevice_t));
1104 dev->internal = init_internal_struct(); // set config to default values
1106 dev->startpage = swf_startframe;
1107 dev->endpage = swf_endframe;
1108 dev->finish = swf_finish;
1109 dev->fillbitmap = swf_fillbitmap;
1110 dev->setparameter = swf_setparameter;
1111 dev->stroke = swf_stroke;
1112 dev->startclip = swf_startclip;
1113 dev->endclip = swf_endclip;
1114 dev->fill = swf_fill;
1115 dev->fillbitmap = swf_fillbitmap;
1116 dev->fillgradient = swf_fillgradient;
1117 dev->addfont = swf_addfont;
1118 dev->drawchar = swf_drawchar;
1119 dev->drawlink = swf_drawlink;
1121 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1124 msg("<verbose> initializing swf output\n", i->max_x,i->max_y);
1128 i->swf = (SWF*)rfx_calloc(sizeof(SWF));
1129 i->swf->fileVersion = 0;
1130 i->swf->frameRate = 0x80;
1131 i->swf->movieSize.xmin = 0;
1132 i->swf->movieSize.ymin = 0;
1133 i->swf->movieSize.xmax = 0;
1134 i->swf->movieSize.ymax = 0;
1135 i->swf->fileAttributes = 9; // as3, local-with-network
1137 i->swf->firstTag = swf_InsertTag(NULL,ST_SETBACKGROUNDCOLOR);
1138 i->tag = i->swf->firstTag;
1140 rgb.a = rgb.r = rgb.g = rgb.b = 0xff;
1142 swf_SetRGB(i->tag,&rgb);
1144 i->startdepth = i->depth = 0;
1145 i->startids = i->currentswfid = 0;
1148 static void startshape(gfxdevice_t*dev)
1150 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1155 //if(i->chardatapos && i->chardata[i->chardatapos-1].color.a)
1158 i->tag = swf_InsertTag(i->tag,ST_DEFINESHAPE3);
1160 swf_ShapeNew(&i->shape);
1161 i->linestyleid = swf_ShapeAddLineStyle(i->shape,i->linewidth,&i->strokergb);
1162 i->fillstyleid = swf_ShapeAddSolidFillStyle(i->shape,&i->fillrgb);
1164 RGBA markcol = {0,i->mark[0],i->mark[1],i->mark[2]};
1165 swf_ShapeAddSolidFillStyle(i->shape,&markcol);
1168 i->shapeid = getNewID(dev);
1170 msg("<debug> Using shape id %d", i->shapeid);
1172 swf_SetU16(i->tag,i->shapeid); // ID
1174 i->bboxrectpos = i->tag->len;
1176 swf_SetRect(i->tag,&i->pagebbox);
1178 memset(&i->bboxrect, 0, sizeof(i->bboxrect));
1180 swf_SetShapeStyles(i->tag,i->shape);
1181 swf_ShapeCountBits(i->shape,NULL,NULL);
1182 swf_SetShapeBits(i->tag,i->shape);
1184 /* TODO: do we really need this? */
1185 //swf_ShapeSetAll(i->tag,i->shape,/*x*/0,/*y*/0,i->linestyleid,0,0);
1186 //swf_ShapeSetAll(i->tag,i->shape,/*x*/UNDEFINED_COORD,/*y*/UNDEFINED_COORD,i->linestyleid,0,0);
1187 i->swflastx=i->swflasty=UNDEFINED_COORD;
1188 i->lastwasfill = -1;
1189 i->shapeisempty = 1;
1192 static void starttext(gfxdevice_t*dev)
1194 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1198 if(i->config_watermark) {
1199 insert_watermark(dev, 0);
1201 i->textid = getNewID(dev);
1202 i->swflastx=i->swflasty=0;
1206 /* TODO: move to ../lib/rfxswf */
1207 void changeRect(gfxdevice_t*dev, TAG*tag, int pos, SRECT*newrect)
1209 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1210 /* determine length of old rect */
1214 swf_GetRect(tag, &old);
1215 swf_ResetReadBits(tag);
1216 int pos_end = tag->pos;
1218 int len = tag->len - pos_end;
1219 U8*data = (U8*)malloc(len);
1220 memcpy(data, &tag->data[pos_end], len);
1223 swf_SetRect(tag, newrect);
1224 swf_SetBlock(tag, data, len);
1226 tag->pos = tag->readBit = 0;
1229 void cancelshape(gfxdevice_t*dev)
1231 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1232 /* delete old shape tag */
1234 i->tag = i->tag->prev;
1235 swf_DeleteTag(0, todel);
1236 if(i->shape) {swf_ShapeFree(i->shape);i->shape=0;}
1238 i->bboxrectpos = -1;
1240 // i->currentswfid--; // doesn't work, for some reason
1243 void fixAreas(gfxdevice_t*dev)
1245 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1246 if(!i->shapeisempty && i->fill &&
1247 (i->bboxrect.xmin == i->bboxrect.xmax ||
1248 i->bboxrect.ymin == i->bboxrect.ymax) &&
1249 i->config_minlinewidth >= 0.001
1251 msg("<debug> Shape has size 0: width=%.2f height=%.2f",
1252 (i->bboxrect.xmax-i->bboxrect.xmin)/20.0,
1253 (i->bboxrect.ymax-i->bboxrect.ymin)/20.0
1256 SRECT r = i->bboxrect;
1258 if(r.xmin == r.xmax && r.ymin == r.ymax) {
1259 /* this thing comes down to a single dot- nothing to fix here */
1265 RGBA save_col = i->strokergb;
1266 int save_width = i->linewidth;
1268 i->strokergb = i->fillrgb;
1269 i->linewidth = (int)(i->config_minlinewidth*20);
1270 if(i->linewidth==0) i->linewidth = 1;
1275 moveto(dev, i->tag, r.xmin/20.0,r.ymin/20.0);
1276 lineto(dev, i->tag, r.xmax/20.0,r.ymax/20.0);
1278 i->strokergb = save_col;
1279 i->linewidth = save_width;
1284 static void endshape_noput(gfxdevice_t*dev)
1286 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1289 //changeRect(dev, i->tag, i->bboxrectpos, &i->bboxrect);
1292 swf_ShapeFree(i->shape);
1300 static void endshape(gfxdevice_t*dev)
1302 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1308 if(i->shapeisempty ||
1310 (i->bboxrect.xmin == i->bboxrect.xmax &&
1311 i->bboxrect.ymin == i->bboxrect.ymax))
1313 // delete the shape again, we didn't do anything
1314 msg("<debug> cancelling shape: bbox is (%f,%f,%f,%f)",
1315 i->bboxrect.xmin /20.0,
1316 i->bboxrect.ymin /20.0,
1317 i->bboxrect.xmax /20.0,
1318 i->bboxrect.ymax /20.0
1324 swf_ShapeSetEnd(i->tag);
1326 SRECT r = swf_ClipRect(i->pagebbox, i->bboxrect);
1327 changeRect(dev, i->tag, i->bboxrectpos, &r);
1329 msg("<trace> Placing shape ID %d", i->shapeid);
1331 i->tag = swf_InsertTag(i->tag,ST_PLACEOBJECT2);
1332 MATRIX m = i->page_matrix;
1333 m.tx += i->shapeposx;
1334 m.ty += i->shapeposy;
1335 swf_ObjectPlace(i->tag,i->shapeid,getNewDepth(dev),&m,NULL,NULL);
1337 if(i->config_animate) {
1338 i->tag = swf_InsertTag(i->tag,ST_SHOWFRAME);
1341 swf_ShapeFree(i->shape);
1344 i->bboxrectpos = -1;
1351 void wipeSWF(SWF*swf)
1353 TAG*tag = swf->firstTag;
1355 TAG*next = tag->next;
1356 if(tag->id != ST_SETBACKGROUNDCOLOR &&
1357 tag->id != ST_END &&
1358 tag->id != ST_DOACTION &&
1359 tag->id != ST_SHOWFRAME) {
1360 swf_DeleteTag(swf, tag);
1366 void swfoutput_finalize(gfxdevice_t*dev)
1368 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1370 if(i->tag && i->tag->id == ST_END)
1371 return; //already done
1373 i->swf->fileVersion = i->config_flashversion;
1374 i->swf->frameRate = i->config_framerate*0x100;
1376 if(i->config_bboxvars) {
1377 TAG* tag = swf_InsertTag(i->swf->firstTag, ST_DOACTION);
1379 a = action_PushString(a, "xmin");
1380 a = action_PushFloat(a, i->swf->movieSize.xmin / 20.0);
1381 a = action_SetVariable(a);
1382 a = action_PushString(a, "ymin");
1383 a = action_PushFloat(a, i->swf->movieSize.ymin / 20.0);
1384 a = action_SetVariable(a);
1385 a = action_PushString(a, "xmax");
1386 a = action_PushFloat(a, i->swf->movieSize.xmax / 20.0);
1387 a = action_SetVariable(a);
1388 a = action_PushString(a, "ymax");
1389 a = action_PushFloat(a, i->swf->movieSize.ymax / 20.0);
1390 a = action_SetVariable(a);
1391 a = action_PushString(a, "width");
1392 a = action_PushFloat(a, (i->swf->movieSize.xmax - i->swf->movieSize.xmin) / 20.0);
1393 a = action_SetVariable(a);
1394 a = action_PushString(a, "height");
1395 a = action_PushFloat(a, (i->swf->movieSize.ymax - i->swf->movieSize.ymin) / 20.0);
1396 a = action_SetVariable(a);
1398 swf_ActionSet(tag, a);
1403 free(i->mark);i->mark = 0;
1407 fontlist_t *iterator = i->fontlist;
1409 TAG*mtag = i->swf->firstTag;
1410 if(iterator->swffont) {
1411 if(!i->config_storeallcharacters) {
1412 msg("<debug> Reducing font %s", iterator->swffont->name);
1413 swf_FontReduce(iterator->swffont);
1415 int used = iterator->swffont->use && iterator->swffont->use->used_glyphs;
1417 mtag = swf_InsertTag(mtag, ST_DEFINEFONT2);
1418 swf_FontSetDefine2(mtag, iterator->swffont);
1422 iterator = iterator->next;
1425 i->tag = swf_InsertTag(i->tag,ST_END);
1426 TAG* tag = i->tag->prev;
1428 /* remove the removeobject2 tags between the last ST_SHOWFRAME
1429 and the ST_END- they confuse the flash player */
1430 while(tag->id == ST_REMOVEOBJECT2) {
1431 TAG* prev = tag->prev;
1432 swf_DeleteTag(i->swf, tag);
1439 if(i->config_enablezlib || i->config_flashversion>=6) {
1440 i->swf->compressed = 1;
1443 /* Add AVM2 actionscript */
1444 if(i->config_flashversion>=9 &&
1445 (i->config_insertstoptag || i->hasbuttons)) {
1446 swf_AddButtonLinks(i->swf, i->config_insertstoptag,
1447 i->config_internallinkfunction||i->config_externallinkfunction);
1449 // if(i->config_reordertags)
1450 // swf_Optimize(i->swf);
1453 int swfresult_save(gfxresult_t*gfx, const char*filename)
1455 SWF*swf = (SWF*)gfx->internal;
1458 fi = open(filename, O_BINARY|O_CREAT|O_TRUNC|O_WRONLY, 0777);
1463 msg("<fatal> Could not create \"%s\". ", FIXNULL(filename));
1467 if FAILED(swf_WriteSWF(fi,swf))
1468 msg("<error> WriteSWF() failed.\n");
1474 void* swfresult_get(gfxresult_t*gfx, const char*name)
1476 SWF*swf = (SWF*)gfx->internal;
1477 if(!strcmp(name, "swf")) {
1478 return (void*)swf_CopySWF(swf);
1479 } else if(!strcmp(name, "xmin")) {
1480 return (void*)(ptroff_t)(swf->movieSize.xmin/20);
1481 } else if(!strcmp(name, "ymin")) {
1482 return (void*)(ptroff_t)(swf->movieSize.ymin/20);
1483 } else if(!strcmp(name, "xmax")) {
1484 return (void*)(ptroff_t)(swf->movieSize.xmax/20);
1485 } else if(!strcmp(name, "ymax")) {
1486 return (void*)(ptroff_t)(swf->movieSize.ymax/20);
1487 } else if(!strcmp(name, "width")) {
1488 return (void*)(ptroff_t)((swf->movieSize.xmax - swf->movieSize.xmin)/20);
1489 } else if(!strcmp(name, "height")) {
1490 return (void*)(ptroff_t)((swf->movieSize.ymax - swf->movieSize.ymin)/20);
1494 void swfresult_destroy(gfxresult_t*gfx)
1497 swf_FreeTags((SWF*)gfx->internal);
1498 free(gfx->internal);
1501 memset(gfx, 0, sizeof(gfxresult_t));
1505 static void swfoutput_destroy(gfxdevice_t* dev);
1507 gfxresult_t* swf_finish(gfxdevice_t* dev)
1509 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1512 if(i->config_linktarget) {
1513 free(i->config_linktarget);
1514 i->config_linktarget = 0;
1517 swfoutput_finalize(dev);
1518 SWF* swf = i->swf;i->swf = 0;
1519 swfoutput_destroy(dev);
1521 result = (gfxresult_t*)rfx_calloc(sizeof(gfxresult_t));
1522 result->internal = swf;
1523 result->save = swfresult_save;
1525 result->get = swfresult_get;
1526 result->destroy = swfresult_destroy;
1530 /* Perform cleaning up */
1531 static void swfoutput_destroy(gfxdevice_t* dev)
1533 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1535 /* not initialized yet- nothing to destroy */
1539 fontlist_t *tmp,*iterator = i->fontlist;
1541 if(iterator->swffont) {
1542 swf_FontFree(iterator->swffont);iterator->swffont=0;
1545 iterator = iterator->next;
1548 if(i->swf) {swf_FreeTags(i->swf);free(i->swf);i->swf = 0;}
1551 memset(dev, 0, sizeof(gfxdevice_t));
1554 static void swfoutput_setstrokecolor(gfxdevice_t* dev, U8 r, U8 g, U8 b, U8 a)
1556 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1557 if(i->strokergb.r == r &&
1558 i->strokergb.g == g &&
1559 i->strokergb.b == b &&
1560 i->strokergb.a == a) return;
1570 //#define ROUND_UP 19
1571 //#define ROUND_UP 10
1573 static void swfoutput_setlinewidth(gfxdevice_t*dev, double _linewidth)
1575 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1576 if(i->linewidth == (U16)(_linewidth*20+19.0/20.0))
1580 i->linewidth = (U16)(_linewidth*20+19.0/20.0);
1584 static void drawlink(gfxdevice_t*dev, ActionTAG*,ActionTAG*, gfxline_t*points, char mouseover, const char*url);
1585 static void swfoutput_namedlink(gfxdevice_t*dev, char*name, gfxline_t*points);
1586 static void swfoutput_linktopage(gfxdevice_t*dev, int page, gfxline_t*points);
1587 static void swfoutput_linktourl(gfxdevice_t*dev, const char*url, gfxline_t*points);
1589 /*void swfoutput_drawlink(gfxdevice_t*dev, char*url, gfxline_t*points)
1591 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1592 dev->drawlink(dev, points, url);
1595 void swf_drawlink(gfxdevice_t*dev, gfxline_t*points, const char*url)
1597 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1599 if(i->config_disablelinks)
1602 if(!strncmp("http://pdf2swf:", url, 15)) {
1603 char*tmp = strdup(url);
1604 int l = strlen(tmp);
1607 swfoutput_namedlink(dev, tmp+15, points);
1610 } else if(!strncmp("page", url, 4)) {
1613 if(url[t]<'0' || url[t]>'9')
1616 int page = atoi(&url[4]);
1617 if(page<0) page = 0;
1618 swfoutput_linktopage(dev, page, points);
1621 swfoutput_linktourl(dev, url, points);
1624 void swfoutput_linktourl(gfxdevice_t*dev, const char*url, gfxline_t*points)
1626 ActionTAG* actions = 0;
1627 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1633 /* TODO: escape special characters in url */
1635 if(i->config_externallinkfunction && i->config_flashversion<=8) {
1636 actions = action_PushString(actions, url); //parameter
1637 actions = action_PushInt(actions, 1); //number of parameters (1)
1638 actions = action_PushString(actions, i->config_externallinkfunction); //function name
1639 actions = action_CallFunction(actions);
1640 } else if(!i->config_linktarget) {
1641 if(!i->config_opennewwindow)
1642 actions = action_GetUrl(actions, url, "_parent");
1644 actions = action_GetUrl(actions, url, "_this");
1646 actions = action_GetUrl(actions, url, i->config_linktarget);
1648 actions = action_End(actions);
1650 drawlink(dev, actions, 0, points, 0, url);
1652 swf_ActionFree(actions);
1654 void swfoutput_linktopage(gfxdevice_t*dev, int page, gfxline_t*points)
1656 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1657 ActionTAG* actions = 0;
1664 if(!i->config_internallinkfunction || i->config_flashversion>=9) {
1665 actions = action_GotoFrame(actions, page-1);
1666 actions = action_End(actions);
1668 actions = action_PushInt(actions, page); //parameter
1669 actions = action_PushInt(actions, 1); //number of parameters (1)
1670 actions = action_PushString(actions, i->config_internallinkfunction); //function name
1671 actions = action_CallFunction(actions);
1672 actions = action_End(actions);
1676 sprintf(name, "page%d", page);
1678 drawlink(dev, actions, 0, points, 0, name);
1680 swf_ActionFree(actions);
1683 /* Named Links (a.k.a. Acrobatmenu) are used to implement various gadgets
1684 of the viewer objects, like subtitles, index elements etc.
1686 void swfoutput_namedlink(gfxdevice_t*dev, char*name, gfxline_t*points)
1688 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1689 ActionTAG *actions1,*actions2;
1690 char*tmp = strdup(name);
1698 if(!strncmp(tmp, "call:", 5))
1700 char*x = strchr(&tmp[5], ':');
1702 actions1 = action_PushInt(0, 0); //number of parameters (0)
1703 actions1 = action_PushString(actions1, &tmp[5]); //function name
1704 actions1 = action_CallFunction(actions1);
1705 actions1 = action_End(actions1);
1708 actions1 = action_PushString(0, x+1); //parameter
1709 actions1 = action_PushInt(actions1, 1); //number of parameters (1)
1710 actions1 = action_PushString(actions1, &tmp[5]); //function name
1711 actions1 = action_CallFunction(actions1);
1712 actions1 = action_End(actions1);
1714 actions2 = action_End(0);
1719 actions1 = action_PushString(0, "/:subtitle");
1720 actions1 = action_PushString(actions1, name);
1721 actions1 = action_SetVariable(actions1);
1722 actions1 = action_End(actions1);
1724 actions2 = action_PushString(0, "/:subtitle");
1725 actions2 = action_PushString(actions2, "");
1726 actions2 = action_SetVariable(actions2);
1727 actions2 = action_End(actions2);
1730 drawlink(dev, actions1, actions2, points, mouseover, name);
1732 swf_ActionFree(actions1);
1733 swf_ActionFree(actions2);
1737 static void drawgfxline(gfxdevice_t*dev, gfxline_t*line, int fill)
1739 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1740 gfxcoord_t lastx=0,lasty=0,px=0,py=0;
1742 int lines= 0, splines=0;
1749 /* check whether the next segment is zero */
1750 if(line->type == gfx_moveTo) {
1751 moveto(dev, i->tag, line->x, line->y);
1752 px = lastx = line->x;
1753 py = lasty = line->y;
1755 } if(line->type == gfx_lineTo) {
1756 lineto(dev, i->tag, line->x, line->y);
1761 } else if(line->type == gfx_splineTo) {
1763 s.x = line->sx;p.x = line->x;
1764 s.y = line->sy;p.y = line->y;
1765 splineto(dev, i->tag, s, p);
1773 msg("<trace> drawgfxline, %d lines, %d splines", lines, splines);
1777 static void drawlink(gfxdevice_t*dev, ActionTAG*actions1, ActionTAG*actions2, gfxline_t*points, char mouseover, const char*url)
1779 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1788 int buttonid = getNewID(dev);
1789 gfxbbox_t bbox = gfxline_getbbox(points);
1794 myshapeid = getNewID(dev);
1795 i->tag = swf_InsertTag(i->tag,ST_DEFINESHAPE3);
1796 swf_ShapeNew(&i->shape);
1797 rgb.r = rgb.b = rgb.a = rgb.g = 0;
1798 fsid = swf_ShapeAddSolidFillStyle(i->shape,&rgb);
1799 swf_SetU16(i->tag, myshapeid);
1800 r.xmin = (int)(bbox.xmin*20);
1801 r.ymin = (int)(bbox.ymin*20);
1802 r.xmax = (int)(bbox.xmax*20);
1803 r.ymax = (int)(bbox.ymax*20);
1804 r = swf_ClipRect(i->pagebbox, r);
1805 swf_SetRect(i->tag,&r);
1806 swf_SetShapeStyles(i->tag,i->shape);
1807 swf_ShapeCountBits(i->shape,NULL,NULL);
1808 swf_SetShapeBits(i->tag,i->shape);
1809 swf_ShapeSetAll(i->tag,i->shape,/*x*/0,/*y*/0,0,fsid,0);
1810 i->swflastx = i->swflasty = 0;
1811 drawgfxline(dev, points, 1);
1812 swf_ShapeSetEnd(i->tag);
1813 swf_ShapeFree(i->shape);
1816 myshapeid2 = getNewID(dev);
1817 i->tag = swf_InsertTag(i->tag,ST_DEFINESHAPE3);
1818 swf_ShapeNew(&i->shape);
1820 rgb = i->config_linkcolor;
1822 fsid = swf_ShapeAddSolidFillStyle(i->shape,&rgb);
1823 swf_SetU16(i->tag, myshapeid2);
1824 r.xmin = (int)(bbox.xmin*20);
1825 r.ymin = (int)(bbox.ymin*20);
1826 r.xmax = (int)(bbox.xmax*20);
1827 r.ymax = (int)(bbox.ymax*20);
1828 r = swf_ClipRect(i->pagebbox, r);
1829 swf_SetRect(i->tag,&r);
1830 swf_SetShapeStyles(i->tag,i->shape);
1831 swf_ShapeCountBits(i->shape,NULL,NULL);
1832 swf_SetShapeBits(i->tag,i->shape);
1833 swf_ShapeSetAll(i->tag,i->shape,/*x*/0,/*y*/0,0,fsid,0);
1834 i->swflastx = i->swflasty = 0;
1835 drawgfxline(dev, points, 1);
1836 swf_ShapeSetEnd(i->tag);
1837 swf_ShapeFree(i->shape);
1841 i->tag = swf_InsertTag(i->tag,ST_DEFINEBUTTON);
1842 swf_SetU16(i->tag,buttonid); //id
1843 swf_ButtonSetFlags(i->tag, 0); //menu=no
1844 swf_ButtonSetRecord(i->tag,0x01,myshapeid,i->depth,0,0);
1845 swf_ButtonSetRecord(i->tag,0x02,myshapeid2,i->depth,0,0);
1846 swf_ButtonSetRecord(i->tag,0x04,myshapeid2,i->depth,0,0);
1847 swf_ButtonSetRecord(i->tag,0x08,myshapeid,i->depth,0,0);
1848 swf_SetU8(i->tag,0);
1849 swf_ActionSet(i->tag,actions1);
1850 swf_SetU8(i->tag,0);
1854 i->tag = swf_InsertTag(i->tag,ST_DEFINEBUTTON2);
1855 swf_SetU16(i->tag,buttonid); //id
1856 swf_ButtonSetFlags(i->tag, 0); //menu=no
1857 swf_ButtonSetRecord(i->tag,0x01,myshapeid,i->depth,0,0);
1858 swf_ButtonSetRecord(i->tag,0x02,myshapeid2,i->depth,0,0);
1859 swf_ButtonSetRecord(i->tag,0x04,myshapeid2,i->depth,0,0);
1860 swf_ButtonSetRecord(i->tag,0x08,myshapeid,i->depth,0,0);
1861 swf_SetU8(i->tag,0); // end of button records
1862 swf_ButtonSetCondition(i->tag, BC_IDLE_OVERUP);
1863 swf_ActionSet(i->tag,actions1);
1865 swf_ButtonSetCondition(i->tag, BC_OVERUP_IDLE);
1866 swf_ActionSet(i->tag,actions2);
1867 swf_SetU8(i->tag,0);
1868 swf_ButtonPostProcess(i->tag, 2);
1870 swf_SetU8(i->tag,0);
1871 swf_ButtonPostProcess(i->tag, 1);
1875 const char* name = 0;
1876 if(i->config_linknameurl) {
1880 sprintf(buf, "button%d", buttonid);
1883 msg("<trace> Placing link ID %d", buttonid);
1884 i->tag = swf_InsertTag(i->tag,ST_PLACEOBJECT2);
1886 if(posx!=0 || posy!=0) {
1888 p.x = (int)(posx*20);
1889 p.y = (int)(posy*20);
1890 p = swf_TurnPoint(p, &i->page_matrix);
1895 swf_ObjectPlace(i->tag, buttonid, getNewDepth(dev),&m,0,(U8*)name);
1897 swf_ObjectPlace(i->tag, buttonid, getNewDepth(dev),&i->page_matrix,0,(U8*)name);
1904 for(t=0;t<picpos;t++)
1906 if(pic_xids[t] == xid &&
1907 pic_yids[t] == yid) {
1908 width = pic_width[t];
1909 height = pic_height[t];
1913 pic_ids[picpos] = swfoutput_drawimagelosslessN(&output, pic, pal, width, height, x1,y1,x2,y2,x3,y3,x4,y4, numpalette);
1914 pic_xids[picpos] = xid;
1915 pic_yids[picpos] = yid;
1916 pic_width[picpos] = width;
1917 pic_height[picpos] = height;
1920 pic[width*y+x] = buf[0];
1924 xid += pal[1].r*3 + pal[1].g*11 + pal[1].b*17;
1925 yid += pal[1].r*7 + pal[1].g*5 + pal[1].b*23;
1929 xid += x*r+x*b*3+x*g*7+x*a*11;
1930 yid += y*r*3+y*b*17+y*g*19+y*a*11;
1932 for(t=0;t<picpos;t++)
1934 if(pic_xids[t] == xid &&
1935 pic_yids[t] == yid) {
1944 int swf_setparameter(gfxdevice_t*dev, const char*name, const char*value)
1946 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1948 msg("<trace> swfdevice: %s=%s", name, value);
1949 if(!strcmp(name, "jpegsubpixels")) {
1950 i->config_jpegsubpixels = atof(value);
1951 } else if(!strcmp(name, "ppmsubpixels")) {
1952 i->config_ppmsubpixels = atof(value);
1953 } else if(!strcmp(name, "subpixels")) {
1954 i->config_ppmsubpixels = i->config_jpegsubpixels = atof(value);
1955 } else if(!strcmp(name, "drawonlyshapes")) {
1956 i->config_drawonlyshapes = atoi(value);
1957 } else if(!strcmp(name, "ignoredraworder")) {
1958 i->config_ignoredraworder = atoi(value);
1959 } else if(!strcmp(name, "mark")) {
1960 if(!value || !value[0]) {
1961 if(i->mark) free(i->mark);
1965 i->mark = strdup("...");
1966 for(t=0;t<3;t++) if(value[t]) i->mark[t] = value[t];
1968 } else if(!strcmp(name, "filloverlap")) {
1969 i->config_filloverlap = atoi(value);
1970 } else if(!strcmp(name, "linksopennewwindow")) {
1971 i->config_opennewwindow = atoi(value);
1972 } else if(!strcmp(name, "opennewwindow")) {
1973 i->config_opennewwindow = atoi(value);
1974 } else if(!strcmp(name, "storeallcharacters")) {
1975 i->config_storeallcharacters = atoi(value);
1976 } else if(!strcmp(name, "enablezlib")) {
1977 i->config_enablezlib = atoi(value);
1978 } else if(!strcmp(name, "bboxvars")) {
1979 i->config_bboxvars = atoi(value);
1980 } else if(!strcmp(name, "dots")) {
1981 i->config_dots = atoi(value);
1982 } else if(!strcmp(name, "frameresets")) {
1983 i->config_frameresets = atoi(value);
1984 } else if(!strcmp(name, "showclipshapes")) {
1985 i->config_showclipshapes = atoi(value);
1986 } else if(!strcmp(name, "reordertags")) {
1987 i->config_reordertags = atoi(value);
1988 } else if(!strcmp(name, "internallinkfunction")) {
1989 i->config_internallinkfunction = strdup(value);
1990 } else if(!strcmp(name, "externallinkfunction")) {
1991 i->config_externallinkfunction = strdup(value);
1992 } else if(!strcmp(name, "linkfunction")) { //sets both internallinkfunction and externallinkfunction
1993 i->config_internallinkfunction = strdup(value);
1994 i->config_externallinkfunction = strdup(value);
1995 } else if(!strcmp(name, "disable_polygon_conversion")) {
1996 i->config_disable_polygon_conversion = atoi(value);
1997 } else if(!strcmp(name, "normalize_polygon_positions")) {
1998 i->config_normalize_polygon_positions = atoi(value);
1999 } else if(!strcmp(name, "wxwindowparams")) {
2000 i->config_watermark = atoi(value);
2001 } else if(!strcmp(name, "insertstop")) {
2002 i->config_insertstoptag = atoi(value);
2003 } else if(!strcmp(name, "protect")) {
2004 i->config_protect = atoi(value);
2005 if(i->config_protect && i->tag) {
2006 i->tag = swf_InsertTag(i->tag, ST_PROTECT);
2008 } else if(!strcmp(name, "flashversion")) {
2009 i->config_flashversion = atoi(value);
2011 i->swf->fileVersion = i->config_flashversion;
2013 } else if(!strcmp(name, "framerate")) {
2014 i->config_framerate = atof(value);
2016 i->swf->frameRate = i->config_framerate*0x100;
2018 } else if(!strcmp(name, "minlinewidth")) {
2019 i->config_minlinewidth = atof(value);
2020 } else if(!strcmp(name, "caplinewidth")) {
2021 i->config_caplinewidth = atof(value);
2022 } else if(!strcmp(name, "linktarget")) {
2023 i->config_linktarget = strdup(value);
2024 } else if(!strcmp(name, "dumpfonts")) {
2025 i->config_dumpfonts = atoi(value);
2026 } else if(!strcmp(name, "animate")) {
2027 i->config_animate = atoi(value);
2028 } else if(!strcmp(name, "disablelinks")) {
2029 i->config_disablelinks = atoi(value);
2030 } else if(!strcmp(name, "simpleviewer")) {
2031 i->config_simpleviewer = atoi(value);
2032 } else if(!strcmp(name, "next_bitmap_is_jpeg")) {
2034 } else if(!strcmp(name, "jpegquality")) {
2035 int val = atoi(value);
2037 if(val>101) val=101;
2038 i->config_jpegquality = val;
2039 } else if(!strcmp(name, "splinequality")) {
2040 int v = atoi(value);
2041 v = 500-(v*5); // 100% = 0.25 pixel, 0% = 25 pixel
2043 i->config_splinemaxerror = v;
2044 } else if(!strcmp(name, "fontquality")) {
2045 int v = atoi(value);
2046 v = 500-(v*5); // 100% = 0.25 pixel, 0% = 25 pixel
2048 i->config_fontsplinemaxerror = v;
2049 } else if(!strcmp(name, "linkcolor")) {
2050 if(strlen(value)!=8) {
2051 fprintf(stderr, "Unknown format for option 'linkcolor'. (%s <-> RRGGBBAA)\n", value);
2054 # define NIBBLE(s) (((s)>='0' && (s)<='9')?((s)-'0'):((s)&0x0f)+9)
2055 i->config_linkcolor.r = NIBBLE(value[0])<<4 | NIBBLE(value[1]);
2056 i->config_linkcolor.g = NIBBLE(value[2])<<4 | NIBBLE(value[3]);
2057 i->config_linkcolor.b = NIBBLE(value[4])<<4 | NIBBLE(value[5]);
2058 i->config_linkcolor.a = NIBBLE(value[6])<<4 | NIBBLE(value[7]);
2059 } else if(!strcmp(name, "help")) {
2060 printf("\nSWF layer options:\n");
2061 printf("jpegsubpixels=<pixels> resolution adjustment for jpeg images (same as jpegdpi, but in pixels)\n");
2062 printf("ppmsubpixels=<pixels resolution adjustment for lossless images (same as ppmdpi, but in pixels)\n");
2063 printf("subpixels=<pixels> shortcut for setting both jpegsubpixels and ppmsubpixels\n");
2064 printf("drawonlyshapes convert everything to shapes (currently broken)\n");
2065 printf("ignoredraworder allow to perform a few optimizations for creating smaller SWFs\n");
2066 printf("linksopennewwindow make links open a new browser window\n");
2067 printf("linktarget target window name of new links\n");
2068 printf("linkcolor=<color) color of links (format: RRGGBBAA)\n");
2069 printf("linknameurl Link buttons will be named like the URL they refer to (handy for iterating through links with actionscript)\n");
2070 printf("storeallcharacters don't reduce the fonts to used characters in the output file\n");
2071 printf("enablezlib switch on zlib compression (also done if flashversion>=7)\n");
2072 printf("bboxvars store the bounding box of the SWF file in actionscript variables\n");
2073 printf("dots Take care to handle dots correctly\n");
2074 printf("reordertags=0/1 (default: 1) perform some tag optimizations\n");
2075 printf("internallinkfunction=<name> when the user clicks a internal link (to a different page) in the converted file, this actionscript function is called\n");
2076 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");
2077 printf("disable_polygon_conversion never convert strokes to polygons (will remove capstyles and joint styles)\n");
2078 printf("caplinewidth=<width> the minimum thichness a line needs to have so that capstyles become visible (and are converted)\n");
2079 printf("insertstop put an ActionScript \"STOP\" tag in every frame\n");
2080 printf("protect add a \"protect\" tag to the file, to prevent loading in the Flash editor\n");
2081 printf("flashversion=<version> the SWF fileversion (6)\n");
2082 printf("framerate=<fps> SWF framerate\n");
2083 printf("minlinewidth=<width> convert horizontal/vertical boxes smaller than this width to lines (0.05) \n");
2084 printf("simpleviewer Add next/previous buttons to the SWF\n");
2085 printf("animate insert a showframe tag after each placeobject (animate draw order of PDF files)\n");
2086 printf("jpegquality=<quality> set compression quality of jpeg images\n");
2087 printf("splinequality=<value> Set the quality of spline convertion to value (0-100, default: 100).\n");
2088 printf("disablelinks Disable links.\n");
2095 // --------------------------------------------------------------------
2097 static CXFORM gfxcxform_to_cxform(gfxcxform_t* c)
2100 swf_GetCXForm(0, &cx, 1);
2103 if(c->rg!=0 || c->rb!=0 || c->ra!=0 ||
2104 c->gr!=0 || c->gb!=0 || c->ga!=0 ||
2105 c->br!=0 || c->bg!=0 || c->ba!=0 ||
2106 c->ar!=0 || c->ag!=0 || c->ab!=0)
2107 msg("<warning> CXForm not SWF-compatible");
2109 cx.a0 = (S16)(c->aa*256);
2110 cx.r0 = (S16)(c->rr*256);
2111 cx.g0 = (S16)(c->gg*256);
2112 cx.b0 = (S16)(c->bb*256);
2121 static int imageInCache(gfxdevice_t*dev, void*data, int width, int height)
2125 static void addImageToCache(gfxdevice_t*dev, void*data, int width, int height)
2129 static int add_image(swfoutput_internal*i, gfximage_t*img, int targetwidth, int targetheight, int* newwidth, int* newheight)
2131 gfxdevice_t*dev = i->dev;
2133 RGBA*mem = (RGBA*)img->data;
2135 int sizex = img->width;
2136 int sizey = img->height;
2137 int is_jpeg = i->jpeg;
2140 int newsizex=sizex, newsizey=sizey;
2143 if(is_jpeg && i->config_jpegsubpixels) {
2144 newsizex = (int)(targetwidth*i->config_jpegsubpixels + 0.5);
2145 newsizey = (int)(targetheight*i->config_jpegsubpixels + 0.5);
2146 } else if(!is_jpeg && i->config_ppmsubpixels) {
2147 newsizex = (int)(targetwidth*i->config_ppmsubpixels + 0.5);
2148 newsizey = (int)(targetheight*i->config_ppmsubpixels + 0.5);
2152 if(sizex<=0 || sizey<=0)
2159 /* TODO: cache images */
2161 if(newsizex<sizex || newsizey<sizey) {
2162 msg("<verbose> Scaling %dx%d image to %dx%d", sizex, sizey, newsizex, newsizey);
2163 newpic = swf_ImageScale(mem, sizex, sizey, newsizex, newsizey);
2164 *newwidth = sizex = newsizex;
2165 *newheight = sizey = newsizey;
2168 *newwidth = newsizex = sizex;
2169 *newheight = newsizey = sizey;
2172 int num_colors = swf_ImageGetNumberOfPaletteEntries(mem,sizex,sizey,0);
2173 int has_alpha = swf_ImageHasAlpha(mem,sizex,sizey);
2175 msg("<verbose> Drawing %dx%d %s%simage (id %d) at size %dx%d (%dx%d), %s%d colors",
2177 has_alpha?(has_alpha==2?"semi-transparent ":"transparent "):"",
2178 is_jpeg?"jpeg-":"", i->currentswfid+1,
2180 targetwidth, targetheight,
2181 /*newsizex, newsizey,*/
2182 num_colors>256?">":"", num_colors>256?256:num_colors);
2184 /*RGBA* pal = (RGBA*)rfx_alloc(sizeof(RGBA)*num_colors);
2185 swf_ImageGetNumberOfPaletteEntries(mem,sizex,sizey,pal);
2187 for(t=0;t<num_colors;t++) {
2188 printf("%02x%02x%02x%02x ",
2189 pal[t].r, pal[t].g, pal[t].b, pal[t].a);
2196 int cacheid = imageInCache(dev, mem, sizex, sizey);
2199 bitid = getNewID(dev);
2201 i->tag = swf_AddImage(i->tag, bitid, mem, sizex, sizey, i->config_jpegquality);
2202 addImageToCache(dev, mem, sizex, sizey);
2212 static SRECT gfxline_getSWFbbox(gfxline_t*line)
2214 gfxbbox_t bbox = gfxline_getbbox(line);
2216 r.xmin = (int)(bbox.xmin*20);
2217 r.ymin = (int)(bbox.ymin*20);
2218 r.xmax = (int)(bbox.xmax*20);
2219 r.ymax = (int)(bbox.ymax*20);
2223 int line_is_empty(gfxline_t*line)
2226 if(line->type != gfx_moveTo)
2233 static void swf_fillbitmap(gfxdevice_t*dev, gfxline_t*line, gfximage_t*img, gfxmatrix_t*matrix, gfxcxform_t*cxform)
2235 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
2237 if(line_is_empty(line))
2243 int targetx = (int)(sqrt(matrix->m00*matrix->m00 + matrix->m01*matrix->m01)*img->width);
2244 int targety = (int)(sqrt(matrix->m10*matrix->m10 + matrix->m11*matrix->m11)*img->height);
2246 int newwidth=0,newheight=0;
2247 int bitid = add_image(i, img, targetx, targety, &newwidth, &newheight);
2250 double fx = (double)img->width / (double)newwidth;
2251 double fy = (double)img->height / (double)newheight;
2254 m.sx = (int)(65536*20*matrix->m00*fx); m.r1 = (int)(65536*20*matrix->m10*fy);
2255 m.r0 = (int)(65536*20*matrix->m01*fx); m.sy = (int)(65536*20*matrix->m11*fy);
2256 m.tx = (int)(matrix->tx*20);
2257 m.ty = (int)(matrix->ty*20);
2260 int myshapeid = getNewID(dev);
2261 i->tag = swf_InsertTag(i->tag,ST_DEFINESHAPE);
2263 swf_ShapeNew(&shape);
2264 int fsid = swf_ShapeAddBitmapFillStyle(shape,&m,bitid,1);
2265 swf_SetU16(i->tag, myshapeid);
2266 SRECT r = gfxline_getSWFbbox(line);
2267 r = swf_ClipRect(i->pagebbox, r);
2268 swf_SetRect(i->tag,&r);
2269 swf_SetShapeStyles(i->tag,shape);
2270 swf_ShapeCountBits(shape,NULL,NULL);
2271 swf_SetShapeBits(i->tag,shape);
2272 swf_ShapeSetAll(i->tag,shape,UNDEFINED_COORD,UNDEFINED_COORD,0,fsid,0);
2273 i->swflastx = i->swflasty = UNDEFINED_COORD;
2274 drawgfxline(dev, line, 1);
2275 swf_ShapeSetEnd(i->tag);
2276 swf_ShapeFree(shape);
2278 msg("<trace> Placing image, shape ID %d, bitmap ID %d", myshapeid, bitid);
2279 i->tag = swf_InsertTag(i->tag,ST_PLACEOBJECT2);
2280 CXFORM cxform2 = gfxcxform_to_cxform(cxform);
2281 swf_ObjectPlace(i->tag,myshapeid,getNewDepth(dev),&i->page_matrix,&cxform2,NULL);
2284 static RGBA col_black = {255,0,0,0};
2286 static void drawoutline(gfxdevice_t*dev, gfxline_t*line)
2288 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
2290 int myshapeid = getNewID(dev);
2291 i->tag = swf_InsertTag(i->tag,ST_DEFINESHAPE3);
2294 swf_ShapeNew(&shape);
2295 int lsid = swf_ShapeAddLineStyle(shape,1,&col_black);
2297 swf_SetU16(i->tag,myshapeid);
2298 SRECT r = gfxline_getSWFbbox(line);
2299 r = swf_ClipRect(i->pagebbox, r);
2300 swf_SetRect(i->tag,&r);
2301 swf_SetShapeStyles(i->tag,shape);
2302 swf_ShapeCountBits(shape,NULL,NULL);
2303 swf_SetShapeBits(i->tag,shape);
2304 swf_ShapeSetAll(i->tag,shape,UNDEFINED_COORD,UNDEFINED_COORD,lsid,0,0);
2305 drawgfxline(dev, line, 1);
2306 swf_ShapeSetEnd(i->tag);
2307 swf_ShapeFree(shape);
2309 i->tag = swf_InsertTag(i->tag,ST_PLACEOBJECT2);
2310 swf_ObjectPlace(i->tag, myshapeid, getNewDepth(dev), 0,0,0);
2313 static void swf_startclip(gfxdevice_t*dev, gfxline_t*line)
2315 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
2320 if(i->clippos >= 127)
2322 msg("<warning> Too many clip levels.");
2326 if(i->config_showclipshapes)
2327 drawoutline(dev, line);
2329 int myshapeid = getNewID(dev);
2330 i->tag = swf_InsertTag(i->tag,ST_DEFINESHAPE3);
2332 memset(&col, 0, sizeof(RGBA));
2335 swf_ShapeNew(&shape);
2336 int fsid = swf_ShapeAddSolidFillStyle(shape,&col);
2338 RGBA markcol = {0,i->mark[0],i->mark[1],i->mark[2]};
2339 swf_ShapeAddSolidFillStyle(shape,&markcol);
2341 swf_SetU16(i->tag,myshapeid);
2342 SRECT r = gfxline_getSWFbbox(line);
2343 r = swf_ClipRect(i->pagebbox, r);
2344 swf_SetRect(i->tag,&r);
2345 swf_SetShapeStyles(i->tag,shape);
2346 swf_ShapeCountBits(shape,NULL,NULL);
2347 swf_SetShapeBits(i->tag,shape);
2348 swf_ShapeSetAll(i->tag,shape,UNDEFINED_COORD,UNDEFINED_COORD,0,fsid,0);
2349 i->swflastx = i->swflasty = UNDEFINED_COORD;
2350 i->shapeisempty = 1;
2351 drawgfxline(dev, line, 1);
2352 if(i->shapeisempty) {
2353 /* an empty clip shape is equivalent to a shape with no area */
2354 int x = line?line->x:0;
2355 int y = line?line->y:0;
2356 moveto(dev, i->tag, x,y);
2357 lineto(dev, i->tag, x,y);
2358 lineto(dev, i->tag, x,y);
2360 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)) {
2361 if(i->config_watermark) {
2362 gfxbbox_t r; r.xmin = r.ymin = 0;r.xmax = i->max_x;r.ymax = i->max_y;
2363 draw_watermark(dev, r, 1);
2366 swf_ShapeSetEnd(i->tag);
2367 swf_ShapeFree(shape);
2369 /* TODO: remember the bbox, and check all shapes against it */
2371 msg("<trace> Placing clip ID %d", myshapeid);
2372 i->tag = swf_InsertTag(i->tag,ST_PLACEOBJECT2);
2373 i->cliptags[i->clippos] = i->tag;
2374 i->clipshapes[i->clippos] = myshapeid;
2375 i->clipdepths[i->clippos] = getNewDepth(dev);
2379 static void swf_endclip(gfxdevice_t*dev)
2381 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
2388 msg("<error> Invalid end of clipping region");
2392 /*swf_ObjectPlaceClip(i->cliptags[i->clippos],i->clipshapes[i->clippos],i->clipdepths[i->clippos],&i->page_matrix,NULL,NULL,
2393 / * clip to depth: * / i->depth <= i->clipdepths[i->clippos]? i->depth : i->depth - 1);
2395 swf_ObjectPlaceClip(i->cliptags[i->clippos],i->clipshapes[i->clippos],i->clipdepths[i->clippos],&i->page_matrix,NULL,NULL,i->depth);
2397 static int gfxline_type(gfxline_t*line)
2403 int haszerosegments=0;
2406 if(line->type == gfx_moveTo) {
2409 } else if(line->type == gfx_lineTo) {
2413 } else if(line->type == gfx_splineTo) {
2415 if(tmpsplines>lines)
2423 if(lines==0 && splines==0) return 0;
2424 else if(lines==1 && splines==0) return 1;
2425 else if(lines==0 && splines==1) return 2;
2426 else if(splines==0) return 3;
2430 static int gfxline_has_dots(gfxline_t*line)
2438 if(line->type == gfx_moveTo) {
2439 /* test the length of the preceding line, and assume it is a dot if
2440 it's length is less than 1.0. But *only* if there's a noticable
2441 gap between the previous line and the next moveTo. (I've come
2442 across a PDF where thousands of "dots" were stringed together,
2444 int last_short_gap = short_gap;
2445 if((fabs(line->x - x) + fabs(line->y - y)) < 1.0) {
2450 if(isline && dist < 1 && !short_gap && !last_short_gap) {
2455 } else if(line->type == gfx_lineTo) {
2456 dist += fabs(line->x - x) + fabs(line->y - y);
2458 } else if(line->type == gfx_splineTo) {
2459 dist += fabs(line->sx - x) + fabs(line->sy - y) +
2460 fabs(line->x - line->sx) + fabs(line->y - line->sy);
2467 if(isline && dist < 1 && !short_gap) {
2473 static int gfxline_fix_short_edges(gfxline_t*line)
2477 if(line->type == gfx_lineTo) {
2478 if(fabs(line->x - x) + fabs(line->y - y) < 0.01) {
2481 } else if(line->type == gfx_splineTo) {
2482 if(fabs(line->sx - x) + fabs(line->sy - y) +
2483 fabs(line->x - line->sx) + fabs(line->y - line->sy) < 0.01) {
2494 static char is_inside_page(gfxdevice_t*dev, gfxcoord_t x, gfxcoord_t y)
2496 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
2497 if(x<i->min_x || x>i->max_x) return 0;
2498 if(y<i->min_y || y>i->max_y) return 0;
2502 gfxline_t* gfxline_move(gfxline_t*line, double x, double y)
2504 gfxline_t*l = line = gfxline_clone(line);
2516 //#define NORMALIZE_POLYGON_POSITIONS
2518 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)
2520 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
2521 if(line_is_empty(line))
2523 int type = gfxline_type(line);
2524 int has_dots = gfxline_has_dots(line);
2525 gfxbbox_t r = gfxline_getbbox(line);
2526 int is_outside_page = !is_inside_page(dev, r.xmin, r.ymin) || !is_inside_page(dev, r.xmax, r.ymax);
2528 /* TODO: * split line into segments, and perform this check for all segments */
2530 if(i->config_disable_polygon_conversion || /*type>=5 ||*/
2532 (width <= i->config_caplinewidth
2533 || (cap_style == gfx_capRound && joint_style == gfx_joinRound)
2534 || (cap_style == gfx_capRound && type<=2))))
2538 /* convert line to polygon */
2539 msg("<trace> draw as polygon, type=%d dots=%d", type, has_dots);
2541 gfxline_fix_short_edges(line);
2542 /* we need to convert the line into a polygon */
2543 gfxpoly_t* poly = gfxpoly_strokeToPoly(line, width, cap_style, joint_style, miterLimit);
2544 gfxline_t*gfxline = gfxpoly_to_gfxline(poly);
2545 dev->fill(dev, gfxline, color);
2546 gfxline_free(gfxline);
2551 msg("<trace> draw as stroke, type=%d dots=%d", type, has_dots);
2554 if(i->config_normalize_polygon_positions) {
2556 double startx = 0, starty = 0;
2557 if(line && line->type == gfx_moveTo) {
2561 line = gfxline_move(line, -startx, -starty);
2562 i->shapeposx = (int)(startx*20);
2563 i->shapeposy = (int)(starty*20);
2566 swfoutput_setstrokecolor(dev, color->r, color->g, color->b, color->a);
2567 swfoutput_setlinewidth(dev, width);
2570 drawgfxline(dev, line, 0);
2572 if(i->config_normalize_polygon_positions) {
2573 free(line); //account for _move
2578 static void swf_fill(gfxdevice_t*dev, gfxline_t*line, gfxcolor_t*color)
2580 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
2581 if(line_is_empty(line))
2585 gfxbbox_t r = gfxline_getbbox(line);
2586 int is_outside_page = !is_inside_page(dev, r.xmin, r.ymin) || !is_inside_page(dev, r.xmax, r.ymax);
2588 //if(i->chardatapos && i->chardata[i->chardatapos-1].color.a) {
2591 if(!i->config_ignoredraworder)
2594 if(i->config_normalize_polygon_positions) {
2596 double startx = 0, starty = 0;
2597 if(line && line->type == gfx_moveTo) {
2601 line = gfxline_move(line, -startx, -starty);
2602 i->shapeposx = (int)(startx*20);
2603 i->shapeposy = (int)(starty*20);
2606 swfoutput_setfillcolor(dev, color->r, color->g, color->b, color->a);
2609 drawgfxline(dev, line, 1);
2611 if(i->currentswfid==2 && r.xmin==0 && r.ymin==0 && r.xmax==i->max_x && r.ymax==i->max_y) {
2612 if(i->config_watermark) {
2613 draw_watermark(dev, r, 1);
2617 msg("<trace> end of swf_fill (shapeid=%d)", i->shapeid);
2619 if(i->config_normalize_polygon_positions) {
2620 free(line); //account for _move
2624 static GRADIENT* gfxgradient_to_GRADIENT(gfxgradient_t*gradient)
2627 gfxgradient_t*g = gradient;
2632 GRADIENT* swfgradient = malloc(sizeof(GRADIENT));
2633 swfgradient->num = num;
2634 swfgradient->rgba = malloc(sizeof(swfgradient->rgba[0])*num);
2635 swfgradient->ratios = malloc(sizeof(swfgradient->ratios[0])*num);
2640 swfgradient->ratios[num] = g->pos*255;
2641 swfgradient->rgba[num] = *(RGBA*)&g->color;
2648 static void swf_fillgradient(gfxdevice_t*dev, gfxline_t*line, gfxgradient_t*gradient, gfxgradienttype_t type, gfxmatrix_t*matrix)
2650 if(line_is_empty(line))
2652 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
2654 if(line_is_empty(line))
2657 GRADIENT* swfgradient = gfxgradient_to_GRADIENT(gradient);
2664 double f = type==gfxgradient_radial?4:4;
2666 m.sx = (int)(matrix->m00*20*f); m.r1 = (int)(matrix->m10*20*f);
2667 m.r0 = (int)(matrix->m01*20*f); m.sy = (int)(matrix->m11*20*f);
2668 m.tx = (int)(matrix->tx*20);
2669 m.ty = (int)(matrix->ty*20);
2672 int myshapeid = getNewID(dev);
2673 i->tag = swf_InsertTag(i->tag, ST_DEFINESHAPE2);
2675 swf_ShapeNew(&shape);
2676 int fsid = swf_ShapeAddGradientFillStyle(shape,&m,swfgradient,type==gfxgradient_radial);
2677 swf_SetU16(i->tag, myshapeid);
2678 SRECT r = gfxline_getSWFbbox(line);
2679 r = swf_ClipRect(i->pagebbox, r);
2680 swf_SetRect(i->tag,&r);
2681 swf_SetShapeStyles(i->tag,shape);
2682 swf_ShapeCountBits(shape,NULL,NULL);
2683 swf_SetShapeBits(i->tag,shape);
2684 swf_ShapeSetAll(i->tag,shape,UNDEFINED_COORD,UNDEFINED_COORD,0,fsid,0);
2685 i->swflastx = i->swflasty = UNDEFINED_COORD;
2686 drawgfxline(dev, line, 1);
2687 swf_ShapeSetEnd(i->tag);
2688 swf_ShapeFree(shape);
2690 int depth = getNewDepth(dev);
2691 msg("<trace> Placing gradient, shape ID %d, depth %d", myshapeid, depth);
2692 i->tag = swf_InsertTag(i->tag,ST_PLACEOBJECT2);
2693 swf_ObjectPlace(i->tag,myshapeid,depth,&i->page_matrix,NULL,NULL);
2695 swf_FreeGradient(swfgradient);free(swfgradient);
2698 static SWFFONT* gfxfont_to_swffont(gfxfont_t*font, const char* id)
2700 SWFFONT*swffont = (SWFFONT*)rfx_calloc(sizeof(SWFFONT));
2702 SRECT bounds = {0,0,0,0};
2704 swffont->version = 2;
2705 swffont->name = (U8*)strdup(id);
2706 swffont->layout = (SWFLAYOUT*)rfx_calloc(sizeof(SWFLAYOUT));
2707 swffont->layout->ascent = 0;
2708 swffont->layout->descent = 0;
2709 swffont->layout->leading = 0;
2710 swffont->layout->bounds = (SRECT*)rfx_calloc(sizeof(SRECT)*font->num_glyphs);
2711 swffont->encoding = FONT_ENCODING_UNICODE;
2712 swffont->numchars = font->num_glyphs;
2713 swffont->maxascii = font->max_unicode;
2714 swffont->ascii2glyph = (int*)rfx_calloc(sizeof(int)*swffont->maxascii);
2715 swffont->glyph2ascii = (U16*)rfx_calloc(sizeof(U16)*swffont->numchars);
2716 swffont->glyph = (SWFGLYPH*)rfx_calloc(sizeof(SWFGLYPH)*swffont->numchars);
2717 swffont->glyphnames = (char**)rfx_calloc(sizeof(char*)*swffont->numchars);
2718 for(t=0;t<font->max_unicode;t++) {
2719 swffont->ascii2glyph[t] = font->unicode2glyph[t];
2721 SRECT max = {0,0,0,0};
2722 for(t=0;t<font->num_glyphs;t++) {
2726 swffont->glyph2ascii[t] = font->glyphs[t].unicode;
2727 if(swffont->glyph2ascii[t] == 0xffff || swffont->glyph2ascii[t] == 0x0000) {
2728 /* flash 8 flashtype requires unique unicode IDs for each character.
2729 We use the Unicode private user area to assign characters, hoping that
2730 the font doesn't contain more than 2048 glyphs */
2731 swffont->glyph2ascii[t] = 0xe000 + (t&0x1fff);
2734 if(font->glyphs[t].name) {
2735 swffont->glyphnames[t] = strdup(font->glyphs[t].name);
2737 swffont->glyphnames[t] = 0;
2739 advance = font->glyphs[t].advance;
2741 swf_Shape01DrawerInit(&draw, 0);
2742 line = font->glyphs[t].line;
2745 c.x = line->sx * GLYPH_SCALE; c.y = line->sy * GLYPH_SCALE;
2746 to.x = line->x * GLYPH_SCALE; to.y = line->y * GLYPH_SCALE;
2747 if(line->type == gfx_moveTo) {
2748 draw.moveTo(&draw, &to);
2749 } else if(line->type == gfx_lineTo) {
2750 draw.lineTo(&draw, &to);
2751 } else if(line->type == gfx_splineTo) {
2752 draw.splineTo(&draw, &c, &to);
2757 swffont->glyph[t].shape = swf_ShapeDrawerToShape(&draw);
2759 SRECT bbox = swf_ShapeDrawerGetBBox(&draw);
2760 swf_ExpandRect2(&max, &bbox);
2762 swffont->layout->bounds[t] = bbox;
2764 if(advance<32768.0/20) {
2765 swffont->glyph[t].advance = (int)(advance*20);
2767 //msg("<warning> Advance value overflow in glyph %d", t);
2768 swffont->glyph[t].advance = 32767;
2771 draw.dealloc(&draw);
2773 swf_ExpandRect2(&bounds, &swffont->layout->bounds[t]);
2775 for(t=0;t<font->num_glyphs;t++) {
2776 SRECT bbox = swffont->layout->bounds[t];
2778 /* if the glyph doesn't have a bounding box, use the
2779 combined bounding box (necessary e.g. for space characters) */
2780 if(!(bbox.xmin|bbox.ymin|bbox.xmax|bbox.ymax)) {
2781 swffont->layout->bounds[t] = bbox = max;
2784 /* check that the advance value is reasonable, by comparing it
2785 with the bounding box */
2786 if(bbox.xmax>0 && (bbox.xmax*10 < swffont->glyph[t].advance || !swffont->glyph[t].advance)) {
2787 if(swffont->glyph[t].advance)
2788 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);
2789 swffont->glyph[t].advance = bbox.xmax;
2791 //swffont->glyph[t].advance = bbox.xmax - bbox.xmin;
2795 /* Flash player will use the advance value from the char, and the ascent/descent values
2796 from the layout for text selection.
2797 ascent will extend the char into negative y direction, from the baseline, while descent
2798 will extend in positive y direction, also from the baseline.
2799 The baseline is defined as the y-position zero
2802 swffont->layout->ascent = -bounds.ymin;
2803 if(swffont->layout->ascent < 0)
2804 swffont->layout->ascent = 0;
2805 swffont->layout->descent = bounds.ymax;
2806 if(swffont->layout->descent < 0)
2807 swffont->layout->descent = 0;
2808 swffont->layout->leading = bounds.ymax - bounds.ymin;
2810 /* if the font has proper ascent/descent values (>0) and those define
2811 greater line spacing that what we estimated from the bounding boxes,
2812 use the font's parameters */
2813 if(font->ascent*20 > swffont->layout->ascent)
2814 swffont->layout->ascent = font->ascent*20;
2815 if(font->descent*20 > swffont->layout->descent)
2816 swffont->layout->descent = font->descent*20;
2821 static void swf_addfont(gfxdevice_t*dev, gfxfont_t*font)
2823 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
2825 if(i->swffont && i->swffont->name && !strcmp((char*)i->swffont->name,font->id))
2826 return; // the requested font is the current font
2828 fontlist_t*last=0,*l = i->fontlist;
2831 if(!strcmp((char*)l->swffont->name, font->id)) {
2832 return; // we already know this font
2836 l = (fontlist_t*)rfx_calloc(sizeof(fontlist_t));
2837 l->swffont = gfxfont_to_swffont(font, font->id);
2844 swf_FontSetID(l->swffont, getNewID(i->dev));
2846 if(getScreenLogLevel() >= LOGLEVEL_DEBUG) {
2848 // print font information
2849 msg("<debug> Font %s",font->id);
2850 msg("<debug> | ID: %d", l->swffont->id);
2851 msg("<debug> | Version: %d", l->swffont->version);
2852 msg("<debug> | Name: %s", l->swffont->name);
2853 msg("<debug> | Numchars: %d", l->swffont->numchars);
2854 msg("<debug> | Maxascii: %d", l->swffont->maxascii);
2855 msg("<debug> | Style: %d", l->swffont->style);
2856 msg("<debug> | Encoding: %d", l->swffont->encoding);
2857 for(iii=0; iii<l->swffont->numchars;iii++) {
2858 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,
2859 l->swffont->layout->bounds[iii].xmin/20.0,
2860 l->swffont->layout->bounds[iii].ymin/20.0,
2861 l->swffont->layout->bounds[iii].xmax/20.0,
2862 l->swffont->layout->bounds[iii].ymax/20.0
2865 for(t=0;t<l->swffont->maxascii;t++) {
2866 if(l->swffont->ascii2glyph[t] == iii)
2867 msg("<debug> | - maps to %d",t);
2873 static void swf_switchfont(gfxdevice_t*dev, const char*fontid)
2875 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
2877 if(i->swffont && i->swffont->name && !strcmp((char*)i->swffont->name,fontid))
2878 return; // the requested font is the current font
2880 fontlist_t*l = i->fontlist;
2882 if(!strcmp((char*)l->swffont->name, fontid)) {
2883 i->swffont = l->swffont;
2888 msg("<error> Unknown font id: %s", fontid);
2892 static void swf_drawchar(gfxdevice_t*dev, gfxfont_t*font, int glyph, gfxcolor_t*color, gfxmatrix_t*matrix)
2894 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
2896 msg("<error> swf_drawchar called (glyph %d) without font", glyph);
2900 if(i->config_drawonlyshapes) {
2901 gfxglyph_t*g = &font->glyphs[glyph];
2902 gfxline_t*line2 = gfxline_clone(g->line);
2903 gfxline_transform(line2, matrix);
2904 dev->fill(dev, line2, color);
2905 gfxline_free(line2);
2909 if(!i->swffont || !i->swffont->name || strcmp((char*)i->swffont->name,font->id)) // not equal to current font
2911 /* TODO: remove the need for this (enhance getcharacterbbox so that it can cope
2912 with multiple fonts */
2914 swf_switchfont(dev, font->id); // set the current font
2917 msg("<warning> swf_drawchar: Font is NULL");
2920 if(glyph<0 || glyph>=i->swffont->numchars) {
2921 msg("<warning> No character %d in font %s (%d chars)", glyph, FIXNULL((char*)i->swffont->name), i->swffont->numchars);
2925 setfontscale(dev, matrix->m00, matrix->m01, matrix->m10, matrix->m11, matrix->tx, matrix->ty, 0);
2927 double det = i->fontmatrix.sx/65536.0 * i->fontmatrix.sy/65536.0 -
2928 i->fontmatrix.r0/65536.0 * i->fontmatrix.r1/65536.0;
2929 if(fabs(det) < 0.0005) {
2930 /* x direction equals y direction- the text is invisible */
2931 msg("<verbose> Not drawing invisible character character %d (det=%f, m=[%f %f;%f %f]\n", glyph,
2933 i->fontmatrix.sx/65536.0, i->fontmatrix.r1/65536.0,
2934 i->fontmatrix.r0/65536.0, i->fontmatrix.sy/65536.0);
2938 /*if(i->swffont->glyph[glyph].shape->bitlen <= 16) {
2939 msg("<warning> Glyph %d in current charset (%s, %d characters) is empty",
2940 glyph, FIXNULL((char*)i->swffont->name), i->swffont->numchars);
2944 /* calculate character position with respect to the current font matrix */
2945 double s = 20 * GLYPH_SCALE / det;
2946 double px = matrix->tx - i->fontmatrix.tx/20.0;
2947 double py = matrix->ty - i->fontmatrix.ty/20.0;
2948 int x = (SCOORD)(( px * i->fontmatrix.sy/65536.0 - py * i->fontmatrix.r1/65536.0)*s);
2949 int y = (SCOORD)((- px * i->fontmatrix.r0/65536.0 + py * i->fontmatrix.sx/65536.0)*s);
2950 if(x>32767 || x<-32768 || y>32767 || y<-32768) {
2951 msg("<verbose> Moving character origin to %f %f\n", matrix->tx, matrix->ty);
2953 setfontscale(dev, matrix->m00, matrix->m01, matrix->m10, matrix->m11, matrix->tx, matrix->ty, 1);
2954 /* since we just moved the char origin to the current char's position,
2955 it now has the relative position (0,0) */
2964 msg("<trace> Drawing char %d in font %d at %d,%d in color %02x%02x%02x%02x",
2965 glyph, i->swffont->id, x, y, color->r, color->g, color->b, color->a);
2967 putcharacter(dev, i->swffont->id, glyph, x, y, i->current_font_size, *(RGBA*)color);
2968 swf_FontUseGlyph(i->swffont, glyph);