3 Part of the swftools package.
5 Copyright (c) 2001,2002,2003,2004,2005 Matthias Kramm <kramm@quiss.org>
7 Swftools is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 2 of the License, or
10 (at your option) any later version.
12 Swftools is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
17 You should have received a copy of the GNU General Public License
18 along with swftools; if not, write to the Free Software
19 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
24 #include "../../config.h"
40 #include "../rfxswf.h"
41 #include "../gfxdevice.h"
42 #include "../gfxtools.h"
44 #include "../gfxpoly.h"
47 #define CHARDATAMAX 1024
51 typedef struct _charatposition {
60 typedef struct _chararray {
61 charatposition_t chr[CHARDATAMAX+1];
63 struct _chararray *next;
66 typedef struct _charbuffer {
70 struct _charbuffer *next;
73 typedef struct _fontlist
76 struct _fontlist*next;
79 typedef long int twip;
81 typedef struct _swfmatrix {
82 double m11,m12,m21,m22,m31,m32;
85 typedef struct _swfoutput_internal
87 gfxdevice_t*dev; // the gfxdevice object where this internal struct resides
89 double config_dumpfonts;
90 double config_ppmsubpixels;
91 double config_jpegsubpixels;
93 int config_invisibletexttofront;
95 int config_simpleviewer;
96 int config_opennewwindow;
97 int config_ignoredraworder;
98 int config_drawonlyshapes;
99 int config_frameresets;
100 int config_linknameurl;
101 int config_jpegquality;
102 int config_storeallcharacters;
103 int config_enablezlib;
104 int config_insertstoptag;
105 int config_watermark;
107 int config_flashversion;
108 int config_reordertags;
109 int config_showclipshapes;
110 int config_splinemaxerror;
111 int config_fontsplinemaxerror;
112 int config_filloverlap;
115 int config_disable_polygon_conversion;
116 int config_normalize_polygon_positions;
117 char config_disablelinks;
118 RGBA config_linkcolor;
119 float config_minlinewidth;
120 double config_caplinewidth;
121 char* config_linktarget;
122 char*config_internallinkfunction;
123 char*config_externallinkfunction;
125 double config_framerate;
129 fontlist_t* fontlist;
168 int pic_height[1024];
175 char fillstylechanged;
177 int jpeg; //next image type
184 charbuffer_t* chardata;
185 charbuffer_t* topchardata; //chars supposed to be above everything else
192 int current_font_size;
194 double lastfontm11,lastfontm12,lastfontm21,lastfontm22;
205 } swfoutput_internal;
207 static void swf_fillbitmap(gfxdevice_t*driver, gfxline_t*line, gfximage_t*img, gfxmatrix_t*move, gfxcxform_t*cxform);
208 static int swf_setparameter(gfxdevice_t*driver, const char*key, const char*value);
209 static void swf_drawstroke(gfxdevice_t*dev, gfxline_t*line, gfxcoord_t width, gfxcolor_t*color, gfx_capType cap_style, gfx_joinType joint_style, gfxcoord_t miterLimit);
210 static void swf_startclip(gfxdevice_t*dev, gfxline_t*line);
211 static void swf_endclip(gfxdevice_t*dev);
212 static void swf_stroke(gfxdevice_t*dev, gfxline_t*line, gfxcoord_t width, gfxcolor_t*color, gfx_capType cap_style, gfx_joinType joint_style, gfxcoord_t miterLimit);
213 static void swf_fill(gfxdevice_t*dev, gfxline_t*line, gfxcolor_t*color);
214 static void swf_fillbitmap(gfxdevice_t*dev, gfxline_t*line, gfximage_t*img, gfxmatrix_t*matrix, gfxcxform_t*cxform);
215 static void swf_fillgradient(gfxdevice_t*dev, gfxline_t*line, gfxgradient_t*gradient, gfxgradienttype_t type, gfxmatrix_t*matrix);
216 static void swf_drawchar(gfxdevice_t*dev, gfxfont_t*font, int glyph, gfxcolor_t*color, gfxmatrix_t*matrix);
217 static void swf_addfont(gfxdevice_t*dev, gfxfont_t*font);
218 static void swf_drawlink(gfxdevice_t*dev, gfxline_t*line, const char*action);
219 static void swf_startframe(gfxdevice_t*dev, int width, int height);
220 static void swf_endframe(gfxdevice_t*dev);
221 static gfxresult_t* swf_finish(gfxdevice_t*driver);
223 static swfoutput_internal* init_internal_struct()
225 swfoutput_internal*i = (swfoutput_internal*)malloc(sizeof(swfoutput_internal));
226 memset(i, 0, sizeof(swfoutput_internal));
250 i->fillstylechanged = 0;
257 i->config_disablelinks=0;
258 i->config_dumpfonts=0;
259 i->config_ppmsubpixels=0;
260 i->config_jpegsubpixels=0;
261 i->config_opennewwindow=1;
262 i->config_ignoredraworder=0;
263 i->config_drawonlyshapes=0;
264 i->config_jpegquality=85;
265 i->config_storeallcharacters=0;
267 i->config_enablezlib=0;
268 i->config_insertstoptag=0;
269 i->config_flashversion=6;
270 i->config_framerate=0.25;
271 i->config_splinemaxerror=1;
272 i->config_fontsplinemaxerror=1;
273 i->config_filloverlap=0;
275 i->config_bboxvars=0;
276 i->config_showclipshapes=0;
277 i->config_minlinewidth=0.05;
278 i->config_caplinewidth=1;
279 i->config_linktarget=0;
280 i->config_internallinkfunction=0;
281 i->config_externallinkfunction=0;
282 i->config_reordertags=1;
283 i->config_linknameurl=0;
285 i->config_linkcolor.r = i->config_linkcolor.g = i->config_linkcolor.b = 255;
286 i->config_linkcolor.a = 0x40;
291 static int id_error = 0;
293 static U16 getNewID(gfxdevice_t* dev)
295 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
296 if(i->currentswfid == 65535) {
298 msg("<error> ID Table overflow");
299 msg("<error> This file is too complex to render- SWF only supports 65536 shapes at once");
305 return ++i->currentswfid;
307 static U16 getNewDepth(gfxdevice_t* dev)
309 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
310 if(i->depth == 65520) {
312 msg("<error> Depth Table overflow");
313 msg("<error> This file is too complex to render- SWF only supports 65536 shapes at once");
322 static void startshape(gfxdevice_t* dev);
323 static void starttext(gfxdevice_t* dev);
324 static void endshape(gfxdevice_t* dev);
325 static void endtext(gfxdevice_t* dev);
327 typedef struct _plotxy
332 static inline int twipsnap(double f)
334 /* if(f < -0x40000000/20.0) {
335 fprintf(stderr, "Warning: Coordinate underflow (%f)\n", f);
336 f = -0x40000000/20.0;
337 } else if(f>0x3fffffff/20.0) {
338 fprintf(stderr, "Warning: Coordinate overflow (%f)\n", f);
342 /* clamp coordinates to a rectangle with the property that we
343 can represent a line from the upper left corner to the upper
344 right corner using no more than 64 strokes */
345 const double min = -(1<<(18+4))/20.0;
346 const double max = ((1<<(18+4))-1)/20.0;
348 fprintf(stderr, "Warning: Coordinate underflow (%f)\n", f);
351 fprintf(stderr, "Warning: Coordinate overflow (%f)\n", f);
358 // write a move-to command into the swf
359 static int movetoxy(gfxdevice_t*dev, TAG*tag, plotxy_t p0)
361 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
362 int rx = twipsnap(p0.x);
363 int ry = twipsnap(p0.y);
364 if(rx!=i->swflastx || ry!=i->swflasty || i->fillstylechanged) {
365 swf_ShapeSetMove (tag, i->shape, rx,ry);
366 i->fillstylechanged = 0;
373 static int moveto(gfxdevice_t*dev, TAG*tag, double x, double y)
375 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
379 return movetoxy(dev, tag, p);
381 static void addPointToBBox(gfxdevice_t*dev, int px, int py)
383 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
389 swf_ExpandRect(&i->bboxrect, p);
391 swf_ExpandRect3(&i->bboxrect, p, i->linewidth*3/2);
395 /*static void plot(gfxdevice_t*dev, int x, int y, TAG*tag)
397 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
398 int width = i->linewidth/4;
402 //swf_ShapeSetLine(tag, i->shape,-width,-width);
403 //swf_ShapeSetLine(tag, i->shape,width*2,0);
404 //swf_ShapeSetLine(tag, i->shape,0,width*2);
405 //swf_ShapeSetLine(tag, i->shape,-width*2,0);
406 //swf_ShapeSetLine(tag, i->shape,0,-width*2);
407 //swf_ShapeSetLine(tag, i->shape,width,width);
410 swf_ShapeSetLine(tag, i->shape,-width,0);
411 swf_ShapeSetLine(tag, i->shape,width,-width);
412 swf_ShapeSetLine(tag, i->shape,width,width);
413 swf_ShapeSetLine(tag, i->shape,-width,width);
414 swf_ShapeSetLine(tag, i->shape,-width,-width);
415 swf_ShapeSetLine(tag, i->shape,width,0);
417 addPointToBBox(dev, x-width ,y-width);
418 addPointToBBox(dev, x+width ,y+width);
421 // write a line-to command into the swf
422 static void linetoxy(gfxdevice_t*dev, TAG*tag, plotxy_t p0)
424 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
425 int px = twipsnap(p0.x);
426 int py = twipsnap(p0.y);
427 int rx = (px-i->swflastx);
428 int ry = (py-i->swflasty);
430 swf_ShapeSetLine (tag, i->shape, rx,ry);
431 addPointToBBox(dev, i->swflastx,i->swflasty);
432 addPointToBBox(dev, px,py);
433 } /* this is a nice idea, but doesn't work with current flash
434 players (the pixel will be invisible if they're not
435 precisely on a pixel boundary)
436 Besides, we should only do this if this lineto itself
437 is again followed by a "move".
438 else if(!i->fill && i->config_dots) {
439 // treat lines of length 0 as plots, making them
440 // at least 1 twip wide so Flash will display them
441 //plot(dev, i->swflastx, i->swflasty, tag);
442 swf_ShapeSetLine (tag, i->shape, rx+1,ry);
449 static void lineto(gfxdevice_t*dev, TAG*tag, double x, double y)
454 linetoxy(dev,tag, p);
457 // write a spline-to command into the swf
458 static void splineto(gfxdevice_t*dev, TAG*tag, plotxy_t control,plotxy_t end)
460 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
461 int lastlastx = i->swflastx;
462 int lastlasty = i->swflasty;
464 int cx = (twipsnap(control.x)-i->swflastx);
465 int cy = (twipsnap(control.y)-i->swflasty);
468 int ex = (twipsnap(end.x)-i->swflastx);
469 int ey = (twipsnap(end.y)-i->swflasty);
473 if((cx || cy) && (ex || ey)) {
474 swf_ShapeSetCurve(tag, i->shape, cx,cy,ex,ey);
475 addPointToBBox(dev, lastlastx ,lastlasty );
476 addPointToBBox(dev, lastlastx+cx,lastlasty+cy);
477 addPointToBBox(dev, lastlastx+cx+ex,lastlasty+cy+ey);
478 } else if(cx || cy || ex || ey) {
479 swf_ShapeSetLine(tag, i->shape, cx+ex,cy+ey);
480 addPointToBBox(dev, lastlastx ,lastlasty );
481 addPointToBBox(dev, lastlastx+cx,lastlasty+cy);
482 addPointToBBox(dev, lastlastx+cx+ex,lastlasty+cy+ey);
488 /* write a line, given two points and the transformation
490 /*static void line(gfxdevice_t*dev, TAG*tag, plotxy_t p0, plotxy_t p1)
492 moveto(dev, tag, p0);
493 lineto(dev, tag, p1);
496 void resetdrawer(gfxdevice_t*dev)
498 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
503 static void stopFill(gfxdevice_t*dev)
505 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
506 if(i->lastwasfill!=0)
508 swf_ShapeSetStyle(i->tag,i->shape,i->linestyleid,0x8000,0);
509 i->fillstylechanged = 1;
513 static void startFill(gfxdevice_t*dev)
515 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
516 if(i->lastwasfill!=1)
518 swf_ShapeSetStyle(i->tag,i->shape,0x8000,i->fillstyleid,0);
519 i->fillstylechanged = 1;
524 static inline int colorcompare(RGBA*a,RGBA*b)
536 static SRECT getcharacterbbox(chararray_t*chardata, MATRIX* m)
540 memset(&r, 0, sizeof(r));
543 if(debug) printf("\n");
546 for(t=0;t<chardata->pos;t++) {
547 charatposition_t*chr = &chardata->chr[t];
548 SRECT b = chr->font->layout->bounds[chr->charid];
554 /* divide by 1024, rounding xmax/ymax up */
555 b.xmax += 1023; b.ymax += 1023; b.xmin /= 1024; b.ymin /= 1024; b.xmax /= 1024; b.ymax /= 1024;
562 /* until we solve the INTERNAL_SCALING problem (see below)
563 make sure the bounding box is big enough */
569 b = swf_TurnRect(b, m);
571 if(debug) printf("(%f,%f,%f,%f) -> (%f,%f,%f,%f) [font %d, char %d]\n",
572 chr->font->layout->bounds[chr->charid].xmin/20.0,
573 chr->font->layout->bounds[chr->charid].ymin/20.0,
574 chr->font->layout->bounds[chr->charid].xmax/20.0,
575 chr->font->layout->bounds[chr->charid].ymax/20.0,
582 swf_ExpandRect2(&r, &b);
584 chardata = chardata->next;
586 if(debug) printf("-----> (%f,%f,%f,%f)\n",
594 static chararray_t*chararray_reverse(chararray_t*buf)
596 chararray_t*prev = 0;
598 chararray_t*next = buf->next;
606 static void chararray_writetotag(chararray_t*_chardata, TAG*tag)
610 color.r = _chardata?_chardata->chr[0].color.r^255:0;
619 int charadvance[128];
622 int glyphbits=1; //TODO: can this be zero?
625 if(tag->id != ST_DEFINETEXT &&
626 tag->id != ST_DEFINETEXT2) {
627 msg("<error> internal error: charbuffer_put needs an text tag, not %d\n",tag->id);
631 msg("<warning> charbuffer_put called with zero characters");
634 for(pass = 0; pass < 2; pass++)
644 advancebits++; // add sign bit
645 swf_SetU8(tag, glyphbits);
646 swf_SetU8(tag, advancebits);
649 chararray_t*chardata = _chardata;
654 assert(!chardata->next || chardata->pos == CHARDATAMAX);
655 assert(chardata->pos);
657 int to = chardata->next?chardata->pos-1:chardata->pos;
661 char islast = t==chardata->pos;
663 charatposition_t*chr = &chardata->chr[t];
665 if(lastfont != chardata->chr[t].font ||
666 lastx!=chardata->chr[t].x ||
667 lasty!=chardata->chr[t].y ||
668 !colorcompare(&color, &chardata->chr[t].color) ||
670 lastsize != chardata->chr[t].size ||
673 if(charstorepos && pass==0)
676 for(s=0;s<charstorepos;s++)
678 while(charids[s]>=(1<<glyphbits))
680 while(charadvance[s]>=(1<<advancebits))
684 if(charstorepos && pass==1)
686 tag->writeBit = 0; // Q&D
687 swf_SetBits(tag, 0, 1); // GLYPH Record
688 swf_SetBits(tag, charstorepos, 7); // number of glyphs
690 for(s=0;s<charstorepos;s++)
692 swf_SetBits(tag, charids[s], glyphbits);
693 swf_SetBits(tag, charadvance[s], advancebits);
698 if(pass == 1 && !islast)
704 if(lastx != chr->x ||
714 if(!colorcompare(&color, &chr->color))
719 font.id = chr->font->id;
720 if(lastfont != chr->font || lastsize != chr->size)
723 tag->writeBit = 0; // Q&D
724 swf_TextSetInfoRecord(tag, newfont, chr->size, newcolor, newx,newy);
727 lastfont = chr->font;
730 lastsize = chr->size;
737 if(t<chardata->pos-1) nextx = chardata->chr[t+1].x;
738 if(t==chardata->pos-1 && chardata->next) nextx = chardata->next->chr[0].x;
739 int dx = nextx-chr->x;
742 if(dx>=0 && (dx<(1<<(advancebits-1)) || pass==0)) {
749 charids[charstorepos] = chr->charid;
750 charadvance[charstorepos] = advance;
753 chardata = chardata->next;
758 static void chararray_destroy(chararray_t*chr)
761 chararray_t*next = chr->next;
768 static inline int matrix_diff(MATRIX*m1, MATRIX*m2)
770 return memcmp(m1,m2,sizeof(MATRIX));
772 static charbuffer_t*charbuffer_append(charbuffer_t*buf, SWFFONT*font, int charid, int x,int y, int size, RGBA color, MATRIX*m)
774 if(!buf || matrix_diff(&buf->matrix,m)) {
775 charbuffer_t*n = rfx_calloc(sizeof(charbuffer_t));
780 if(!buf->last || buf->last->pos == CHARDATAMAX) {
781 chararray_t*n = rfx_calloc(sizeof(chararray_t));
783 buf->array = buf->last = n;
789 chararray_t*a = buf->last;
790 a->chr[a->pos].font = font;
791 a->chr[a->pos].charid = charid;
792 a->chr[a->pos].x = x;
793 a->chr[a->pos].y = y;
794 a->chr[a->pos].color = color;
795 a->chr[a->pos].size = size;
800 /* Notice: we can only put chars in the range -1639,1638 (-32768/20,32768/20).
801 So if we set this value to high, the char coordinates will overflow.
802 If we set it to low, however, the char positions will be inaccurate */
803 #define GLYPH_SCALE 1
805 static void chararray_writetodev(gfxdevice_t*dev, chararray_t*array, MATRIX*matrix)
807 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
809 int textid = getNewID(dev);
810 i->tag = swf_InsertTag(i->tag,ST_DEFINETEXT2);
811 swf_SetU16(i->tag, textid);
813 r = getcharacterbbox(array, matrix);
814 r = swf_ClipRect(i->pagebbox, r);
815 swf_SetRect(i->tag,&r);
816 swf_SetMatrix(i->tag, matrix);
817 msg("<trace> Placing text as ID %d", textid);
818 chararray_writetotag(array, i->tag);
823 if(i->swf->fileVersion >= 8) {
824 i->tag = swf_InsertTag(i->tag, ST_CSMTEXTSETTINGS);
825 swf_SetU16(i->tag, textid);
827 //swf_SetU8(i->tag, /*subpixel grid*/(2<<3)|/*flashtype*/0x40);
828 swf_SetU8(i->tag, /*grid*/(1<<3)|/*flashtype*/0x40);
829 //swf_SetU8(i->tag, /*no grid*/(0<<3)|/*flashtype*/0x40);
831 swf_SetU32(i->tag, 0);//thickness
832 swf_SetU32(i->tag, 0);//sharpness
833 swf_SetU8(i->tag, 0);//reserved
835 i->tag = swf_InsertTag(i->tag,ST_PLACEOBJECT2);
836 swf_ObjectPlace(i->tag,textid,getNewDepth(dev),&i->page_matrix,NULL,NULL);
839 static void charbuffer_writetodevandfree(gfxdevice_t*dev, charbuffer_t*buf)
842 charbuffer_t*next = buf->next;buf->next = 0;
843 chararray_writetodev(dev, buf->array, &buf->matrix);
844 chararray_destroy(buf->array);
850 static void endtext(gfxdevice_t*dev)
852 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
855 charbuffer_writetodevandfree(dev, i->chardata);i->chardata = 0;
859 /* sets the matrix which is to be applied to characters drawn by swfoutput_drawchar() */
860 static void setfontscale(gfxdevice_t*dev,double m11,double m12, double m21,double m22,double x, double y, char force)
866 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
867 if(i->lastfontm11 == m11 &&
868 i->lastfontm12 == m12 &&
869 i->lastfontm21 == m21 &&
870 i->lastfontm22 == m22 && !force)
875 i->lastfontm11 = m11;
876 i->lastfontm12 = m12;
877 i->lastfontm21 = m21;
878 i->lastfontm22 = m22;
880 double xsize = sqrt(m11*m11 + m12*m12);
881 double ysize = sqrt(m21*m21 + m22*m22);
882 i->current_font_size = (xsize>ysize?xsize:ysize)*1;
883 if(i->current_font_size < 1)
884 i->current_font_size = 1;
885 double ifs = 1.0 / (i->current_font_size*GLYPH_SCALE);
888 m.sx = (S32)((m11*ifs)*65536); m.r1 = (S32)((m21*ifs)*65536);
889 m.r0 = (S32)((m12*ifs)*65536); m.sy = (S32)((m22*ifs)*65536);
890 /* this is the position of the first char to set a new fontmatrix-
891 we hope that it's close enough to all other characters using the
892 font, so we use its position as origin for the matrix */
898 static int watermark2_width=47;
899 static int watermark2_height=11;
900 static int watermark2[47] = {95,1989,71,0,2015,337,1678,0,2015,5,1921,320,1938,25,2006,1024,
901 1042,21,13,960,1039,976,8,2000,1359,1088,31,1989,321,1728,0,1152,
902 1344,832,0,1984,0,896,1088,1088,896,0,1984,128,256,512,1984};
904 static void draw_watermark(gfxdevice_t*dev, gfxbbox_t r, char drawall)
906 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
907 double wx = r.xmax / 5.0;
908 double tx = r.xmax*4.0 / 5.0;
909 double ty = r.ymax-wx*watermark2_height/watermark2_width;
910 double sx = (r.xmax - tx) / watermark2_width;
911 double sy = (r.ymax - ty) / watermark2_height;
914 if(ty > 0 && px > 1.0 && py > 1.0) {
916 for(y=0;y<watermark2_height;y++)
917 for(x=0;x<watermark2_width;x++) {
918 if(((watermark2[x]>>y)&1)) {
919 if(!drawall && rand()%5)
921 unsigned int b = rand();
922 moveto(dev, i->tag, x*sx+tx+((b>>1)&1)/20.0, y*sy+ty+((b>>3)&1)/20.0);
923 lineto(dev, i->tag, x*sx+px+tx+((b>>2)&1)/20.0, y*sy+ty+((b>>3)&1)/20.0);
924 lineto(dev, i->tag, x*sx+px+tx+((b>>2)&1)/20.0, y*sy+py+ty+((b>>4)&1)/20.0);
925 lineto(dev, i->tag, x*sx+tx+((b>>1)&1)/20.0, y*sy+py+ty+((b>>4)&1)/20.0);
926 lineto(dev, i->tag, x*sx+tx+((b>>1)&1)/20.0, y*sy+ty+((b>>3)&1)/20.0);
932 static void swfoutput_setfillcolor(gfxdevice_t* dev, U8 r, U8 g, U8 b, U8 a)
934 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
935 if(i->fillrgb.r == r &&
938 i->fillrgb.a == a) return;
947 static void insert_watermark(gfxdevice_t*dev, char drawall)
949 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
950 if(!drawall && i->watermarks>20)
956 swfoutput_setfillcolor(dev, 0,0,255,192);
958 swfoutput_setfillcolor(dev, rand(),rand(),rand(),(rand()&127)+128);
963 gfxbbox_t r; r.xmin = r.ymin = 0;
966 draw_watermark(dev, r, drawall);
972 static void endpage(gfxdevice_t*dev)
974 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
982 charbuffer_writetodevandfree(dev, i->topchardata);
989 if(i->config_watermark) {
990 insert_watermark(dev, 1);
996 static void addViewer(gfxdevice_t* dev)
998 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1001 RGBA button_colors[3]= {{0xbf,0x00,0x00,0x80},{0xbf,0x20,0x20,0xc0}, {0xbf,0xc0,0xc0,0xff}};
1003 int button_sizex = 20;
1004 int button_sizey = 20;
1006 RGBA black = {255,0,0,0};
1008 i->tag = swf_InsertTag(i->tag,ST_DEFINESHAPE3);
1010 int ls1 = swf_ShapeAddLineStyle(s,40,&black);
1011 int fs1 = swf_ShapeAddSolidFillStyle(s,&button_colors[t/2]);
1012 int shapeid = ids[t] = getNewID(dev);
1013 swf_SetU16(i->tag,shapeid);
1015 r.xmin = -20*button_sizex;
1016 r.xmax = 20*button_sizex;
1018 r.ymax = 40*button_sizey;
1019 swf_SetRect(i->tag,&r); // set shape bounds
1020 swf_SetShapeHeader(i->tag,s); // write all styles to tag
1021 swf_ShapeSetAll(i->tag,s,0*button_sizex,0,ls1,fs1,0);
1022 swf_ShapeSetLine(i->tag,s,(1-(t&1)*2)*20*button_sizex,20*button_sizey);
1023 swf_ShapeSetLine(i->tag,s,-(1-(t&1)*2)*20*button_sizex,20*button_sizey);
1024 swf_ShapeSetLine(i->tag,s,0,-40*button_sizey);
1025 swf_ShapeSetEnd(i->tag); // finish drawing
1026 swf_ShapeFree(s); // clean shape structure (which isn't needed anymore after writing the tag)
1028 ActionTAG*a1=0,*a2=0,*a3=0;
1029 a1 = action_NextFrame(a1);
1030 a1 = action_Stop(a1);
1031 a1 = action_End(a1);
1033 a2 = action_PreviousFrame(a2);
1034 a2 = action_Stop(a2);
1035 a2 = action_End(a2);
1037 a3 = action_Stop(a3);
1038 a3 = action_End(a3);
1040 i->tag = swf_InsertTag(i->tag, ST_DOACTION);
1041 swf_ActionSet(i->tag,a3);
1043 i->tag = swf_InsertTag(i->tag,ST_DEFINEBUTTON);
1044 int buttonid1 = getNewID(dev);
1045 swf_SetU16(i->tag, buttonid1);
1046 swf_ButtonSetRecord(i->tag,BS_UP|BS_HIT,ids[0],1,NULL,NULL);
1047 swf_ButtonSetRecord(i->tag,BS_OVER,ids[2],1,NULL,NULL);
1048 swf_ButtonSetRecord(i->tag,BS_DOWN,ids[4],1,NULL,NULL);
1049 swf_SetU8(i->tag,0); // end of button records
1050 swf_ActionSet(i->tag,a1);
1052 i->tag = swf_InsertTag(i->tag,ST_DEFINEBUTTON);
1053 int buttonid2 = getNewID(dev);
1054 swf_SetU16(i->tag, buttonid2);
1055 swf_ButtonSetRecord(i->tag,BS_UP|BS_HIT,ids[1],1,NULL,NULL);
1056 swf_ButtonSetRecord(i->tag,BS_OVER,ids[3],1,NULL,NULL);
1057 swf_ButtonSetRecord(i->tag,BS_DOWN,ids[5],1,NULL,NULL);
1058 swf_SetU8(i->tag,0); // end of button records
1059 swf_ActionSet(i->tag,a2);
1061 i->tag = swf_InsertTag(i->tag,ST_PLACEOBJECT2);
1063 swf_GetMatrix(0, &m);
1064 m.tx = button_sizex*20+200;
1065 swf_ObjectPlace(i->tag, buttonid2, 65534,&m,0,0);
1066 i->tag = swf_InsertTag(i->tag,ST_PLACEOBJECT2);
1067 m.tx = button_sizex*20+200+200;
1068 swf_ObjectPlace(i->tag, buttonid1, 65535,&m,0,0);
1072 void swf_startframe(gfxdevice_t*dev, int width, int height)
1074 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1076 if(i->config_protect) {
1077 i->tag = swf_InsertTag(i->tag, ST_PROTECT);
1078 i->config_protect = 0;
1080 if(i->config_simpleviewer) {
1085 if(!i->firstpage && !i->pagefinished)
1088 msg("<verbose> Starting new SWF page of size %dx%d", width, height);
1090 swf_GetMatrix(0, &i->page_matrix);
1091 i->page_matrix.tx = 0;
1092 i->page_matrix.ty = 0;
1099 /* create a bbox structure with the page size. This is used
1100 for clipping shape and text bounding boxes. As we don't want to
1101 generate bounding boxes which extend beyond the movie size (in
1102 order to not confuse Flash), we clip everything against i->pagebbox */
1103 i->pagebbox.xmin = 0;
1104 i->pagebbox.ymin = 0;
1105 i->pagebbox.xmax = width*20;
1106 i->pagebbox.ymax = height*20;
1108 /* increase SWF's bounding box */
1109 swf_ExpandRect2(&i->swf->movieSize, &i->pagebbox);
1111 i->lastframeno = i->frameno;
1113 i->pagefinished = 0;
1117 void swf_endframe(gfxdevice_t*dev)
1119 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1121 if(!i->pagefinished)
1124 if( (i->swf->fileVersion <= 8) && (i->config_insertstoptag) ) {
1126 atag = action_Stop(atag);
1127 atag = action_End(atag);
1128 i->tag = swf_InsertTag(i->tag,ST_DOACTION);
1129 swf_ActionSet(i->tag,atag);
1131 i->tag = swf_InsertTag(i->tag,ST_SHOWFRAME);
1134 for(i->depth;i->depth>i->startdepth;i->depth--) {
1135 i->tag = swf_InsertTag(i->tag,ST_REMOVEOBJECT2);
1136 swf_SetU16(i->tag,i->depth);
1138 i->depth = i->startdepth;
1140 if(i->config_frameresets) {
1141 for(i->currentswfid;i->currentswfid>i->startids;i->currentswfid--) {
1142 i->tag = swf_InsertTag(i->tag,ST_FREECHARACTER);
1143 swf_SetU16(i->tag,i->currentswfid);
1145 i->currentswfid = i->startids;
1149 static void setBackground(gfxdevice_t*dev, int x1, int y1, int x2, int y2)
1151 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1153 rgb.a = rgb.r = rgb.g = rgb.b = 0xff;
1157 int shapeid = getNewID(dev);
1162 i->tag = swf_InsertTag(i->tag, ST_DEFINESHAPE);
1164 fs1 = swf_ShapeAddSolidFillStyle(s, &rgb);
1165 swf_SetU16(i->tag,shapeid);
1166 swf_SetRect(i->tag,&r);
1167 swf_SetShapeHeader(i->tag,s);
1168 swf_ShapeSetAll(i->tag,s,x1,y1,ls1,fs1,0);
1169 swf_ShapeSetLine(i->tag,s,(x2-x1),0);
1170 swf_ShapeSetLine(i->tag,s,0,(y2-y1));
1171 swf_ShapeSetLine(i->tag,s,(x1-x2),0);
1172 swf_ShapeSetLine(i->tag,s,0,(y1-y2));
1173 swf_ShapeSetEnd(i->tag);
1175 i->tag = swf_InsertTag(i->tag, ST_PLACEOBJECT2);
1176 swf_ObjectPlace(i->tag,shapeid,getNewDepth(dev),0,0,0);
1177 i->tag = swf_InsertTag(i->tag, ST_PLACEOBJECT2);
1178 swf_ObjectPlaceClip(i->tag,shapeid,getNewDepth(dev),0,0,0,65535);
1181 /* initialize the swf writer */
1182 void gfxdevice_swf_init(gfxdevice_t* dev)
1184 memset(dev, 0, sizeof(gfxdevice_t));
1188 dev->internal = init_internal_struct(); // set config to default values
1190 dev->startpage = swf_startframe;
1191 dev->endpage = swf_endframe;
1192 dev->finish = swf_finish;
1193 dev->fillbitmap = swf_fillbitmap;
1194 dev->setparameter = swf_setparameter;
1195 dev->stroke = swf_stroke;
1196 dev->startclip = swf_startclip;
1197 dev->endclip = swf_endclip;
1198 dev->fill = swf_fill;
1199 dev->fillbitmap = swf_fillbitmap;
1200 dev->fillgradient = swf_fillgradient;
1201 dev->addfont = swf_addfont;
1202 dev->drawchar = swf_drawchar;
1203 dev->drawlink = swf_drawlink;
1205 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1208 msg("<verbose> initializing swf output\n", i->max_x,i->max_y);
1212 i->swf = (SWF*)rfx_calloc(sizeof(SWF));
1213 i->swf->fileVersion = 0;
1214 i->swf->frameRate = 0x80;
1215 i->swf->movieSize.xmin = 0;
1216 i->swf->movieSize.ymin = 0;
1217 i->swf->movieSize.xmax = 0;
1218 i->swf->movieSize.ymax = 0;
1219 i->swf->fileAttributes = 9; // as3, local-with-network
1221 i->swf->firstTag = swf_InsertTag(NULL,ST_SETBACKGROUNDCOLOR);
1222 i->tag = i->swf->firstTag;
1224 rgb.a = rgb.r = rgb.g = rgb.b = 0xff;
1226 swf_SetRGB(i->tag,&rgb);
1228 i->startdepth = i->depth = 0;
1229 i->startids = i->currentswfid = 0;
1232 static void startshape(gfxdevice_t*dev)
1234 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1239 //if(i->chardatapos && i->chardata[i->chardatapos-1].color.a)
1242 i->tag = swf_InsertTag(i->tag,ST_DEFINESHAPE3);
1244 swf_ShapeNew(&i->shape);
1245 i->linestyleid = swf_ShapeAddLineStyle(i->shape,i->linewidth,&i->strokergb);
1246 i->fillstyleid = swf_ShapeAddSolidFillStyle(i->shape,&i->fillrgb);
1248 RGBA markcol = {0,i->mark[0],i->mark[1],i->mark[2]};
1249 swf_ShapeAddSolidFillStyle(i->shape,&markcol);
1252 i->shapeid = getNewID(dev);
1254 msg("<debug> Using shape id %d", i->shapeid);
1256 swf_SetU16(i->tag,i->shapeid); // ID
1258 i->bboxrectpos = i->tag->len;
1260 swf_SetRect(i->tag,&i->pagebbox);
1262 memset(&i->bboxrect, 0, sizeof(i->bboxrect));
1264 swf_SetShapeStyles(i->tag,i->shape);
1265 swf_ShapeCountBits(i->shape,NULL,NULL);
1266 swf_SetShapeBits(i->tag,i->shape);
1268 /* TODO: do we really need this? */
1269 //swf_ShapeSetAll(i->tag,i->shape,/*x*/0,/*y*/0,i->linestyleid,0,0);
1270 //swf_ShapeSetAll(i->tag,i->shape,/*x*/UNDEFINED_COORD,/*y*/UNDEFINED_COORD,i->linestyleid,0,0);
1271 i->swflastx=i->swflasty=UNDEFINED_COORD;
1272 i->lastwasfill = -1;
1273 i->shapeisempty = 1;
1276 static void starttext(gfxdevice_t*dev)
1278 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1282 if(i->config_watermark) {
1283 insert_watermark(dev, 0);
1286 i->swflastx=i->swflasty=0;
1290 /* TODO: move to ../lib/rfxswf */
1291 void changeRect(gfxdevice_t*dev, TAG*tag, int pos, SRECT*newrect)
1293 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1294 /* determine length of old rect */
1298 swf_GetRect(tag, &old);
1299 swf_ResetReadBits(tag);
1300 int pos_end = tag->pos;
1302 int len = tag->len - pos_end;
1303 U8*data = (U8*)malloc(len);
1304 memcpy(data, &tag->data[pos_end], len);
1307 swf_SetRect(tag, newrect);
1308 swf_SetBlock(tag, data, len);
1310 tag->pos = tag->readBit = 0;
1313 void cancelshape(gfxdevice_t*dev)
1315 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1316 /* delete old shape tag */
1318 i->tag = i->tag->prev;
1319 swf_DeleteTag(0, todel);
1320 if(i->shape) {swf_ShapeFree(i->shape);i->shape=0;}
1322 i->bboxrectpos = -1;
1324 // i->currentswfid--; // doesn't work, for some reason
1327 void fixAreas(gfxdevice_t*dev)
1329 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1330 if(!i->shapeisempty && i->fill &&
1331 (i->bboxrect.xmin == i->bboxrect.xmax ||
1332 i->bboxrect.ymin == i->bboxrect.ymax) &&
1333 i->config_minlinewidth >= 0.001
1335 msg("<debug> Shape has size 0: width=%.2f height=%.2f",
1336 (i->bboxrect.xmax-i->bboxrect.xmin)/20.0,
1337 (i->bboxrect.ymax-i->bboxrect.ymin)/20.0
1340 SRECT r = i->bboxrect;
1342 if(r.xmin == r.xmax && r.ymin == r.ymax) {
1343 /* this thing comes down to a single dot- nothing to fix here */
1349 RGBA save_col = i->strokergb;
1350 int save_width = i->linewidth;
1352 i->strokergb = i->fillrgb;
1353 i->linewidth = (int)(i->config_minlinewidth*20);
1354 if(i->linewidth==0) i->linewidth = 1;
1359 moveto(dev, i->tag, r.xmin/20.0,r.ymin/20.0);
1360 lineto(dev, i->tag, r.xmax/20.0,r.ymax/20.0);
1362 i->strokergb = save_col;
1363 i->linewidth = save_width;
1368 static void endshape_noput(gfxdevice_t*dev)
1370 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1373 //changeRect(dev, i->tag, i->bboxrectpos, &i->bboxrect);
1376 swf_ShapeFree(i->shape);
1384 static void endshape(gfxdevice_t*dev)
1386 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1392 if(i->shapeisempty ||
1394 (i->bboxrect.xmin == i->bboxrect.xmax &&
1395 i->bboxrect.ymin == i->bboxrect.ymax))
1397 // delete the shape again, we didn't do anything
1398 msg("<debug> cancelling shape: bbox is (%f,%f,%f,%f)",
1399 i->bboxrect.xmin /20.0,
1400 i->bboxrect.ymin /20.0,
1401 i->bboxrect.xmax /20.0,
1402 i->bboxrect.ymax /20.0
1408 swf_ShapeSetEnd(i->tag);
1410 SRECT r = swf_ClipRect(i->pagebbox, i->bboxrect);
1411 changeRect(dev, i->tag, i->bboxrectpos, &r);
1413 msg("<trace> Placing shape ID %d", i->shapeid);
1415 i->tag = swf_InsertTag(i->tag,ST_PLACEOBJECT2);
1416 MATRIX m = i->page_matrix;
1417 m.tx += i->shapeposx;
1418 m.ty += i->shapeposy;
1419 swf_ObjectPlace(i->tag,i->shapeid,getNewDepth(dev),&m,NULL,NULL);
1421 if(i->config_animate) {
1422 i->tag = swf_InsertTag(i->tag,ST_SHOWFRAME);
1425 swf_ShapeFree(i->shape);
1428 i->bboxrectpos = -1;
1435 void wipeSWF(SWF*swf)
1437 TAG*tag = swf->firstTag;
1439 TAG*next = tag->next;
1440 if(tag->id != ST_SETBACKGROUNDCOLOR &&
1441 tag->id != ST_END &&
1442 tag->id != ST_DOACTION &&
1443 tag->id != ST_SHOWFRAME) {
1444 swf_DeleteTag(swf, tag);
1450 void swfoutput_finalize(gfxdevice_t*dev)
1452 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1454 if(i->tag && i->tag->id == ST_END)
1455 return; //already done
1457 i->swf->fileVersion = i->config_flashversion;
1458 i->swf->frameRate = i->config_framerate*0x100;
1460 if(i->config_bboxvars) {
1461 TAG* tag = swf_InsertTag(i->swf->firstTag, ST_DOACTION);
1463 a = action_PushString(a, "xmin");
1464 a = action_PushFloat(a, i->swf->movieSize.xmin / 20.0);
1465 a = action_SetVariable(a);
1466 a = action_PushString(a, "ymin");
1467 a = action_PushFloat(a, i->swf->movieSize.ymin / 20.0);
1468 a = action_SetVariable(a);
1469 a = action_PushString(a, "xmax");
1470 a = action_PushFloat(a, i->swf->movieSize.xmax / 20.0);
1471 a = action_SetVariable(a);
1472 a = action_PushString(a, "ymax");
1473 a = action_PushFloat(a, i->swf->movieSize.ymax / 20.0);
1474 a = action_SetVariable(a);
1475 a = action_PushString(a, "width");
1476 a = action_PushFloat(a, (i->swf->movieSize.xmax - i->swf->movieSize.xmin) / 20.0);
1477 a = action_SetVariable(a);
1478 a = action_PushString(a, "height");
1479 a = action_PushFloat(a, (i->swf->movieSize.ymax - i->swf->movieSize.ymin) / 20.0);
1480 a = action_SetVariable(a);
1482 swf_ActionSet(tag, a);
1487 free(i->mark);i->mark = 0;
1491 fontlist_t *iterator = i->fontlist;
1493 TAG*mtag = i->swf->firstTag;
1494 if(iterator->swffont) {
1495 if(!i->config_storeallcharacters) {
1496 msg("<debug> Reducing font %s", iterator->swffont->name);
1497 swf_FontReduce(iterator->swffont);
1499 int used = iterator->swffont->use && iterator->swffont->use->used_glyphs;
1501 mtag = swf_InsertTag(mtag, ST_DEFINEFONT2);
1502 swf_FontSetDefine2(mtag, iterator->swffont);
1506 iterator = iterator->next;
1509 i->tag = swf_InsertTag(i->tag,ST_END);
1510 TAG* tag = i->tag->prev;
1512 /* remove the removeobject2 tags between the last ST_SHOWFRAME
1513 and the ST_END- they confuse the flash player */
1514 while(tag->id == ST_REMOVEOBJECT2) {
1515 TAG* prev = tag->prev;
1516 swf_DeleteTag(i->swf, tag);
1523 if(i->config_enablezlib || i->config_flashversion>=6) {
1524 i->swf->compressed = 1;
1527 /* Add AVM2 actionscript */
1528 if(i->config_flashversion>=9 &&
1529 (i->config_insertstoptag || i->hasbuttons)) {
1530 swf_AddButtonLinks(i->swf, i->config_insertstoptag,
1531 i->config_internallinkfunction||i->config_externallinkfunction);
1533 // if(i->config_reordertags)
1534 // swf_Optimize(i->swf);
1537 int swfresult_save(gfxresult_t*gfx, const char*filename)
1539 SWF*swf = (SWF*)gfx->internal;
1542 fi = open(filename, O_BINARY|O_CREAT|O_TRUNC|O_WRONLY, 0777);
1547 msg("<fatal> Could not create \"%s\". ", FIXNULL(filename));
1551 if FAILED(swf_WriteSWF(fi,swf))
1552 msg("<error> WriteSWF() failed.\n");
1558 void* swfresult_get(gfxresult_t*gfx, const char*name)
1560 SWF*swf = (SWF*)gfx->internal;
1561 if(!strcmp(name, "swf")) {
1562 return (void*)swf_CopySWF(swf);
1563 } else if(!strcmp(name, "xmin")) {
1564 return (void*)(ptroff_t)(swf->movieSize.xmin/20);
1565 } else if(!strcmp(name, "ymin")) {
1566 return (void*)(ptroff_t)(swf->movieSize.ymin/20);
1567 } else if(!strcmp(name, "xmax")) {
1568 return (void*)(ptroff_t)(swf->movieSize.xmax/20);
1569 } else if(!strcmp(name, "ymax")) {
1570 return (void*)(ptroff_t)(swf->movieSize.ymax/20);
1571 } else if(!strcmp(name, "width")) {
1572 return (void*)(ptroff_t)((swf->movieSize.xmax - swf->movieSize.xmin)/20);
1573 } else if(!strcmp(name, "height")) {
1574 return (void*)(ptroff_t)((swf->movieSize.ymax - swf->movieSize.ymin)/20);
1578 void swfresult_destroy(gfxresult_t*gfx)
1581 swf_FreeTags((SWF*)gfx->internal);
1582 free(gfx->internal);
1585 memset(gfx, 0, sizeof(gfxresult_t));
1589 static void swfoutput_destroy(gfxdevice_t* dev);
1591 gfxresult_t* swf_finish(gfxdevice_t* dev)
1593 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1596 if(i->config_linktarget) {
1597 free(i->config_linktarget);
1598 i->config_linktarget = 0;
1601 swfoutput_finalize(dev);
1602 SWF* swf = i->swf;i->swf = 0;
1603 swfoutput_destroy(dev);
1605 result = (gfxresult_t*)rfx_calloc(sizeof(gfxresult_t));
1606 result->internal = swf;
1607 result->save = swfresult_save;
1609 result->get = swfresult_get;
1610 result->destroy = swfresult_destroy;
1614 /* Perform cleaning up */
1615 static void swfoutput_destroy(gfxdevice_t* dev)
1617 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1619 /* not initialized yet- nothing to destroy */
1623 fontlist_t *tmp,*iterator = i->fontlist;
1625 if(iterator->swffont) {
1626 swf_FontFree(iterator->swffont);iterator->swffont=0;
1629 iterator = iterator->next;
1632 if(i->swf) {swf_FreeTags(i->swf);free(i->swf);i->swf = 0;}
1635 memset(dev, 0, sizeof(gfxdevice_t));
1638 static void swfoutput_setstrokecolor(gfxdevice_t* dev, U8 r, U8 g, U8 b, U8 a)
1640 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1641 if(i->strokergb.r == r &&
1642 i->strokergb.g == g &&
1643 i->strokergb.b == b &&
1644 i->strokergb.a == a) return;
1654 //#define ROUND_UP 19
1655 //#define ROUND_UP 10
1657 static void swfoutput_setlinewidth(gfxdevice_t*dev, double _linewidth)
1659 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1660 if(i->linewidth == (U16)(_linewidth*20+19.0/20.0))
1664 i->linewidth = (U16)(_linewidth*20+19.0/20.0);
1668 static void drawlink(gfxdevice_t*dev, ActionTAG*,ActionTAG*, gfxline_t*points, char mouseover, const char*url);
1669 static void swfoutput_namedlink(gfxdevice_t*dev, char*name, gfxline_t*points);
1670 static void swfoutput_linktopage(gfxdevice_t*dev, int page, gfxline_t*points);
1671 static void swfoutput_linktourl(gfxdevice_t*dev, const char*url, gfxline_t*points);
1673 /*void swfoutput_drawlink(gfxdevice_t*dev, char*url, gfxline_t*points)
1675 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1676 dev->drawlink(dev, points, url);
1679 void swf_drawlink(gfxdevice_t*dev, gfxline_t*points, const char*url)
1681 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1683 if(i->config_disablelinks)
1686 if(!strncmp("http://pdf2swf:", url, 15)) {
1687 char*tmp = strdup(url);
1688 int l = strlen(tmp);
1691 swfoutput_namedlink(dev, tmp+15, points);
1694 } else if(!strncmp("page", url, 4)) {
1697 if(url[t]<'0' || url[t]>'9')
1700 int page = atoi(&url[4]);
1701 if(page<0) page = 0;
1702 swfoutput_linktopage(dev, page, points);
1705 swfoutput_linktourl(dev, url, points);
1708 void swfoutput_linktourl(gfxdevice_t*dev, const char*url, gfxline_t*points)
1710 ActionTAG* actions = 0;
1711 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1717 /* TODO: escape special characters in url */
1719 if(i->config_externallinkfunction && i->config_flashversion<=8) {
1720 actions = action_PushString(actions, url); //parameter
1721 actions = action_PushInt(actions, 1); //number of parameters (1)
1722 actions = action_PushString(actions, i->config_externallinkfunction); //function name
1723 actions = action_CallFunction(actions);
1724 } else if(!i->config_linktarget) {
1725 if(!i->config_opennewwindow)
1726 actions = action_GetUrl(actions, url, "_parent");
1728 actions = action_GetUrl(actions, url, "_this");
1730 actions = action_GetUrl(actions, url, i->config_linktarget);
1732 actions = action_End(actions);
1734 drawlink(dev, actions, 0, points, 0, url);
1736 swf_ActionFree(actions);
1738 void swfoutput_linktopage(gfxdevice_t*dev, int page, gfxline_t*points)
1740 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1741 ActionTAG* actions = 0;
1748 if(!i->config_internallinkfunction || i->config_flashversion>=9) {
1749 actions = action_GotoFrame(actions, page-1);
1750 actions = action_End(actions);
1752 actions = action_PushInt(actions, page); //parameter
1753 actions = action_PushInt(actions, 1); //number of parameters (1)
1754 actions = action_PushString(actions, i->config_internallinkfunction); //function name
1755 actions = action_CallFunction(actions);
1756 actions = action_End(actions);
1760 sprintf(name, "page%d", page);
1762 drawlink(dev, actions, 0, points, 0, name);
1764 swf_ActionFree(actions);
1767 /* Named Links (a.k.a. Acrobatmenu) are used to implement various gadgets
1768 of the viewer objects, like subtitles, index elements etc.
1770 void swfoutput_namedlink(gfxdevice_t*dev, char*name, gfxline_t*points)
1772 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1773 ActionTAG *actions1,*actions2;
1774 char*tmp = strdup(name);
1782 if(!strncmp(tmp, "call:", 5))
1784 char*x = strchr(&tmp[5], ':');
1786 actions1 = action_PushInt(0, 0); //number of parameters (0)
1787 actions1 = action_PushString(actions1, &tmp[5]); //function name
1788 actions1 = action_CallFunction(actions1);
1789 actions1 = action_End(actions1);
1792 actions1 = action_PushString(0, x+1); //parameter
1793 actions1 = action_PushInt(actions1, 1); //number of parameters (1)
1794 actions1 = action_PushString(actions1, &tmp[5]); //function name
1795 actions1 = action_CallFunction(actions1);
1796 actions1 = action_End(actions1);
1798 actions2 = action_End(0);
1803 actions1 = action_PushString(0, "/:subtitle");
1804 actions1 = action_PushString(actions1, name);
1805 actions1 = action_SetVariable(actions1);
1806 actions1 = action_End(actions1);
1808 actions2 = action_PushString(0, "/:subtitle");
1809 actions2 = action_PushString(actions2, "");
1810 actions2 = action_SetVariable(actions2);
1811 actions2 = action_End(actions2);
1814 drawlink(dev, actions1, actions2, points, mouseover, name);
1816 swf_ActionFree(actions1);
1817 swf_ActionFree(actions2);
1821 static void drawgfxline(gfxdevice_t*dev, gfxline_t*line, int fill)
1823 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1824 gfxcoord_t lastx=0,lasty=0,px=0,py=0;
1826 int lines= 0, splines=0;
1833 /* check whether the next segment is zero */
1834 if(line->type == gfx_moveTo) {
1835 moveto(dev, i->tag, line->x, line->y);
1836 px = lastx = line->x;
1837 py = lasty = line->y;
1839 } if(line->type == gfx_lineTo) {
1840 lineto(dev, i->tag, line->x, line->y);
1845 } else if(line->type == gfx_splineTo) {
1847 s.x = line->sx;p.x = line->x;
1848 s.y = line->sy;p.y = line->y;
1849 splineto(dev, i->tag, s, p);
1857 msg("<trace> drawgfxline, %d lines, %d splines", lines, splines);
1861 static void drawlink(gfxdevice_t*dev, ActionTAG*actions1, ActionTAG*actions2, gfxline_t*points, char mouseover, const char*url)
1863 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1872 int buttonid = getNewID(dev);
1873 gfxbbox_t bbox = gfxline_getbbox(points);
1878 myshapeid = getNewID(dev);
1879 i->tag = swf_InsertTag(i->tag,ST_DEFINESHAPE3);
1880 swf_ShapeNew(&i->shape);
1881 rgb.r = rgb.b = rgb.a = rgb.g = 0;
1882 fsid = swf_ShapeAddSolidFillStyle(i->shape,&rgb);
1883 swf_SetU16(i->tag, myshapeid);
1884 r.xmin = (int)(bbox.xmin*20);
1885 r.ymin = (int)(bbox.ymin*20);
1886 r.xmax = (int)(bbox.xmax*20);
1887 r.ymax = (int)(bbox.ymax*20);
1888 r = swf_ClipRect(i->pagebbox, r);
1889 swf_SetRect(i->tag,&r);
1890 swf_SetShapeStyles(i->tag,i->shape);
1891 swf_ShapeCountBits(i->shape,NULL,NULL);
1892 swf_SetShapeBits(i->tag,i->shape);
1893 swf_ShapeSetAll(i->tag,i->shape,/*x*/0,/*y*/0,0,fsid,0);
1894 i->swflastx = i->swflasty = 0;
1895 drawgfxline(dev, points, 1);
1896 swf_ShapeSetEnd(i->tag);
1897 swf_ShapeFree(i->shape);
1900 myshapeid2 = getNewID(dev);
1901 i->tag = swf_InsertTag(i->tag,ST_DEFINESHAPE3);
1902 swf_ShapeNew(&i->shape);
1904 rgb = i->config_linkcolor;
1906 fsid = swf_ShapeAddSolidFillStyle(i->shape,&rgb);
1907 swf_SetU16(i->tag, myshapeid2);
1908 r.xmin = (int)(bbox.xmin*20);
1909 r.ymin = (int)(bbox.ymin*20);
1910 r.xmax = (int)(bbox.xmax*20);
1911 r.ymax = (int)(bbox.ymax*20);
1912 r = swf_ClipRect(i->pagebbox, r);
1913 swf_SetRect(i->tag,&r);
1914 swf_SetShapeStyles(i->tag,i->shape);
1915 swf_ShapeCountBits(i->shape,NULL,NULL);
1916 swf_SetShapeBits(i->tag,i->shape);
1917 swf_ShapeSetAll(i->tag,i->shape,/*x*/0,/*y*/0,0,fsid,0);
1918 i->swflastx = i->swflasty = 0;
1919 drawgfxline(dev, points, 1);
1920 swf_ShapeSetEnd(i->tag);
1921 swf_ShapeFree(i->shape);
1925 i->tag = swf_InsertTag(i->tag,ST_DEFINEBUTTON);
1926 swf_SetU16(i->tag,buttonid); //id
1927 swf_ButtonSetFlags(i->tag, 0); //menu=no
1928 swf_ButtonSetRecord(i->tag,0x01,myshapeid,i->depth,0,0);
1929 swf_ButtonSetRecord(i->tag,0x02,myshapeid2,i->depth,0,0);
1930 swf_ButtonSetRecord(i->tag,0x04,myshapeid2,i->depth,0,0);
1931 swf_ButtonSetRecord(i->tag,0x08,myshapeid,i->depth,0,0);
1932 swf_SetU8(i->tag,0);
1933 swf_ActionSet(i->tag,actions1);
1934 swf_SetU8(i->tag,0);
1938 i->tag = swf_InsertTag(i->tag,ST_DEFINEBUTTON2);
1939 swf_SetU16(i->tag,buttonid); //id
1940 swf_ButtonSetFlags(i->tag, 0); //menu=no
1941 swf_ButtonSetRecord(i->tag,0x01,myshapeid,i->depth,0,0);
1942 swf_ButtonSetRecord(i->tag,0x02,myshapeid2,i->depth,0,0);
1943 swf_ButtonSetRecord(i->tag,0x04,myshapeid2,i->depth,0,0);
1944 swf_ButtonSetRecord(i->tag,0x08,myshapeid,i->depth,0,0);
1945 swf_SetU8(i->tag,0); // end of button records
1946 swf_ButtonSetCondition(i->tag, BC_IDLE_OVERUP);
1947 swf_ActionSet(i->tag,actions1);
1949 swf_ButtonSetCondition(i->tag, BC_OVERUP_IDLE);
1950 swf_ActionSet(i->tag,actions2);
1951 swf_SetU8(i->tag,0);
1952 swf_ButtonPostProcess(i->tag, 2);
1954 swf_SetU8(i->tag,0);
1955 swf_ButtonPostProcess(i->tag, 1);
1959 const char* name = 0;
1960 if(i->config_linknameurl) {
1964 sprintf(buf, "button%d", buttonid);
1967 msg("<trace> Placing link ID %d", buttonid);
1968 i->tag = swf_InsertTag(i->tag,ST_PLACEOBJECT2);
1970 if(posx!=0 || posy!=0) {
1972 p.x = (int)(posx*20);
1973 p.y = (int)(posy*20);
1974 p = swf_TurnPoint(p, &i->page_matrix);
1979 swf_ObjectPlace(i->tag, buttonid, getNewDepth(dev),&m,0,(U8*)name);
1981 swf_ObjectPlace(i->tag, buttonid, getNewDepth(dev),&i->page_matrix,0,(U8*)name);
1988 for(t=0;t<picpos;t++)
1990 if(pic_xids[t] == xid &&
1991 pic_yids[t] == yid) {
1992 width = pic_width[t];
1993 height = pic_height[t];
1997 pic_ids[picpos] = swfoutput_drawimagelosslessN(&output, pic, pal, width, height, x1,y1,x2,y2,x3,y3,x4,y4, numpalette);
1998 pic_xids[picpos] = xid;
1999 pic_yids[picpos] = yid;
2000 pic_width[picpos] = width;
2001 pic_height[picpos] = height;
2004 pic[width*y+x] = buf[0];
2008 xid += pal[1].r*3 + pal[1].g*11 + pal[1].b*17;
2009 yid += pal[1].r*7 + pal[1].g*5 + pal[1].b*23;
2013 xid += x*r+x*b*3+x*g*7+x*a*11;
2014 yid += y*r*3+y*b*17+y*g*19+y*a*11;
2016 for(t=0;t<picpos;t++)
2018 if(pic_xids[t] == xid &&
2019 pic_yids[t] == yid) {
2028 int swf_setparameter(gfxdevice_t*dev, const char*name, const char*value)
2030 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
2032 msg("<trace> swfdevice: %s=%s", name, value);
2033 if(!strcmp(name, "jpegsubpixels")) {
2034 i->config_jpegsubpixels = atof(value);
2035 } else if(!strcmp(name, "ppmsubpixels")) {
2036 i->config_ppmsubpixels = atof(value);
2037 } else if(!strcmp(name, "subpixels")) {
2038 i->config_ppmsubpixels = i->config_jpegsubpixels = atof(value);
2039 } else if(!strcmp(name, "drawonlyshapes")) {
2040 i->config_drawonlyshapes = atoi(value);
2041 } else if(!strcmp(name, "ignoredraworder")) {
2042 i->config_ignoredraworder = atoi(value);
2043 } else if(!strcmp(name, "mark")) {
2044 if(!value || !value[0]) {
2045 if(i->mark) free(i->mark);
2049 i->mark = strdup("...");
2050 for(t=0;t<3;t++) if(value[t]) i->mark[t] = value[t];
2052 } else if(!strcmp(name, "filloverlap")) {
2053 i->config_filloverlap = atoi(value);
2054 } else if(!strcmp(name, "linksopennewwindow")) {
2055 i->config_opennewwindow = atoi(value);
2056 } else if(!strcmp(name, "opennewwindow")) {
2057 i->config_opennewwindow = atoi(value);
2058 } else if(!strcmp(name, "storeallcharacters")) {
2059 i->config_storeallcharacters = atoi(value);
2060 } else if(!strcmp(name, "enablezlib")) {
2061 i->config_enablezlib = atoi(value);
2062 } else if(!strcmp(name, "bboxvars")) {
2063 i->config_bboxvars = atoi(value);
2064 } else if(!strcmp(name, "dots")) {
2065 i->config_dots = atoi(value);
2066 } else if(!strcmp(name, "frameresets")) {
2067 i->config_frameresets = atoi(value);
2068 } else if(!strcmp(name, "showclipshapes")) {
2069 i->config_showclipshapes = atoi(value);
2070 } else if(!strcmp(name, "reordertags")) {
2071 i->config_reordertags = atoi(value);
2072 } else if(!strcmp(name, "internallinkfunction")) {
2073 i->config_internallinkfunction = strdup(value);
2074 } else if(!strcmp(name, "externallinkfunction")) {
2075 i->config_externallinkfunction = strdup(value);
2076 } else if(!strcmp(name, "linkfunction")) { //sets both internallinkfunction and externallinkfunction
2077 i->config_internallinkfunction = strdup(value);
2078 i->config_externallinkfunction = strdup(value);
2079 } else if(!strcmp(name, "disable_polygon_conversion")) {
2080 i->config_disable_polygon_conversion = atoi(value);
2081 } else if(!strcmp(name, "normalize_polygon_positions")) {
2082 i->config_normalize_polygon_positions = atoi(value);
2083 } else if(!strcmp(name, "wxwindowparams")) {
2084 i->config_watermark = atoi(value);
2085 } else if(!strcmp(name, "insertstop")) {
2086 i->config_insertstoptag = atoi(value);
2087 } else if(!strcmp(name, "protect")) {
2088 i->config_protect = atoi(value);
2089 if(i->config_protect && i->tag) {
2090 i->tag = swf_InsertTag(i->tag, ST_PROTECT);
2092 } else if(!strcmp(name, "flashversion")) {
2093 i->config_flashversion = atoi(value);
2095 i->swf->fileVersion = i->config_flashversion;
2097 } else if(!strcmp(name, "framerate")) {
2098 i->config_framerate = atof(value);
2100 i->swf->frameRate = i->config_framerate*0x100;
2102 } else if(!strcmp(name, "minlinewidth")) {
2103 i->config_minlinewidth = atof(value);
2104 } else if(!strcmp(name, "caplinewidth")) {
2105 i->config_caplinewidth = atof(value);
2106 } else if(!strcmp(name, "linktarget")) {
2107 i->config_linktarget = strdup(value);
2108 } else if(!strcmp(name, "invisibletexttofront")) {
2109 i->config_invisibletexttofront = atoi(value);
2110 } else if(!strcmp(name, "noclips")) {
2111 i->config_noclips = atoi(value);
2112 } else if(!strcmp(name, "dumpfonts")) {
2113 i->config_dumpfonts = atoi(value);
2114 } else if(!strcmp(name, "animate")) {
2115 i->config_animate = atoi(value);
2116 } else if(!strcmp(name, "disablelinks")) {
2117 i->config_disablelinks = atoi(value);
2118 } else if(!strcmp(name, "simpleviewer")) {
2119 i->config_simpleviewer = atoi(value);
2120 } else if(!strcmp(name, "next_bitmap_is_jpeg")) {
2122 } else if(!strcmp(name, "jpegquality")) {
2123 int val = atoi(value);
2125 if(val>101) val=101;
2126 i->config_jpegquality = val;
2127 } else if(!strcmp(name, "splinequality")) {
2128 int v = atoi(value);
2129 v = 500-(v*5); // 100% = 0.25 pixel, 0% = 25 pixel
2131 i->config_splinemaxerror = v;
2132 } else if(!strcmp(name, "fontquality")) {
2133 int v = atoi(value);
2134 v = 500-(v*5); // 100% = 0.25 pixel, 0% = 25 pixel
2136 i->config_fontsplinemaxerror = v;
2137 } else if(!strcmp(name, "linkcolor")) {
2138 if(strlen(value)!=8) {
2139 fprintf(stderr, "Unknown format for option 'linkcolor'. (%s <-> RRGGBBAA)\n", value);
2142 # define NIBBLE(s) (((s)>='0' && (s)<='9')?((s)-'0'):((s)&0x0f)+9)
2143 i->config_linkcolor.r = NIBBLE(value[0])<<4 | NIBBLE(value[1]);
2144 i->config_linkcolor.g = NIBBLE(value[2])<<4 | NIBBLE(value[3]);
2145 i->config_linkcolor.b = NIBBLE(value[4])<<4 | NIBBLE(value[5]);
2146 i->config_linkcolor.a = NIBBLE(value[6])<<4 | NIBBLE(value[7]);
2147 } else if(!strcmp(name, "help")) {
2148 printf("\nSWF layer options:\n");
2149 printf("jpegsubpixels=<pixels> resolution adjustment for jpeg images (same as jpegdpi, but in pixels)\n");
2150 printf("ppmsubpixels=<pixels resolution adjustment for lossless images (same as ppmdpi, but in pixels)\n");
2151 printf("subpixels=<pixels> shortcut for setting both jpegsubpixels and ppmsubpixels\n");
2152 printf("drawonlyshapes convert everything to shapes (currently broken)\n");
2153 printf("ignoredraworder allow to perform a few optimizations for creating smaller SWFs\n");
2154 printf("linksopennewwindow make links open a new browser window\n");
2155 printf("linktarget target window name of new links\n");
2156 printf("linkcolor=<color) color of links (format: RRGGBBAA)\n");
2157 printf("linknameurl Link buttons will be named like the URL they refer to (handy for iterating through links with actionscript)\n");
2158 printf("storeallcharacters don't reduce the fonts to used characters in the output file\n");
2159 printf("enablezlib switch on zlib compression (also done if flashversion>=7)\n");
2160 printf("bboxvars store the bounding box of the SWF file in actionscript variables\n");
2161 printf("dots Take care to handle dots correctly\n");
2162 printf("reordertags=0/1 (default: 1) perform some tag optimizations\n");
2163 printf("internallinkfunction=<name> when the user clicks a internal link (to a different page) in the converted file, this actionscript function is called\n");
2164 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");
2165 printf("disable_polygon_conversion never convert strokes to polygons (will remove capstyles and joint styles)\n");
2166 printf("caplinewidth=<width> the minimum thichness a line needs to have so that capstyles become visible (and are converted)\n");
2167 printf("insertstop put an ActionScript \"STOP\" tag in every frame\n");
2168 printf("protect add a \"protect\" tag to the file, to prevent loading in the Flash editor\n");
2169 printf("flashversion=<version> the SWF fileversion (6)\n");
2170 printf("framerate=<fps> SWF framerate\n");
2171 printf("minlinewidth=<width> convert horizontal/vertical boxes smaller than this width to lines (0.05) \n");
2172 printf("simpleviewer Add next/previous buttons to the SWF\n");
2173 printf("animate insert a showframe tag after each placeobject (animate draw order of PDF files)\n");
2174 printf("jpegquality=<quality> set compression quality of jpeg images\n");
2175 printf("splinequality=<value> Set the quality of spline convertion to value (0-100, default: 100).\n");
2176 printf("disablelinks Disable links.\n");
2183 // --------------------------------------------------------------------
2185 static CXFORM gfxcxform_to_cxform(gfxcxform_t* c)
2188 swf_GetCXForm(0, &cx, 1);
2191 if(c->rg!=0 || c->rb!=0 || c->ra!=0 ||
2192 c->gr!=0 || c->gb!=0 || c->ga!=0 ||
2193 c->br!=0 || c->bg!=0 || c->ba!=0 ||
2194 c->ar!=0 || c->ag!=0 || c->ab!=0)
2195 msg("<warning> CXForm not SWF-compatible");
2197 cx.a0 = (S16)(c->aa*256);
2198 cx.r0 = (S16)(c->rr*256);
2199 cx.g0 = (S16)(c->gg*256);
2200 cx.b0 = (S16)(c->bb*256);
2209 static int imageInCache(gfxdevice_t*dev, void*data, int width, int height)
2213 static void addImageToCache(gfxdevice_t*dev, void*data, int width, int height)
2217 static int add_image(swfoutput_internal*i, gfximage_t*img, int targetwidth, int targetheight, int* newwidth, int* newheight)
2219 gfxdevice_t*dev = i->dev;
2221 RGBA*mem = (RGBA*)img->data;
2223 int sizex = img->width;
2224 int sizey = img->height;
2225 int is_jpeg = i->jpeg;
2228 int newsizex=sizex, newsizey=sizey;
2231 if(is_jpeg && i->config_jpegsubpixels) {
2232 newsizex = (int)(targetwidth*i->config_jpegsubpixels + 0.5);
2233 newsizey = (int)(targetheight*i->config_jpegsubpixels + 0.5);
2234 } else if(!is_jpeg && i->config_ppmsubpixels) {
2235 newsizex = (int)(targetwidth*i->config_ppmsubpixels + 0.5);
2236 newsizey = (int)(targetheight*i->config_ppmsubpixels + 0.5);
2240 if(sizex<=0 || sizey<=0)
2247 /* TODO: cache images */
2249 if(newsizex<sizex || newsizey<sizey) {
2250 msg("<verbose> Scaling %dx%d image to %dx%d", sizex, sizey, newsizex, newsizey);
2251 newpic = swf_ImageScale(mem, sizex, sizey, newsizex, newsizey);
2252 *newwidth = sizex = newsizex;
2253 *newheight = sizey = newsizey;
2256 *newwidth = newsizex = sizex;
2257 *newheight = newsizey = sizey;
2260 int num_colors = swf_ImageGetNumberOfPaletteEntries(mem,sizex,sizey,0);
2261 int has_alpha = swf_ImageHasAlpha(mem,sizex,sizey);
2263 msg("<verbose> Drawing %dx%d %s%simage (id %d) at size %dx%d (%dx%d), %s%d colors",
2265 has_alpha?(has_alpha==2?"semi-transparent ":"transparent "):"",
2266 is_jpeg?"jpeg-":"", i->currentswfid+1,
2268 targetwidth, targetheight,
2269 /*newsizex, newsizey,*/
2270 num_colors>256?">":"", num_colors>256?256:num_colors);
2272 /*RGBA* pal = (RGBA*)rfx_alloc(sizeof(RGBA)*num_colors);
2273 swf_ImageGetNumberOfPaletteEntries(mem,sizex,sizey,pal);
2275 for(t=0;t<num_colors;t++) {
2276 printf("%02x%02x%02x%02x ",
2277 pal[t].r, pal[t].g, pal[t].b, pal[t].a);
2284 int cacheid = imageInCache(dev, mem, sizex, sizey);
2287 bitid = getNewID(dev);
2289 i->tag = swf_AddImage(i->tag, bitid, mem, sizex, sizey, i->config_jpegquality);
2290 addImageToCache(dev, mem, sizex, sizey);
2300 static SRECT gfxline_getSWFbbox(gfxline_t*line)
2302 gfxbbox_t bbox = gfxline_getbbox(line);
2304 r.xmin = (int)(bbox.xmin*20);
2305 r.ymin = (int)(bbox.ymin*20);
2306 r.xmax = (int)(bbox.xmax*20);
2307 r.ymax = (int)(bbox.ymax*20);
2311 int line_is_empty(gfxline_t*line)
2314 if(line->type != gfx_moveTo)
2321 static void swf_fillbitmap(gfxdevice_t*dev, gfxline_t*line, gfximage_t*img, gfxmatrix_t*matrix, gfxcxform_t*cxform)
2323 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
2325 if(line_is_empty(line))
2331 int targetx = (int)(sqrt(matrix->m00*matrix->m00 + matrix->m01*matrix->m01)*img->width);
2332 int targety = (int)(sqrt(matrix->m10*matrix->m10 + matrix->m11*matrix->m11)*img->height);
2334 int newwidth=0,newheight=0;
2335 int bitid = add_image(i, img, targetx, targety, &newwidth, &newheight);
2338 double fx = (double)img->width / (double)newwidth;
2339 double fy = (double)img->height / (double)newheight;
2342 m.sx = (int)(65536*20*matrix->m00*fx); m.r1 = (int)(65536*20*matrix->m10*fy);
2343 m.r0 = (int)(65536*20*matrix->m01*fx); m.sy = (int)(65536*20*matrix->m11*fy);
2344 m.tx = (int)(matrix->tx*20);
2345 m.ty = (int)(matrix->ty*20);
2348 int myshapeid = getNewID(dev);
2349 i->tag = swf_InsertTag(i->tag,ST_DEFINESHAPE);
2351 swf_ShapeNew(&shape);
2352 int fsid = swf_ShapeAddBitmapFillStyle(shape,&m,bitid,1);
2353 swf_SetU16(i->tag, myshapeid);
2354 SRECT r = gfxline_getSWFbbox(line);
2355 r = swf_ClipRect(i->pagebbox, r);
2356 swf_SetRect(i->tag,&r);
2357 swf_SetShapeStyles(i->tag,shape);
2358 swf_ShapeCountBits(shape,NULL,NULL);
2359 swf_SetShapeBits(i->tag,shape);
2360 swf_ShapeSetAll(i->tag,shape,UNDEFINED_COORD,UNDEFINED_COORD,0,fsid,0);
2361 i->swflastx = i->swflasty = UNDEFINED_COORD;
2362 drawgfxline(dev, line, 1);
2363 swf_ShapeSetEnd(i->tag);
2364 swf_ShapeFree(shape);
2366 msg("<trace> Placing image, shape ID %d, bitmap ID %d", myshapeid, bitid);
2367 i->tag = swf_InsertTag(i->tag,ST_PLACEOBJECT2);
2368 CXFORM cxform2 = gfxcxform_to_cxform(cxform);
2369 swf_ObjectPlace(i->tag,myshapeid,getNewDepth(dev),&i->page_matrix,&cxform2,NULL);
2372 static RGBA col_black = {255,0,0,0};
2374 static void drawoutline(gfxdevice_t*dev, gfxline_t*line)
2376 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
2378 int myshapeid = getNewID(dev);
2379 i->tag = swf_InsertTag(i->tag,ST_DEFINESHAPE3);
2382 swf_ShapeNew(&shape);
2383 int lsid = swf_ShapeAddLineStyle(shape,1,&col_black);
2385 swf_SetU16(i->tag,myshapeid);
2386 SRECT r = gfxline_getSWFbbox(line);
2387 r = swf_ClipRect(i->pagebbox, r);
2388 swf_SetRect(i->tag,&r);
2389 swf_SetShapeStyles(i->tag,shape);
2390 swf_ShapeCountBits(shape,NULL,NULL);
2391 swf_SetShapeBits(i->tag,shape);
2392 swf_ShapeSetAll(i->tag,shape,UNDEFINED_COORD,UNDEFINED_COORD,lsid,0,0);
2393 drawgfxline(dev, line, 1);
2394 swf_ShapeSetEnd(i->tag);
2395 swf_ShapeFree(shape);
2397 i->tag = swf_InsertTag(i->tag,ST_PLACEOBJECT2);
2398 swf_ObjectPlace(i->tag, myshapeid, getNewDepth(dev), 0,0,0);
2401 static void swf_startclip(gfxdevice_t*dev, gfxline_t*line)
2403 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
2404 if(i->config_noclips)
2410 if(i->clippos >= 127)
2412 msg("<warning> Too many clip levels.");
2416 if(i->config_showclipshapes)
2417 drawoutline(dev, line);
2419 int myshapeid = getNewID(dev);
2420 i->tag = swf_InsertTag(i->tag,ST_DEFINESHAPE3);
2422 memset(&col, 0, sizeof(RGBA));
2425 swf_ShapeNew(&shape);
2426 int fsid = swf_ShapeAddSolidFillStyle(shape,&col);
2428 RGBA markcol = {0,i->mark[0],i->mark[1],i->mark[2]};
2429 swf_ShapeAddSolidFillStyle(shape,&markcol);
2431 swf_SetU16(i->tag,myshapeid);
2432 SRECT r = gfxline_getSWFbbox(line);
2433 r = swf_ClipRect(i->pagebbox, r);
2434 swf_SetRect(i->tag,&r);
2435 swf_SetShapeStyles(i->tag,shape);
2436 swf_ShapeCountBits(shape,NULL,NULL);
2437 swf_SetShapeBits(i->tag,shape);
2438 swf_ShapeSetAll(i->tag,shape,UNDEFINED_COORD,UNDEFINED_COORD,0,fsid,0);
2439 i->swflastx = i->swflasty = UNDEFINED_COORD;
2440 i->shapeisempty = 1;
2441 drawgfxline(dev, line, 1);
2442 if(i->shapeisempty) {
2443 /* an empty clip shape is equivalent to a shape with no area */
2444 int x = line?line->x:0;
2445 int y = line?line->y:0;
2446 moveto(dev, i->tag, x,y);
2447 lineto(dev, i->tag, x,y);
2448 lineto(dev, i->tag, x,y);
2450 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)) {
2451 if(i->config_watermark) {
2452 gfxbbox_t r; r.xmin = r.ymin = 0;r.xmax = i->max_x;r.ymax = i->max_y;
2453 draw_watermark(dev, r, 1);
2456 swf_ShapeSetEnd(i->tag);
2457 swf_ShapeFree(shape);
2459 /* TODO: remember the bbox, and check all shapes against it */
2461 msg("<trace> Placing clip ID %d", myshapeid);
2462 i->tag = swf_InsertTag(i->tag,ST_PLACEOBJECT2);
2463 i->cliptags[i->clippos] = i->tag;
2464 i->clipshapes[i->clippos] = myshapeid;
2465 i->clipdepths[i->clippos] = getNewDepth(dev);
2469 static void swf_endclip(gfxdevice_t*dev)
2471 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
2472 if(i->config_noclips)
2480 msg("<error> Invalid end of clipping region");
2484 /*swf_ObjectPlaceClip(i->cliptags[i->clippos],i->clipshapes[i->clippos],i->clipdepths[i->clippos],&i->page_matrix,NULL,NULL,
2485 / * clip to depth: * / i->depth <= i->clipdepths[i->clippos]? i->depth : i->depth - 1);
2487 swf_ObjectPlaceClip(i->cliptags[i->clippos],i->clipshapes[i->clippos],i->clipdepths[i->clippos],&i->page_matrix,NULL,NULL,i->depth);
2489 static int gfxline_type(gfxline_t*line)
2495 int haszerosegments=0;
2498 if(line->type == gfx_moveTo) {
2501 } else if(line->type == gfx_lineTo) {
2505 } else if(line->type == gfx_splineTo) {
2507 if(tmpsplines>lines)
2515 if(lines==0 && splines==0) return 0;
2516 else if(lines==1 && splines==0) return 1;
2517 else if(lines==0 && splines==1) return 2;
2518 else if(splines==0) return 3;
2522 static int gfxline_has_dots(gfxline_t*line)
2530 if(line->type == gfx_moveTo) {
2531 /* test the length of the preceding line, and assume it is a dot if
2532 it's length is less than 1.0. But *only* if there's a noticable
2533 gap between the previous line and the next moveTo. (I've come
2534 across a PDF where thousands of "dots" were stringed together,
2536 int last_short_gap = short_gap;
2537 if((fabs(line->x - x) + fabs(line->y - y)) < 1.0) {
2542 if(isline && dist < 1 && !short_gap && !last_short_gap) {
2547 } else if(line->type == gfx_lineTo) {
2548 dist += fabs(line->x - x) + fabs(line->y - y);
2550 } else if(line->type == gfx_splineTo) {
2551 dist += fabs(line->sx - x) + fabs(line->sy - y) +
2552 fabs(line->x - line->sx) + fabs(line->y - line->sy);
2559 if(isline && dist < 1 && !short_gap) {
2565 static int gfxline_fix_short_edges(gfxline_t*line)
2569 if(line->type == gfx_lineTo) {
2570 if(fabs(line->x - x) + fabs(line->y - y) < 0.01) {
2573 } else if(line->type == gfx_splineTo) {
2574 if(fabs(line->sx - x) + fabs(line->sy - y) +
2575 fabs(line->x - line->sx) + fabs(line->y - line->sy) < 0.01) {
2586 static char is_inside_page(gfxdevice_t*dev, gfxcoord_t x, gfxcoord_t y)
2588 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
2589 if(x<i->min_x || x>i->max_x) return 0;
2590 if(y<i->min_y || y>i->max_y) return 0;
2594 gfxline_t* gfxline_move(gfxline_t*line, double x, double y)
2596 gfxline_t*l = line = gfxline_clone(line);
2608 //#define NORMALIZE_POLYGON_POSITIONS
2610 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)
2612 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
2613 if(line_is_empty(line))
2615 int type = gfxline_type(line);
2616 int has_dots = gfxline_has_dots(line);
2617 gfxbbox_t r = gfxline_getbbox(line);
2618 int is_outside_page = !is_inside_page(dev, r.xmin, r.ymin) || !is_inside_page(dev, r.xmax, r.ymax);
2620 /* TODO: * split line into segments, and perform this check for all segments */
2622 if(i->config_disable_polygon_conversion || /*type>=5 ||*/
2624 (width <= i->config_caplinewidth
2625 || (cap_style == gfx_capRound && joint_style == gfx_joinRound)
2626 || (cap_style == gfx_capRound && type<=2))))
2630 /* convert line to polygon */
2631 msg("<trace> draw as polygon, type=%d dots=%d", type, has_dots);
2633 gfxline_fix_short_edges(line);
2634 /* we need to convert the line into a polygon */
2635 gfxpoly_t* poly = gfxpoly_from_stroke(line, width, cap_style, joint_style, miterLimit, DEFAULT_GRID);
2636 gfxline_t*gfxline = gfxline_from_gfxpoly(poly);
2637 dev->fill(dev, gfxline, color);
2638 gfxline_free(gfxline);
2639 gfxpoly_destroy(poly);
2643 msg("<trace> draw as stroke, type=%d dots=%d", type, has_dots);
2646 if(i->config_normalize_polygon_positions) {
2648 double startx = 0, starty = 0;
2649 if(line && line->type == gfx_moveTo) {
2653 line = gfxline_move(line, -startx, -starty);
2654 i->shapeposx = (int)(startx*20);
2655 i->shapeposy = (int)(starty*20);
2658 swfoutput_setstrokecolor(dev, color->r, color->g, color->b, color->a);
2659 swfoutput_setlinewidth(dev, width);
2662 drawgfxline(dev, line, 0);
2664 if(i->config_normalize_polygon_positions) {
2665 free(line); //account for _move
2670 static void swf_fill(gfxdevice_t*dev, gfxline_t*line, gfxcolor_t*color)
2672 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
2673 if(line_is_empty(line))
2677 gfxbbox_t r = gfxline_getbbox(line);
2678 int is_outside_page = !is_inside_page(dev, r.xmin, r.ymin) || !is_inside_page(dev, r.xmax, r.ymax);
2682 if(!i->config_ignoredraworder)
2685 if(i->config_normalize_polygon_positions) {
2687 double startx = 0, starty = 0;
2688 if(line && line->type == gfx_moveTo) {
2692 line = gfxline_move(line, -startx, -starty);
2693 i->shapeposx = (int)(startx*20);
2694 i->shapeposy = (int)(starty*20);
2697 swfoutput_setfillcolor(dev, color->r, color->g, color->b, color->a);
2700 drawgfxline(dev, line, 1);
2702 if(i->currentswfid==2 && r.xmin==0 && r.ymin==0 && r.xmax==i->max_x && r.ymax==i->max_y) {
2703 if(i->config_watermark) {
2704 draw_watermark(dev, r, 1);
2708 msg("<trace> end of swf_fill (shapeid=%d)", i->shapeid);
2710 if(i->config_normalize_polygon_positions) {
2711 free(line); //account for _move
2715 static GRADIENT* gfxgradient_to_GRADIENT(gfxgradient_t*gradient)
2718 gfxgradient_t*g = gradient;
2723 GRADIENT* swfgradient = malloc(sizeof(GRADIENT));
2724 swfgradient->num = num;
2725 swfgradient->rgba = malloc(sizeof(swfgradient->rgba[0])*num);
2726 swfgradient->ratios = malloc(sizeof(swfgradient->ratios[0])*num);
2731 swfgradient->ratios[num] = g->pos*255;
2732 swfgradient->rgba[num] = *(RGBA*)&g->color;
2739 static void swf_fillgradient(gfxdevice_t*dev, gfxline_t*line, gfxgradient_t*gradient, gfxgradienttype_t type, gfxmatrix_t*matrix)
2741 if(line_is_empty(line))
2743 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
2745 if(line_is_empty(line))
2748 GRADIENT* swfgradient = gfxgradient_to_GRADIENT(gradient);
2755 double f = type==gfxgradient_radial?4:4;
2757 m.sx = (int)(matrix->m00*20*f); m.r1 = (int)(matrix->m10*20*f);
2758 m.r0 = (int)(matrix->m01*20*f); m.sy = (int)(matrix->m11*20*f);
2759 m.tx = (int)(matrix->tx*20);
2760 m.ty = (int)(matrix->ty*20);
2763 int myshapeid = getNewID(dev);
2764 i->tag = swf_InsertTag(i->tag, ST_DEFINESHAPE2);
2766 swf_ShapeNew(&shape);
2767 int fsid = swf_ShapeAddGradientFillStyle(shape,&m,swfgradient,type==gfxgradient_radial);
2768 swf_SetU16(i->tag, myshapeid);
2769 SRECT r = gfxline_getSWFbbox(line);
2770 r = swf_ClipRect(i->pagebbox, r);
2771 swf_SetRect(i->tag,&r);
2772 swf_SetShapeStyles(i->tag,shape);
2773 swf_ShapeCountBits(shape,NULL,NULL);
2774 swf_SetShapeBits(i->tag,shape);
2775 swf_ShapeSetAll(i->tag,shape,UNDEFINED_COORD,UNDEFINED_COORD,0,fsid,0);
2776 i->swflastx = i->swflasty = UNDEFINED_COORD;
2777 drawgfxline(dev, line, 1);
2778 swf_ShapeSetEnd(i->tag);
2779 swf_ShapeFree(shape);
2781 int depth = getNewDepth(dev);
2782 msg("<trace> Placing gradient, shape ID %d, depth %d", myshapeid, depth);
2783 i->tag = swf_InsertTag(i->tag,ST_PLACEOBJECT2);
2784 swf_ObjectPlace(i->tag,myshapeid,depth,&i->page_matrix,NULL,NULL);
2786 swf_FreeGradient(swfgradient);free(swfgradient);
2789 static SWFFONT* gfxfont_to_swffont(gfxfont_t*font, const char* id)
2791 SWFFONT*swffont = (SWFFONT*)rfx_calloc(sizeof(SWFFONT));
2793 SRECT bounds = {0,0,0,0};
2795 swffont->version = 2;
2796 swffont->name = (U8*)strdup(id);
2797 swffont->layout = (SWFLAYOUT*)rfx_calloc(sizeof(SWFLAYOUT));
2798 swffont->layout->ascent = 0;
2799 swffont->layout->descent = 0;
2800 swffont->layout->leading = 0;
2801 swffont->layout->bounds = (SRECT*)rfx_calloc(sizeof(SRECT)*font->num_glyphs);
2802 swffont->encoding = FONT_ENCODING_UNICODE;
2803 swffont->numchars = font->num_glyphs;
2804 swffont->maxascii = font->max_unicode;
2805 swffont->ascii2glyph = (int*)rfx_calloc(sizeof(int)*swffont->maxascii);
2806 swffont->glyph2ascii = (U16*)rfx_calloc(sizeof(U16)*swffont->numchars);
2807 swffont->glyph = (SWFGLYPH*)rfx_calloc(sizeof(SWFGLYPH)*swffont->numchars);
2808 swffont->glyphnames = (char**)rfx_calloc(sizeof(char*)*swffont->numchars);
2809 for(t=0;t<font->max_unicode;t++) {
2810 swffont->ascii2glyph[t] = font->unicode2glyph[t];
2812 SRECT max = {0,0,0,0};
2813 for(t=0;t<font->num_glyphs;t++) {
2817 swffont->glyph2ascii[t] = font->glyphs[t].unicode;
2818 if(swffont->glyph2ascii[t] == 0xffff || swffont->glyph2ascii[t] == 0x0000) {
2819 /* flash 8 flashtype requires unique unicode IDs for each character.
2820 We use the Unicode private user area to assign characters, hoping that
2821 the font doesn't contain more than 2048 glyphs */
2822 swffont->glyph2ascii[t] = 0xe000 + (t&0x1fff);
2825 if(font->glyphs[t].name) {
2826 swffont->glyphnames[t] = strdup(font->glyphs[t].name);
2828 swffont->glyphnames[t] = 0;
2830 advance = font->glyphs[t].advance;
2832 swf_Shape01DrawerInit(&draw, 0);
2833 line = font->glyphs[t].line;
2837 c.x = line->sx * GLYPH_SCALE; c.y = line->sy * GLYPH_SCALE;
2838 to.x = line->x * GLYPH_SCALE; to.y = line->y * GLYPH_SCALE;
2839 if(line->type == gfx_moveTo) {
2840 draw.moveTo(&draw, &to);
2841 } else if(line->type == gfx_lineTo) {
2842 draw.lineTo(&draw, &to);
2843 } else if(line->type == gfx_splineTo) {
2844 draw.splineTo(&draw, &c, &to);
2849 swffont->glyph[t].shape = swf_ShapeDrawerToShape(&draw);
2851 SRECT bbox = swf_ShapeDrawerGetBBox(&draw);
2852 swf_ExpandRect2(&max, &bbox);
2854 swffont->layout->bounds[t] = bbox;
2856 if(advance<32768.0/20) {
2857 swffont->glyph[t].advance = (int)(advance*20);
2859 //msg("<warning> Advance value overflow in glyph %d", t);
2860 swffont->glyph[t].advance = 32767;
2863 draw.dealloc(&draw);
2865 swf_ExpandRect2(&bounds, &swffont->layout->bounds[t]);
2867 for(t=0;t<font->num_glyphs;t++) {
2868 SRECT bbox = swffont->layout->bounds[t];
2870 /* if the glyph doesn't have a bounding box, use the
2871 combined bounding box (necessary e.g. for space characters) */
2872 if(!(bbox.xmin|bbox.ymin|bbox.xmax|bbox.ymax)) {
2873 swffont->layout->bounds[t] = bbox = max;
2876 /* check that the advance value is reasonable, by comparing it
2877 with the bounding box */
2878 if(bbox.xmax>0 && (bbox.xmax*10 < swffont->glyph[t].advance || !swffont->glyph[t].advance)) {
2879 if(swffont->glyph[t].advance)
2880 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);
2881 swffont->glyph[t].advance = bbox.xmax;
2883 //swffont->glyph[t].advance = bbox.xmax - bbox.xmin;
2887 /* Flash player will use the advance value from the char, and the ascent/descent values
2888 from the layout for text selection.
2889 ascent will extend the char into negative y direction, from the baseline, while descent
2890 will extend in positive y direction, also from the baseline.
2891 The baseline is defined as the y-position zero
2894 swffont->layout->ascent = -bounds.ymin;
2895 if(swffont->layout->ascent < 0)
2896 swffont->layout->ascent = 0;
2897 swffont->layout->descent = bounds.ymax;
2898 if(swffont->layout->descent < 0)
2899 swffont->layout->descent = 0;
2900 swffont->layout->leading = bounds.ymax - bounds.ymin;
2902 /* if the font has proper ascent/descent values (>0) and those define
2903 greater line spacing that what we estimated from the bounding boxes,
2904 use the font's parameters */
2905 if(font->ascent*20 > swffont->layout->ascent)
2906 swffont->layout->ascent = font->ascent*20;
2907 if(font->descent*20 > swffont->layout->descent)
2908 swffont->layout->descent = font->descent*20;
2913 static void swf_addfont(gfxdevice_t*dev, gfxfont_t*font)
2915 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
2917 if(i->swffont && i->swffont->name && !strcmp((char*)i->swffont->name,font->id))
2918 return; // the requested font is the current font
2920 fontlist_t*last=0,*l = i->fontlist;
2923 if(!strcmp((char*)l->swffont->name, font->id)) {
2924 return; // we already know this font
2928 l = (fontlist_t*)rfx_calloc(sizeof(fontlist_t));
2929 l->swffont = gfxfont_to_swffont(font, font->id);
2936 swf_FontSetID(l->swffont, getNewID(i->dev));
2938 if(getScreenLogLevel() >= LOGLEVEL_DEBUG) {
2940 // print font information
2941 msg("<debug> Font %s",font->id);
2942 msg("<debug> | ID: %d", l->swffont->id);
2943 msg("<debug> | Version: %d", l->swffont->version);
2944 msg("<debug> | Name: %s", l->swffont->name);
2945 msg("<debug> | Numchars: %d", l->swffont->numchars);
2946 msg("<debug> | Maxascii: %d", l->swffont->maxascii);
2947 msg("<debug> | Style: %d", l->swffont->style);
2948 msg("<debug> | Encoding: %d", l->swffont->encoding);
2949 for(iii=0; iii<l->swffont->numchars;iii++) {
2950 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,
2951 l->swffont->layout->bounds[iii].xmin/20.0,
2952 l->swffont->layout->bounds[iii].ymin/20.0,
2953 l->swffont->layout->bounds[iii].xmax/20.0,
2954 l->swffont->layout->bounds[iii].ymax/20.0
2957 for(t=0;t<l->swffont->maxascii;t++) {
2958 if(l->swffont->ascii2glyph[t] == iii)
2959 msg("<debug> | - maps to %d",t);
2965 static void swf_switchfont(gfxdevice_t*dev, const char*fontid)
2967 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
2969 if(i->swffont && i->swffont->name && !strcmp((char*)i->swffont->name,fontid))
2970 return; // the requested font is the current font
2972 fontlist_t*l = i->fontlist;
2974 if(!strcmp((char*)l->swffont->name, fontid)) {
2975 i->swffont = l->swffont;
2980 msg("<error> Unknown font id: %s", fontid);
2984 static void swf_drawchar(gfxdevice_t*dev, gfxfont_t*font, int glyph, gfxcolor_t*color, gfxmatrix_t*matrix)
2986 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
2988 msg("<error> swf_drawchar called (glyph %d) without font", glyph);
2992 if(i->config_drawonlyshapes) {
2993 gfxglyph_t*g = &font->glyphs[glyph];
2994 gfxline_t*line2 = gfxline_clone(g->line);
2995 gfxline_transform(line2, matrix);
2996 dev->fill(dev, line2, color);
2997 gfxline_free(line2);
3001 if(!i->swffont || !i->swffont->name || strcmp((char*)i->swffont->name,font->id)) // not equal to current font
3003 swf_switchfont(dev, font->id); // set the current font
3007 msg("<warning> swf_drawchar: Font is NULL");
3010 if(glyph<0 || glyph>=i->swffont->numchars) {
3011 msg("<warning> No character %d in font %s (%d chars)", glyph, FIXNULL((char*)i->swffont->name), i->swffont->numchars);
3015 setfontscale(dev, matrix->m00, matrix->m01, matrix->m10, matrix->m11, matrix->tx, matrix->ty, 0);
3017 double det = i->fontmatrix.sx/65536.0 * i->fontmatrix.sy/65536.0 -
3018 i->fontmatrix.r0/65536.0 * i->fontmatrix.r1/65536.0;
3019 if(fabs(det) < 0.0005) {
3020 /* x direction equals y direction- the text is invisible */
3021 msg("<verbose> Not drawing invisible character %d (det=%f, m=[%f %f;%f %f]\n", glyph,
3023 i->fontmatrix.sx/65536.0, i->fontmatrix.r1/65536.0,
3024 i->fontmatrix.r0/65536.0, i->fontmatrix.sy/65536.0);
3028 /*if(i->swffont->glyph[glyph].shape->bitlen <= 16) {
3029 msg("<warning> Glyph %d in current charset (%s, %d characters) is empty",
3030 glyph, FIXNULL((char*)i->swffont->name), i->swffont->numchars);
3034 /* calculate character position with respect to the current font matrix */
3035 double s = 20 * GLYPH_SCALE / det;
3036 double px = matrix->tx - i->fontmatrix.tx/20.0;
3037 double py = matrix->ty - i->fontmatrix.ty/20.0;
3038 int x = (SCOORD)(( px * i->fontmatrix.sy/65536.0 - py * i->fontmatrix.r1/65536.0)*s);
3039 int y = (SCOORD)((- px * i->fontmatrix.r0/65536.0 + py * i->fontmatrix.sx/65536.0)*s);
3040 if(x>32767 || x<-32768 || y>32767 || y<-32768) {
3041 msg("<verbose> Moving character origin to %f %f\n", matrix->tx, matrix->ty);
3043 setfontscale(dev, matrix->m00, matrix->m01, matrix->m10, matrix->m11, matrix->tx, matrix->ty, 1);
3044 /* since we just moved the char origin to the current char's position,
3045 it now has the relative position (0,0) */
3054 msg("<trace> Drawing char %d in font %d at %d,%d in color %02x%02x%02x%02x",
3055 glyph, i->swffont->id, x, y, color->r, color->g, color->b, color->a);
3057 if(color->a == 0 && i->config_invisibletexttofront) {
3058 i->topchardata = charbuffer_append(i->topchardata, i->swffont, glyph, x, y, i->current_font_size, *(RGBA*)color, &i->fontmatrix);
3060 i->chardata = charbuffer_append(i->chardata, i->swffont, glyph, x, y, i->current_font_size, *(RGBA*)color, &i->fontmatrix);
3062 swf_FontUseGlyph(i->swffont, glyph);