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 int config_alignfonts;
118 char config_disablelinks;
119 RGBA config_linkcolor;
120 float config_minlinewidth;
121 double config_caplinewidth;
122 char* config_linktarget;
123 char*config_internallinkfunction;
124 char*config_externallinkfunction;
126 double config_framerate;
130 fontlist_t* fontlist;
169 int pic_height[1024];
176 char fillstylechanged;
178 int jpeg; //next image type
185 charbuffer_t* chardata;
186 charbuffer_t* topchardata; //chars supposed to be above everything else
193 int current_font_size;
195 double lastfontm11,lastfontm12,lastfontm21,lastfontm22;
206 } swfoutput_internal;
208 static const int NO_FONT3=0;
210 static void swf_fillbitmap(gfxdevice_t*driver, gfxline_t*line, gfximage_t*img, gfxmatrix_t*move, gfxcxform_t*cxform);
211 static int swf_setparameter(gfxdevice_t*driver, const char*key, const char*value);
212 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);
213 static void swf_startclip(gfxdevice_t*dev, gfxline_t*line);
214 static void swf_endclip(gfxdevice_t*dev);
215 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);
216 static void swf_fill(gfxdevice_t*dev, gfxline_t*line, gfxcolor_t*color);
217 static void swf_fillbitmap(gfxdevice_t*dev, gfxline_t*line, gfximage_t*img, gfxmatrix_t*matrix, gfxcxform_t*cxform);
218 static void swf_fillgradient(gfxdevice_t*dev, gfxline_t*line, gfxgradient_t*gradient, gfxgradienttype_t type, gfxmatrix_t*matrix);
219 static void swf_drawchar(gfxdevice_t*dev, gfxfont_t*font, int glyph, gfxcolor_t*color, gfxmatrix_t*matrix);
220 static void swf_addfont(gfxdevice_t*dev, gfxfont_t*font);
221 static void swf_drawlink(gfxdevice_t*dev, gfxline_t*line, const char*action);
222 static void swf_startframe(gfxdevice_t*dev, int width, int height);
223 static void swf_endframe(gfxdevice_t*dev);
224 static void swfoutput_namedlink(gfxdevice_t*dev, char*name, gfxline_t*points);
225 static void swfoutput_linktopage(gfxdevice_t*dev, int page, gfxline_t*points);
226 static void swfoutput_linktourl(gfxdevice_t*dev, const char*url, gfxline_t*points);
228 static gfxresult_t* swf_finish(gfxdevice_t*driver);
230 static swfoutput_internal* init_internal_struct()
232 swfoutput_internal*i = (swfoutput_internal*)malloc(sizeof(swfoutput_internal));
233 memset(i, 0, sizeof(swfoutput_internal));
257 i->fillstylechanged = 0;
264 i->config_disablelinks=0;
265 i->config_dumpfonts=0;
266 i->config_ppmsubpixels=0;
267 i->config_jpegsubpixels=0;
268 i->config_opennewwindow=1;
269 i->config_ignoredraworder=0;
270 i->config_drawonlyshapes=0;
271 i->config_jpegquality=85;
272 i->config_storeallcharacters=0;
274 i->config_enablezlib=0;
275 i->config_insertstoptag=0;
276 i->config_flashversion=6;
277 i->config_framerate=0.25;
278 i->config_splinemaxerror=1;
279 i->config_fontsplinemaxerror=1;
280 i->config_filloverlap=0;
282 i->config_bboxvars=0;
283 i->config_showclipshapes=0;
284 i->config_minlinewidth=0.05;
285 i->config_caplinewidth=1;
286 i->config_linktarget=0;
287 i->config_internallinkfunction=0;
288 i->config_externallinkfunction=0;
289 i->config_reordertags=1;
290 i->config_linknameurl=0;
292 i->config_linkcolor.r = i->config_linkcolor.g = i->config_linkcolor.b = 255;
293 i->config_linkcolor.a = 0x40;
298 static int id_error = 0;
300 static U16 getNewID(gfxdevice_t* dev)
302 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
303 if(i->currentswfid == 65535) {
305 msg("<error> ID Table overflow");
306 msg("<error> This file is too complex to render- SWF only supports 65536 shapes at once");
312 return ++i->currentswfid;
314 static U16 getNewDepth(gfxdevice_t* dev)
316 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
317 if(i->depth == 65520) {
319 msg("<error> Depth Table overflow");
320 msg("<error> This file is too complex to render- SWF only supports 65536 shapes at once");
329 static void startshape(gfxdevice_t* dev);
330 static void starttext(gfxdevice_t* dev);
331 static void endshape(gfxdevice_t* dev);
332 static void endtext(gfxdevice_t* dev);
334 typedef struct _plotxy
339 static inline int twipsnap(double f)
341 /* if(f < -0x40000000/20.0) {
342 fprintf(stderr, "Warning: Coordinate underflow (%f)\n", f);
343 f = -0x40000000/20.0;
344 } else if(f>0x3fffffff/20.0) {
345 fprintf(stderr, "Warning: Coordinate overflow (%f)\n", f);
349 /* clamp coordinates to a rectangle with the property that we
350 can represent a line from the upper left corner to the upper
351 right corner using no more than 64 strokes */
352 const double min = -(1<<(18+4))/20.0;
353 const double max = ((1<<(18+4))-1)/20.0;
355 fprintf(stderr, "Warning: Coordinate underflow (%f)\n", f);
358 fprintf(stderr, "Warning: Coordinate overflow (%f)\n", f);
365 // write a move-to command into the swf
366 static int movetoxy(gfxdevice_t*dev, TAG*tag, plotxy_t p0)
368 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
369 int rx = twipsnap(p0.x);
370 int ry = twipsnap(p0.y);
371 if(rx!=i->swflastx || ry!=i->swflasty || i->fillstylechanged) {
372 swf_ShapeSetMove (tag, i->shape, rx,ry);
373 i->fillstylechanged = 0;
380 static int moveto(gfxdevice_t*dev, TAG*tag, double x, double y)
382 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
386 return movetoxy(dev, tag, p);
388 static void addPointToBBox(gfxdevice_t*dev, int px, int py)
390 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
396 swf_ExpandRect(&i->bboxrect, p);
398 swf_ExpandRect3(&i->bboxrect, p, i->linewidth*3/2);
402 /*static void plot(gfxdevice_t*dev, int x, int y, TAG*tag)
404 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
405 int width = i->linewidth/4;
409 //swf_ShapeSetLine(tag, i->shape,-width,-width);
410 //swf_ShapeSetLine(tag, i->shape,width*2,0);
411 //swf_ShapeSetLine(tag, i->shape,0,width*2);
412 //swf_ShapeSetLine(tag, i->shape,-width*2,0);
413 //swf_ShapeSetLine(tag, i->shape,0,-width*2);
414 //swf_ShapeSetLine(tag, i->shape,width,width);
417 swf_ShapeSetLine(tag, i->shape,-width,0);
418 swf_ShapeSetLine(tag, i->shape,width,-width);
419 swf_ShapeSetLine(tag, i->shape,width,width);
420 swf_ShapeSetLine(tag, i->shape,-width,width);
421 swf_ShapeSetLine(tag, i->shape,-width,-width);
422 swf_ShapeSetLine(tag, i->shape,width,0);
424 addPointToBBox(dev, x-width ,y-width);
425 addPointToBBox(dev, x+width ,y+width);
428 // write a line-to command into the swf
429 static void linetoxy(gfxdevice_t*dev, TAG*tag, plotxy_t p0)
431 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
432 int px = twipsnap(p0.x);
433 int py = twipsnap(p0.y);
434 int rx = (px-i->swflastx);
435 int ry = (py-i->swflasty);
437 swf_ShapeSetLine (tag, i->shape, rx,ry);
438 addPointToBBox(dev, i->swflastx,i->swflasty);
439 addPointToBBox(dev, px,py);
440 } /* this is a nice idea, but doesn't work with current flash
441 players (the pixel will be invisible if they're not
442 precisely on a pixel boundary)
443 Besides, we should only do this if this lineto itself
444 is again followed by a "move".
445 else if(!i->fill && i->config_dots) {
446 // treat lines of length 0 as plots, making them
447 // at least 1 twip wide so Flash will display them
448 //plot(dev, i->swflastx, i->swflasty, tag);
449 swf_ShapeSetLine (tag, i->shape, rx+1,ry);
456 static void lineto(gfxdevice_t*dev, TAG*tag, double x, double y)
461 linetoxy(dev,tag, p);
464 // write a spline-to command into the swf
465 static void splineto(gfxdevice_t*dev, TAG*tag, plotxy_t control,plotxy_t end)
467 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
468 int lastlastx = i->swflastx;
469 int lastlasty = i->swflasty;
471 int cx = (twipsnap(control.x)-i->swflastx);
472 int cy = (twipsnap(control.y)-i->swflasty);
475 int ex = (twipsnap(end.x)-i->swflastx);
476 int ey = (twipsnap(end.y)-i->swflasty);
480 if((cx || cy) && (ex || ey)) {
481 swf_ShapeSetCurve(tag, i->shape, cx,cy,ex,ey);
482 addPointToBBox(dev, lastlastx ,lastlasty );
483 addPointToBBox(dev, lastlastx+cx,lastlasty+cy);
484 addPointToBBox(dev, lastlastx+cx+ex,lastlasty+cy+ey);
485 } else if(cx || cy || ex || ey) {
486 swf_ShapeSetLine(tag, i->shape, cx+ex,cy+ey);
487 addPointToBBox(dev, lastlastx ,lastlasty );
488 addPointToBBox(dev, lastlastx+cx,lastlasty+cy);
489 addPointToBBox(dev, lastlastx+cx+ex,lastlasty+cy+ey);
495 /* write a line, given two points and the transformation
497 /*static void line(gfxdevice_t*dev, TAG*tag, plotxy_t p0, plotxy_t p1)
499 moveto(dev, tag, p0);
500 lineto(dev, tag, p1);
503 void resetdrawer(gfxdevice_t*dev)
505 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
510 static void stopFill(gfxdevice_t*dev)
512 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
513 if(i->lastwasfill!=0)
515 swf_ShapeSetStyle(i->tag,i->shape,i->linestyleid,0x8000,0);
516 i->fillstylechanged = 1;
520 static void startFill(gfxdevice_t*dev)
522 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
523 if(i->lastwasfill!=1)
525 swf_ShapeSetStyle(i->tag,i->shape,0x8000,i->fillstyleid,0);
526 i->fillstylechanged = 1;
531 static inline int colorcompare(RGBA*a,RGBA*b)
543 static SRECT getcharacterbbox(chararray_t*chardata, MATRIX* m, int flashversion)
547 memset(&r, 0, sizeof(r));
550 if(debug) printf("\n");
552 double div = 1.0 / 1024.0;
553 if(flashversion>=8 && !NO_FONT3) {
558 for(t=0;t<chardata->pos;t++) {
559 charatposition_t*chr = &chardata->chr[t];
560 SRECT b = chr->font->layout->bounds[chr->charid];
561 b.xmin = floor((b.xmin*(double)chr->size) *div);
562 b.ymin = floor((b.ymin*(double)chr->size) *div);
563 b.xmax = ceil((b.xmax*(double)chr->size) *div);
564 b.ymax = ceil((b.ymax*(double)chr->size) *div);
571 /* until we solve the INTERNAL_SCALING problem (see below)
572 make sure the bounding box is big enough */
578 b = swf_TurnRect(b, m);
580 if(debug) printf("(%f,%f,%f,%f) -> (%f,%f,%f,%f) [font %d, char %d]\n",
581 chr->font->layout->bounds[chr->charid].xmin/20.0,
582 chr->font->layout->bounds[chr->charid].ymin/20.0,
583 chr->font->layout->bounds[chr->charid].xmax/20.0,
584 chr->font->layout->bounds[chr->charid].ymax/20.0,
591 swf_ExpandRect2(&r, &b);
593 chardata = chardata->next;
595 if(debug) printf("-----> (%f,%f,%f,%f)\n",
603 static chararray_t*chararray_reverse(chararray_t*buf)
605 chararray_t*prev = 0;
607 chararray_t*next = buf->next;
615 static void chararray_writetotag(chararray_t*_chardata, TAG*tag)
619 color.r = _chardata?_chardata->chr[0].color.r^255:0;
629 int charadvance[128];
632 int glyphbits=1; //TODO: can this be zero?
635 if(tag->id != ST_DEFINETEXT &&
636 tag->id != ST_DEFINETEXT2) {
637 msg("<error> internal error: charbuffer_put needs an text tag, not %d\n",tag->id);
641 msg("<warning> charbuffer_put called with zero characters");
644 for(pass = 0; pass < 2; pass++)
655 advancebits++; // add sign bit
656 swf_SetU8(tag, glyphbits);
657 swf_SetU8(tag, advancebits);
660 chararray_t*chardata = _chardata;
665 assert(!chardata->next || chardata->pos == CHARDATAMAX);
666 assert(chardata->pos);
668 int to = chardata->next?chardata->pos-1:chardata->pos;
672 char islast = t==chardata->pos;
674 charatposition_t*chr = &chardata->chr[t];
676 if(lastfont != chr->font ||
679 !colorcompare(&color, &chardata->chr[t].color) ||
681 lastsize != chardata->chr[t].size ||
684 if(charstorepos && pass==0)
687 for(s=0;s<charstorepos;s++)
689 while(charids[s]>=(1<<glyphbits))
691 while(charadvance[s]>=(1<<advancebits))
695 if(charstorepos && pass==1)
697 tag->writeBit = 0; // Q&D
698 swf_SetBits(tag, 0, 1); // GLYPH Record
699 swf_SetBits(tag, charstorepos, 7); // number of glyphs
701 for(s=0;s<charstorepos;s++)
703 swf_SetBits(tag, charids[s], glyphbits);
704 swf_SetBits(tag, charadvance[s], advancebits);
709 if(pass == 1 && !islast)
715 if(lastx != chr->x ||
725 if(!colorcompare(&color, &chr->color))
730 font.id = chr->font->id;
731 if(lastfont != chr->font || lastsize != chr->size)
734 tag->writeBit = 0; // Q&D
735 swf_TextSetInfoRecord(tag, newfont, chr->size, newcolor, newx, newy);
738 lastfont = chr->font;
741 lastsize = chr->size;
748 if(t<chardata->pos-1) nextx = chardata->chr[t+1].x;
749 if(t==chardata->pos-1 && chardata->next) nextx = chardata->next->chr[0].x;
750 int dx = nextx-chr->x;
753 if(dx>=0 && (dx<(1<<(advancebits-1)) || pass==0)) {
761 charids[charstorepos] = chr->charid;
762 charadvance[charstorepos] = advance;
763 lastchar = chr->charid;
766 chardata = chardata->next;
771 static void chararray_destroy(chararray_t*chr)
774 chararray_t*next = chr->next;
781 static inline int matrix_diff(MATRIX*m1, MATRIX*m2)
783 return memcmp(m1,m2,sizeof(MATRIX));
785 static charbuffer_t*charbuffer_append(charbuffer_t*buf, SWFFONT*font, int charid, int x,int y, int size, RGBA color, MATRIX*m)
787 if(!buf || matrix_diff(&buf->matrix,m)) {
788 charbuffer_t*n = rfx_calloc(sizeof(charbuffer_t));
793 if(!buf->last || buf->last->pos == CHARDATAMAX) {
794 chararray_t*n = rfx_calloc(sizeof(chararray_t));
796 buf->array = buf->last = n;
802 chararray_t*a = buf->last;
803 a->chr[a->pos].font = font;
804 a->chr[a->pos].charid = charid;
805 a->chr[a->pos].x = x;
806 a->chr[a->pos].y = y;
807 a->chr[a->pos].color = color;
808 a->chr[a->pos].size = size;
813 /* Notice: we can only put chars in the range -1639,1638 (-32768/20,32768/20).
814 So if we set this value to high, the char coordinates will overflow.
815 If we set it to low, however, the char positions will be inaccurate */
816 #define GLYPH_SCALE 1
818 static void chararray_writetodev(gfxdevice_t*dev, chararray_t*array, MATRIX*matrix, char invisible)
820 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
822 int textid = getNewID(dev);
823 i->tag = swf_InsertTag(i->tag,ST_DEFINETEXT2);
824 swf_SetU16(i->tag, textid);
826 r = getcharacterbbox(array, matrix, i->config_flashversion);
827 r = swf_ClipRect(i->pagebbox, r);
828 swf_SetRect(i->tag,&r);
829 swf_SetMatrix(i->tag, matrix);
830 msg("<trace> Placing text as ID %d", textid);
831 chararray_writetotag(array, i->tag);
836 if(i->swf->fileVersion >= 8) {
837 i->tag = swf_InsertTag(i->tag, ST_CSMTEXTSETTINGS);
838 swf_SetU16(i->tag, textid);
840 //swf_SetU8(i->tag, /*subpixel grid*/(2<<3)|/*flashtype*/0x40);
841 swf_SetU8(i->tag, /*grid*/(1<<3)|/*flashtype*/0x40);
842 //swf_SetU8(i->tag, /*no grid*/(0<<3)|/*flashtype*/0x40);
844 swf_SetU32(i->tag, 0);//thickness
845 swf_SetU32(i->tag, 0);//sharpness
846 //swf_SetU32(i->tag, 0x20000);//thickness
847 //swf_SetU32(i->tag, 0x800000);//sharpness
848 swf_SetU8(i->tag, 0);//reserved
850 if(invisible && i->config_flashversion>=8) {
851 i->tag = swf_InsertTag(i->tag,ST_PLACEOBJECT3);
852 swf_ObjectPlaceBlend(i->tag,textid,getNewDepth(dev),&i->page_matrix,NULL,NULL,BLENDMODE_MULTIPLY);
854 i->tag = swf_InsertTag(i->tag,ST_PLACEOBJECT2);
855 swf_ObjectPlace(i->tag,textid,getNewDepth(dev),&i->page_matrix,NULL,NULL);
859 static void charbuffer_writetodevandfree(gfxdevice_t*dev, charbuffer_t*buf, char invisible)
862 charbuffer_t*next = buf->next;buf->next = 0;
863 chararray_writetodev(dev, buf->array, &buf->matrix, invisible);
864 chararray_destroy(buf->array);
870 static void endtext(gfxdevice_t*dev)
872 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
875 charbuffer_writetodevandfree(dev, i->chardata, 0);i->chardata = 0;
879 static int watermark2_width=47;
880 static int watermark2_height=11;
881 static int watermark2[47] = {95,1989,71,0,2015,337,1678,0,2015,5,1921,320,1938,25,2006,1024,
882 1042,21,13,960,1039,976,8,2000,1359,1088,31,1989,321,1728,0,1152,
883 1344,832,0,1984,0,896,1088,1088,896,0,1984,128,256,512,1984};
885 static void draw_watermark(gfxdevice_t*dev, gfxbbox_t r, char drawall)
887 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
888 double wx = r.xmax / 5.0;
889 double tx = r.xmax*4.0 / 5.0;
890 double ty = r.ymax-wx*watermark2_height/watermark2_width;
891 double sx = (r.xmax - tx) / watermark2_width;
892 double sy = (r.ymax - ty) / watermark2_height;
895 if(ty > 0 && px > 1.0 && py > 1.0) {
897 for(y=0;y<watermark2_height;y++)
898 for(x=0;x<watermark2_width;x++) {
899 if(((watermark2[x]>>y)&1)) {
900 if(!drawall && rand()%5)
902 unsigned int b = rand();
903 moveto(dev, i->tag, x*sx+tx+((b>>1)&1)/20.0, y*sy+ty+((b>>3)&1)/20.0);
904 lineto(dev, i->tag, x*sx+px+tx+((b>>2)&1)/20.0, y*sy+ty+((b>>3)&1)/20.0);
905 lineto(dev, i->tag, x*sx+px+tx+((b>>2)&1)/20.0, y*sy+py+ty+((b>>4)&1)/20.0);
906 lineto(dev, i->tag, x*sx+tx+((b>>1)&1)/20.0, y*sy+py+ty+((b>>4)&1)/20.0);
907 lineto(dev, i->tag, x*sx+tx+((b>>1)&1)/20.0, y*sy+ty+((b>>3)&1)/20.0);
913 static void swfoutput_setfillcolor(gfxdevice_t* dev, U8 r, U8 g, U8 b, U8 a)
915 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
916 if(i->fillrgb.r == r &&
919 i->fillrgb.a == a) return;
928 static void insert_watermark(gfxdevice_t*dev, char drawall)
930 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
931 if(!drawall && i->watermarks>20)
937 swfoutput_setfillcolor(dev, 0,0,255,192);
939 swfoutput_setfillcolor(dev, rand(),rand(),rand(),(rand()&127)+128);
944 gfxbbox_t r; r.xmin = r.ymin = 0;
947 draw_watermark(dev, r, drawall);
953 static void endpage(gfxdevice_t*dev)
955 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
963 charbuffer_writetodevandfree(dev, i->topchardata, 1);
970 if(i->config_watermark) {
971 insert_watermark(dev, 1);
977 static void addViewer(gfxdevice_t* dev)
979 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
982 RGBA button_colors[3]= {{0xbf,0x00,0x00,0x80},{0xbf,0x20,0x20,0xc0}, {0xbf,0xc0,0xc0,0xff}};
984 int button_sizex = 20;
985 int button_sizey = 20;
987 RGBA black = {255,0,0,0};
989 i->tag = swf_InsertTag(i->tag,ST_DEFINESHAPE3);
991 int ls1 = swf_ShapeAddLineStyle(s,40,&black);
992 int fs1 = swf_ShapeAddSolidFillStyle(s,&button_colors[t/2]);
993 int shapeid = ids[t] = getNewID(dev);
994 swf_SetU16(i->tag,shapeid);
996 r.xmin = -20*button_sizex;
997 r.xmax = 20*button_sizex;
999 r.ymax = 40*button_sizey;
1000 swf_SetRect(i->tag,&r); // set shape bounds
1001 swf_SetShapeHeader(i->tag,s); // write all styles to tag
1002 swf_ShapeSetAll(i->tag,s,0*button_sizex,0,ls1,fs1,0);
1003 swf_ShapeSetLine(i->tag,s,(1-(t&1)*2)*20*button_sizex,20*button_sizey);
1004 swf_ShapeSetLine(i->tag,s,-(1-(t&1)*2)*20*button_sizex,20*button_sizey);
1005 swf_ShapeSetLine(i->tag,s,0,-40*button_sizey);
1006 swf_ShapeSetEnd(i->tag); // finish drawing
1007 swf_ShapeFree(s); // clean shape structure (which isn't needed anymore after writing the tag)
1009 ActionTAG*a1=0,*a2=0,*a3=0;
1010 a1 = action_NextFrame(a1);
1011 a1 = action_Stop(a1);
1012 a1 = action_End(a1);
1014 a2 = action_PreviousFrame(a2);
1015 a2 = action_Stop(a2);
1016 a2 = action_End(a2);
1018 a3 = action_Stop(a3);
1019 a3 = action_End(a3);
1021 i->tag = swf_InsertTag(i->tag, ST_DOACTION);
1022 swf_ActionSet(i->tag,a3);
1024 i->tag = swf_InsertTag(i->tag,ST_DEFINEBUTTON);
1025 int buttonid1 = getNewID(dev);
1026 swf_SetU16(i->tag, buttonid1);
1027 swf_ButtonSetRecord(i->tag,BS_UP|BS_HIT,ids[0],1,NULL,NULL);
1028 swf_ButtonSetRecord(i->tag,BS_OVER,ids[2],1,NULL,NULL);
1029 swf_ButtonSetRecord(i->tag,BS_DOWN,ids[4],1,NULL,NULL);
1030 swf_SetU8(i->tag,0); // end of button records
1031 swf_ActionSet(i->tag,a1);
1033 i->tag = swf_InsertTag(i->tag,ST_DEFINEBUTTON);
1034 int buttonid2 = getNewID(dev);
1035 swf_SetU16(i->tag, buttonid2);
1036 swf_ButtonSetRecord(i->tag,BS_UP|BS_HIT,ids[1],1,NULL,NULL);
1037 swf_ButtonSetRecord(i->tag,BS_OVER,ids[3],1,NULL,NULL);
1038 swf_ButtonSetRecord(i->tag,BS_DOWN,ids[5],1,NULL,NULL);
1039 swf_SetU8(i->tag,0); // end of button records
1040 swf_ActionSet(i->tag,a2);
1042 i->tag = swf_InsertTag(i->tag,ST_PLACEOBJECT2);
1044 swf_GetMatrix(0, &m);
1045 m.tx = button_sizex*20+200;
1046 swf_ObjectPlace(i->tag, buttonid2, 65534,&m,0,0);
1047 i->tag = swf_InsertTag(i->tag,ST_PLACEOBJECT2);
1048 m.tx = button_sizex*20+200+200;
1049 swf_ObjectPlace(i->tag, buttonid1, 65535,&m,0,0);
1053 void swf_startframe(gfxdevice_t*dev, int width, int height)
1055 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1057 if(i->config_protect) {
1058 i->tag = swf_InsertTag(i->tag, ST_PROTECT);
1059 i->config_protect = 0;
1061 if(i->config_simpleviewer) {
1066 if(!i->firstpage && !i->pagefinished)
1069 msg("<verbose> Starting new SWF page of size %dx%d", width, height);
1071 swf_GetMatrix(0, &i->page_matrix);
1072 i->page_matrix.tx = 0;
1073 i->page_matrix.ty = 0;
1080 /* create a bbox structure with the page size. This is used
1081 for clipping shape and text bounding boxes. As we don't want to
1082 generate bounding boxes which extend beyond the movie size (in
1083 order to not confuse Flash), we clip everything against i->pagebbox */
1084 i->pagebbox.xmin = 0;
1085 i->pagebbox.ymin = 0;
1086 i->pagebbox.xmax = width*20;
1087 i->pagebbox.ymax = height*20;
1089 /* increase SWF's bounding box */
1090 swf_ExpandRect2(&i->swf->movieSize, &i->pagebbox);
1092 i->lastframeno = i->frameno;
1094 i->pagefinished = 0;
1098 void swf_endframe(gfxdevice_t*dev)
1100 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1102 if(!i->pagefinished)
1105 if( (i->swf->fileVersion <= 8) && (i->config_insertstoptag) ) {
1107 atag = action_Stop(atag);
1108 atag = action_End(atag);
1109 i->tag = swf_InsertTag(i->tag,ST_DOACTION);
1110 swf_ActionSet(i->tag,atag);
1112 i->tag = swf_InsertTag(i->tag,ST_SHOWFRAME);
1115 for(i->depth;i->depth>i->startdepth;i->depth--) {
1116 i->tag = swf_InsertTag(i->tag,ST_REMOVEOBJECT2);
1117 swf_SetU16(i->tag,i->depth);
1119 i->depth = i->startdepth;
1121 if(i->config_frameresets) {
1122 for(i->currentswfid;i->currentswfid>i->startids;i->currentswfid--) {
1123 i->tag = swf_InsertTag(i->tag,ST_FREECHARACTER);
1124 swf_SetU16(i->tag,i->currentswfid);
1126 i->currentswfid = i->startids;
1130 static void setBackground(gfxdevice_t*dev, int x1, int y1, int x2, int y2)
1132 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1134 rgb.a = rgb.r = rgb.g = rgb.b = 0xff;
1138 int shapeid = getNewID(dev);
1143 i->tag = swf_InsertTag(i->tag, ST_DEFINESHAPE);
1145 fs1 = swf_ShapeAddSolidFillStyle(s, &rgb);
1146 swf_SetU16(i->tag,shapeid);
1147 swf_SetRect(i->tag,&r);
1148 swf_SetShapeHeader(i->tag,s);
1149 swf_ShapeSetAll(i->tag,s,x1,y1,ls1,fs1,0);
1150 swf_ShapeSetLine(i->tag,s,(x2-x1),0);
1151 swf_ShapeSetLine(i->tag,s,0,(y2-y1));
1152 swf_ShapeSetLine(i->tag,s,(x1-x2),0);
1153 swf_ShapeSetLine(i->tag,s,0,(y1-y2));
1154 swf_ShapeSetEnd(i->tag);
1156 i->tag = swf_InsertTag(i->tag, ST_PLACEOBJECT2);
1157 swf_ObjectPlace(i->tag,shapeid,getNewDepth(dev),0,0,0);
1158 i->tag = swf_InsertTag(i->tag, ST_PLACEOBJECT2);
1159 swf_ObjectPlaceClip(i->tag,shapeid,getNewDepth(dev),0,0,0,65535);
1162 /* initialize the swf writer */
1163 void gfxdevice_swf_init(gfxdevice_t* dev)
1165 memset(dev, 0, sizeof(gfxdevice_t));
1169 dev->internal = init_internal_struct(); // set config to default values
1171 dev->startpage = swf_startframe;
1172 dev->endpage = swf_endframe;
1173 dev->finish = swf_finish;
1174 dev->fillbitmap = swf_fillbitmap;
1175 dev->setparameter = swf_setparameter;
1176 dev->stroke = swf_stroke;
1177 dev->startclip = swf_startclip;
1178 dev->endclip = swf_endclip;
1179 dev->fill = swf_fill;
1180 dev->fillbitmap = swf_fillbitmap;
1181 dev->fillgradient = swf_fillgradient;
1182 dev->addfont = swf_addfont;
1183 dev->drawchar = swf_drawchar;
1184 dev->drawlink = swf_drawlink;
1186 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1189 msg("<verbose> initializing swf output\n", i->max_x,i->max_y);
1193 i->swf = (SWF*)rfx_calloc(sizeof(SWF));
1194 i->swf->fileVersion = 0;
1195 i->swf->frameRate = 0x80;
1196 i->swf->movieSize.xmin = 0;
1197 i->swf->movieSize.ymin = 0;
1198 i->swf->movieSize.xmax = 0;
1199 i->swf->movieSize.ymax = 0;
1200 i->swf->fileAttributes = 9; // as3, local-with-network
1202 i->swf->firstTag = swf_InsertTag(NULL,ST_SETBACKGROUNDCOLOR);
1203 i->tag = i->swf->firstTag;
1205 rgb.a = rgb.r = rgb.g = rgb.b = 0xff;
1207 swf_SetRGB(i->tag,&rgb);
1209 i->startdepth = i->depth = 0;
1210 i->startids = i->currentswfid = 0;
1213 static void startshape(gfxdevice_t*dev)
1215 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1220 //if(i->chardatapos && i->chardata[i->chardatapos-1].color.a)
1223 i->tag = swf_InsertTag(i->tag,ST_DEFINESHAPE3);
1225 swf_ShapeNew(&i->shape);
1226 i->linestyleid = swf_ShapeAddLineStyle(i->shape,i->linewidth,&i->strokergb);
1227 i->fillstyleid = swf_ShapeAddSolidFillStyle(i->shape,&i->fillrgb);
1229 RGBA markcol = {0,i->mark[0],i->mark[1],i->mark[2]};
1230 swf_ShapeAddSolidFillStyle(i->shape,&markcol);
1233 i->shapeid = getNewID(dev);
1235 msg("<debug> Using shape id %d", i->shapeid);
1237 swf_SetU16(i->tag,i->shapeid); // ID
1239 i->bboxrectpos = i->tag->len;
1241 swf_SetRect(i->tag,&i->pagebbox);
1243 memset(&i->bboxrect, 0, sizeof(i->bboxrect));
1245 swf_SetShapeStyles(i->tag,i->shape);
1246 swf_ShapeCountBits(i->shape,NULL,NULL);
1247 swf_SetShapeBits(i->tag,i->shape);
1249 /* TODO: do we really need this? */
1250 //swf_ShapeSetAll(i->tag,i->shape,/*x*/0,/*y*/0,i->linestyleid,0,0);
1251 //swf_ShapeSetAll(i->tag,i->shape,/*x*/UNDEFINED_COORD,/*y*/UNDEFINED_COORD,i->linestyleid,0,0);
1252 i->swflastx=i->swflasty=UNDEFINED_COORD;
1253 i->lastwasfill = -1;
1254 i->shapeisempty = 1;
1257 static void starttext(gfxdevice_t*dev)
1259 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1263 if(i->config_watermark) {
1264 insert_watermark(dev, 0);
1267 i->swflastx=i->swflasty=0;
1271 /* TODO: move to ../lib/rfxswf */
1272 void changeRect(gfxdevice_t*dev, TAG*tag, int pos, SRECT*newrect)
1274 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1275 /* determine length of old rect */
1279 swf_GetRect(tag, &old);
1280 swf_ResetReadBits(tag);
1281 int pos_end = tag->pos;
1283 int len = tag->len - pos_end;
1284 U8*data = (U8*)malloc(len);
1285 memcpy(data, &tag->data[pos_end], len);
1288 swf_SetRect(tag, newrect);
1289 swf_SetBlock(tag, data, len);
1291 tag->pos = tag->readBit = 0;
1294 void cancelshape(gfxdevice_t*dev)
1296 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1297 /* delete old shape tag */
1299 i->tag = i->tag->prev;
1300 swf_DeleteTag(0, todel);
1301 if(i->shape) {swf_ShapeFree(i->shape);i->shape=0;}
1303 i->bboxrectpos = -1;
1305 // i->currentswfid--; // doesn't work, for some reason
1308 void fixAreas(gfxdevice_t*dev)
1310 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1311 if(!i->shapeisempty && i->fill &&
1312 (i->bboxrect.xmin == i->bboxrect.xmax ||
1313 i->bboxrect.ymin == i->bboxrect.ymax) &&
1314 i->config_minlinewidth >= 0.001
1316 msg("<debug> Shape has size 0: width=%.2f height=%.2f",
1317 (i->bboxrect.xmax-i->bboxrect.xmin)/20.0,
1318 (i->bboxrect.ymax-i->bboxrect.ymin)/20.0
1321 SRECT r = i->bboxrect;
1323 if(r.xmin == r.xmax && r.ymin == r.ymax) {
1324 /* this thing comes down to a single dot- nothing to fix here */
1330 RGBA save_col = i->strokergb;
1331 int save_width = i->linewidth;
1333 i->strokergb = i->fillrgb;
1334 i->linewidth = (int)(i->config_minlinewidth*20);
1335 if(i->linewidth==0) i->linewidth = 1;
1340 moveto(dev, i->tag, r.xmin/20.0,r.ymin/20.0);
1341 lineto(dev, i->tag, r.xmax/20.0,r.ymax/20.0);
1343 i->strokergb = save_col;
1344 i->linewidth = save_width;
1349 static void endshape_noput(gfxdevice_t*dev)
1351 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1354 //changeRect(dev, i->tag, i->bboxrectpos, &i->bboxrect);
1357 swf_ShapeFree(i->shape);
1365 static void endshape(gfxdevice_t*dev)
1367 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1373 if(i->shapeisempty ||
1375 (i->bboxrect.xmin == i->bboxrect.xmax &&
1376 i->bboxrect.ymin == i->bboxrect.ymax))
1378 // delete the shape again, we didn't do anything
1379 msg("<debug> cancelling shape: bbox is (%f,%f,%f,%f)",
1380 i->bboxrect.xmin /20.0,
1381 i->bboxrect.ymin /20.0,
1382 i->bboxrect.xmax /20.0,
1383 i->bboxrect.ymax /20.0
1389 swf_ShapeSetEnd(i->tag);
1391 SRECT r = swf_ClipRect(i->pagebbox, i->bboxrect);
1392 changeRect(dev, i->tag, i->bboxrectpos, &r);
1394 msg("<trace> Placing shape ID %d", i->shapeid);
1396 i->tag = swf_InsertTag(i->tag,ST_PLACEOBJECT2);
1397 MATRIX m = i->page_matrix;
1398 m.tx += i->shapeposx;
1399 m.ty += i->shapeposy;
1400 swf_ObjectPlace(i->tag,i->shapeid,getNewDepth(dev),&m,NULL,NULL);
1402 if(i->config_animate) {
1403 i->tag = swf_InsertTag(i->tag,ST_SHOWFRAME);
1406 swf_ShapeFree(i->shape);
1409 i->bboxrectpos = -1;
1416 void wipeSWF(SWF*swf)
1418 TAG*tag = swf->firstTag;
1420 TAG*next = tag->next;
1421 if(tag->id != ST_SETBACKGROUNDCOLOR &&
1422 tag->id != ST_END &&
1423 tag->id != ST_DOACTION &&
1424 tag->id != ST_SHOWFRAME) {
1425 swf_DeleteTag(swf, tag);
1431 void swfoutput_finalize(gfxdevice_t*dev)
1433 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1435 if(i->tag && i->tag->id == ST_END)
1436 return; //already done
1438 i->swf->fileVersion = i->config_flashversion;
1439 i->swf->frameRate = i->config_framerate*0x100;
1441 if(i->config_bboxvars) {
1442 TAG* tag = swf_InsertTag(i->swf->firstTag, ST_DOACTION);
1444 a = action_PushString(a, "xmin");
1445 a = action_PushFloat(a, i->swf->movieSize.xmin / 20.0);
1446 a = action_SetVariable(a);
1447 a = action_PushString(a, "ymin");
1448 a = action_PushFloat(a, i->swf->movieSize.ymin / 20.0);
1449 a = action_SetVariable(a);
1450 a = action_PushString(a, "xmax");
1451 a = action_PushFloat(a, i->swf->movieSize.xmax / 20.0);
1452 a = action_SetVariable(a);
1453 a = action_PushString(a, "ymax");
1454 a = action_PushFloat(a, i->swf->movieSize.ymax / 20.0);
1455 a = action_SetVariable(a);
1456 a = action_PushString(a, "width");
1457 a = action_PushFloat(a, (i->swf->movieSize.xmax - i->swf->movieSize.xmin) / 20.0);
1458 a = action_SetVariable(a);
1459 a = action_PushString(a, "height");
1460 a = action_PushFloat(a, (i->swf->movieSize.ymax - i->swf->movieSize.ymin) / 20.0);
1461 a = action_SetVariable(a);
1463 swf_ActionSet(tag, a);
1468 free(i->mark);i->mark = 0;
1472 fontlist_t *iterator = i->fontlist;
1473 char use_font3 = i->config_flashversion>=8 && !NO_FONT3;
1476 TAG*mtag = i->swf->firstTag;
1477 if(iterator->swffont) {
1478 if(!i->config_storeallcharacters) {
1479 msg("<debug> Reducing font %s", iterator->swffont->name);
1480 swf_FontReduce(iterator->swffont);
1482 int used = iterator->swffont->use && iterator->swffont->use->used_glyphs;
1485 mtag = swf_InsertTag(mtag, ST_DEFINEFONT2);
1486 swf_FontSetDefine2(mtag, iterator->swffont);
1488 mtag = swf_InsertTag(mtag, ST_DEFINEFONT3);
1489 swf_FontSetDefine2(mtag, iterator->swffont);
1494 iterator = iterator->next;
1497 i->tag = swf_InsertTag(i->tag,ST_END);
1498 TAG* tag = i->tag->prev;
1500 if(use_font3 && i->config_storeallcharacters && i->config_alignfonts) {
1501 swf_FontPostprocess(i->swf); // generate alignment information
1504 /* remove the removeobject2 tags between the last ST_SHOWFRAME
1505 and the ST_END- they confuse the flash player */
1506 while(tag->id == ST_REMOVEOBJECT2) {
1507 TAG* prev = tag->prev;
1508 swf_DeleteTag(i->swf, tag);
1515 if(i->config_enablezlib || i->config_flashversion>=6) {
1516 i->swf->compressed = 1;
1519 /* Add AVM2 actionscript */
1520 if(i->config_flashversion>=9 &&
1521 (i->config_insertstoptag || i->hasbuttons) && !i->config_linknameurl) {
1522 swf_AddButtonLinks(i->swf, i->config_insertstoptag,
1523 i->config_internallinkfunction||i->config_externallinkfunction);
1525 // if(i->config_reordertags)
1526 // swf_Optimize(i->swf);
1529 int swfresult_save(gfxresult_t*gfx, const char*filename)
1531 SWF*swf = (SWF*)gfx->internal;
1534 fi = open(filename, O_BINARY|O_CREAT|O_TRUNC|O_WRONLY, 0777);
1539 msg("<fatal> Could not create \"%s\". ", FIXNULL(filename));
1543 if FAILED(swf_WriteSWF(fi,swf))
1544 msg("<error> WriteSWF() failed.\n");
1550 void* swfresult_get(gfxresult_t*gfx, const char*name)
1552 SWF*swf = (SWF*)gfx->internal;
1553 if(!strcmp(name, "swf")) {
1554 return (void*)swf_CopySWF(swf);
1555 } else if(!strcmp(name, "xmin")) {
1556 return (void*)(ptroff_t)(swf->movieSize.xmin/20);
1557 } else if(!strcmp(name, "ymin")) {
1558 return (void*)(ptroff_t)(swf->movieSize.ymin/20);
1559 } else if(!strcmp(name, "xmax")) {
1560 return (void*)(ptroff_t)(swf->movieSize.xmax/20);
1561 } else if(!strcmp(name, "ymax")) {
1562 return (void*)(ptroff_t)(swf->movieSize.ymax/20);
1563 } else if(!strcmp(name, "width")) {
1564 return (void*)(ptroff_t)((swf->movieSize.xmax - swf->movieSize.xmin)/20);
1565 } else if(!strcmp(name, "height")) {
1566 return (void*)(ptroff_t)((swf->movieSize.ymax - swf->movieSize.ymin)/20);
1570 void swfresult_destroy(gfxresult_t*gfx)
1573 swf_FreeTags((SWF*)gfx->internal);
1574 free(gfx->internal);
1577 memset(gfx, 0, sizeof(gfxresult_t));
1581 static void swfoutput_destroy(gfxdevice_t* dev);
1583 gfxresult_t* swf_finish(gfxdevice_t* dev)
1585 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1588 if(i->config_linktarget) {
1589 free(i->config_linktarget);
1590 i->config_linktarget = 0;
1593 swfoutput_finalize(dev);
1594 SWF* swf = i->swf;i->swf = 0;
1595 swfoutput_destroy(dev);
1597 result = (gfxresult_t*)rfx_calloc(sizeof(gfxresult_t));
1598 result->internal = swf;
1599 result->save = swfresult_save;
1601 result->get = swfresult_get;
1602 result->destroy = swfresult_destroy;
1606 /* Perform cleaning up */
1607 static void swfoutput_destroy(gfxdevice_t* dev)
1609 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1611 /* not initialized yet- nothing to destroy */
1615 fontlist_t *tmp,*iterator = i->fontlist;
1617 if(iterator->swffont) {
1618 swf_FontFree(iterator->swffont);iterator->swffont=0;
1621 iterator = iterator->next;
1624 if(i->swf) {swf_FreeTags(i->swf);free(i->swf);i->swf = 0;}
1627 memset(dev, 0, sizeof(gfxdevice_t));
1630 static void swfoutput_setstrokecolor(gfxdevice_t* dev, U8 r, U8 g, U8 b, U8 a)
1632 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1633 if(i->strokergb.r == r &&
1634 i->strokergb.g == g &&
1635 i->strokergb.b == b &&
1636 i->strokergb.a == a) return;
1646 //#define ROUND_UP 19
1647 //#define ROUND_UP 10
1649 static void swfoutput_setlinewidth(gfxdevice_t*dev, double _linewidth)
1651 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1652 if(i->linewidth == (U16)(_linewidth*20+19.0/20.0))
1656 i->linewidth = (U16)(_linewidth*20+19.0/20.0);
1660 static void drawlink(gfxdevice_t*dev, ActionTAG*,ActionTAG*, gfxline_t*points, char mouseover, char*type, const char*url);
1661 static void swfoutput_namedlink(gfxdevice_t*dev, char*name, gfxline_t*points);
1662 static void swfoutput_linktopage(gfxdevice_t*dev, int page, gfxline_t*points);
1663 static void swfoutput_linktourl(gfxdevice_t*dev, const char*url, gfxline_t*points);
1665 /*void swfoutput_drawlink(gfxdevice_t*dev, char*url, gfxline_t*points)
1667 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1668 dev->drawlink(dev, points, url);
1671 void swf_drawlink(gfxdevice_t*dev, gfxline_t*points, const char*url)
1673 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1675 if(i->config_disablelinks)
1678 if(!strncmp("http://pdf2swf:", url, 15)) {
1679 char*tmp = strdup(url);
1680 int l = strlen(tmp);
1683 swfoutput_namedlink(dev, tmp+15, points);
1686 } else if(!strncmp("page", url, 4)) {
1689 if(url[t]<'0' || url[t]>'9')
1692 int page = atoi(&url[4]);
1693 if(page<0) page = 0;
1694 swfoutput_linktopage(dev, page, points);
1697 swfoutput_linktourl(dev, url, points);
1700 void swfoutput_linktourl(gfxdevice_t*dev, const char*url, gfxline_t*points)
1702 ActionTAG* actions = 0;
1703 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1709 /* TODO: escape special characters in url */
1711 if(i->config_externallinkfunction && i->config_flashversion<=8) {
1712 actions = action_PushString(actions, url); //parameter
1713 actions = action_PushInt(actions, 1); //number of parameters (1)
1714 actions = action_PushString(actions, i->config_externallinkfunction); //function name
1715 actions = action_CallFunction(actions);
1716 } else if(!i->config_linktarget) {
1717 if(!i->config_opennewwindow)
1718 actions = action_GetUrl(actions, url, "_parent");
1720 actions = action_GetUrl(actions, url, "_this");
1722 actions = action_GetUrl(actions, url, i->config_linktarget);
1724 actions = action_End(actions);
1726 drawlink(dev, actions, 0, points, 0, "url", url);
1728 swf_ActionFree(actions);
1730 void swfoutput_linktopage(gfxdevice_t*dev, int page, gfxline_t*points)
1732 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1733 ActionTAG* actions = 0;
1740 if(!i->config_internallinkfunction || i->config_flashversion>=9) {
1741 actions = action_GotoFrame(actions, page-1);
1742 actions = action_End(actions);
1744 actions = action_PushInt(actions, page); //parameter
1745 actions = action_PushInt(actions, 1); //number of parameters (1)
1746 actions = action_PushString(actions, i->config_internallinkfunction); //function name
1747 actions = action_CallFunction(actions);
1748 actions = action_End(actions);
1752 sprintf(name, "page%d", page);
1754 drawlink(dev, actions, 0, points, 0, "page", name);
1756 swf_ActionFree(actions);
1759 /* Named Links (a.k.a. Acrobatmenu) are used to implement various gadgets
1760 of the viewer objects, like subtitles, index elements etc.
1762 void swfoutput_namedlink(gfxdevice_t*dev, char*name, gfxline_t*points)
1764 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1765 ActionTAG *actions1,*actions2;
1766 char*tmp = strdup(name);
1775 if(!strncmp(tmp, "call:", 5))
1777 char*x = strchr(&tmp[5], ':');
1779 actions1 = action_PushInt(0, 0); //number of parameters (0)
1780 actions1 = action_PushString(actions1, &tmp[5]); //function name
1781 actions1 = action_CallFunction(actions1);
1782 actions1 = action_End(actions1);
1785 actions1 = action_PushString(0, x+1); //parameter
1786 actions1 = action_PushInt(actions1, 1); //number of parameters (1)
1787 actions1 = action_PushString(actions1, &tmp[5]); //function name
1788 actions1 = action_CallFunction(actions1);
1789 actions1 = action_End(actions1);
1791 actions2 = action_End(0);
1797 actions1 = action_PushString(0, "/:subtitle");
1798 actions1 = action_PushString(actions1, name);
1799 actions1 = action_SetVariable(actions1);
1800 actions1 = action_End(actions1);
1802 actions2 = action_PushString(0, "/:subtitle");
1803 actions2 = action_PushString(actions2, "");
1804 actions2 = action_SetVariable(actions2);
1805 actions2 = action_End(actions2);
1809 drawlink(dev, actions1, actions2, points, mouseover, type, name);
1811 swf_ActionFree(actions1);
1812 swf_ActionFree(actions2);
1816 static void drawgfxline(gfxdevice_t*dev, gfxline_t*line, int fill)
1818 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1819 gfxcoord_t lastx=0,lasty=0,px=0,py=0;
1821 int lines= 0, splines=0;
1828 /* check whether the next segment is zero */
1829 if(line->type == gfx_moveTo) {
1830 moveto(dev, i->tag, line->x, line->y);
1831 px = lastx = line->x;
1832 py = lasty = line->y;
1834 } if(line->type == gfx_lineTo) {
1835 lineto(dev, i->tag, line->x, line->y);
1840 } else if(line->type == gfx_splineTo) {
1842 s.x = line->sx;p.x = line->x;
1843 s.y = line->sy;p.y = line->y;
1844 splineto(dev, i->tag, s, p);
1852 msg("<trace> drawgfxline, %d lines, %d splines", lines, splines);
1856 static void drawlink(gfxdevice_t*dev, ActionTAG*actions1, ActionTAG*actions2, gfxline_t*points, char mouseover, char*type, const char*url)
1858 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1867 int buttonid = getNewID(dev);
1868 gfxbbox_t bbox = gfxline_getbbox(points);
1870 if(i->config_linknameurl) {
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);
1961 const char* name = 0;
1962 if(i->config_linknameurl) {
1963 buf2 = malloc(strlen(type)+strlen(url)+2);
1964 sprintf(buf2, "%s:%s", type, url);
1968 sprintf(buf, "button%d", buttonid);
1971 msg("<trace> Placing link ID %d", buttonid);
1972 i->tag = swf_InsertTag(i->tag,ST_PLACEOBJECT2);
1974 if(posx!=0 || posy!=0) {
1976 p.x = (int)(posx*20);
1977 p.y = (int)(posy*20);
1978 p = swf_TurnPoint(p, &i->page_matrix);
1983 swf_ObjectPlace(i->tag, buttonid, getNewDepth(dev),&m,0,(U8*)name);
1985 swf_ObjectPlace(i->tag, buttonid, getNewDepth(dev),&i->page_matrix,0,(U8*)name);
1995 for(t=0;t<picpos;t++)
1997 if(pic_xids[t] == xid &&
1998 pic_yids[t] == yid) {
1999 width = pic_width[t];
2000 height = pic_height[t];
2004 pic_ids[picpos] = swfoutput_drawimagelosslessN(&output, pic, pal, width, height, x1,y1,x2,y2,x3,y3,x4,y4, numpalette);
2005 pic_xids[picpos] = xid;
2006 pic_yids[picpos] = yid;
2007 pic_width[picpos] = width;
2008 pic_height[picpos] = height;
2011 pic[width*y+x] = buf[0];
2015 xid += pal[1].r*3 + pal[1].g*11 + pal[1].b*17;
2016 yid += pal[1].r*7 + pal[1].g*5 + pal[1].b*23;
2020 xid += x*r+x*b*3+x*g*7+x*a*11;
2021 yid += y*r*3+y*b*17+y*g*19+y*a*11;
2023 for(t=0;t<picpos;t++)
2025 if(pic_xids[t] == xid &&
2026 pic_yids[t] == yid) {
2035 int swf_setparameter(gfxdevice_t*dev, const char*name, const char*value)
2037 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
2039 msg("<trace> swfdevice: %s=%s", name, value);
2040 if(!strcmp(name, "jpegsubpixels")) {
2041 i->config_jpegsubpixels = atof(value);
2042 } else if(!strcmp(name, "ppmsubpixels")) {
2043 i->config_ppmsubpixels = atof(value);
2044 } else if(!strcmp(name, "subpixels")) {
2045 i->config_ppmsubpixels = i->config_jpegsubpixels = atof(value);
2046 } else if(!strcmp(name, "drawonlyshapes")) {
2047 i->config_drawonlyshapes = atoi(value);
2048 } else if(!strcmp(name, "ignoredraworder")) {
2049 i->config_ignoredraworder = atoi(value);
2050 } else if(!strcmp(name, "mark")) {
2051 if(!value || !value[0]) {
2052 if(i->mark) free(i->mark);
2056 i->mark = strdup("...");
2057 for(t=0;t<3;t++) if(value[t]) i->mark[t] = value[t];
2059 } else if(!strcmp(name, "filloverlap")) {
2060 i->config_filloverlap = atoi(value);
2061 } else if(!strcmp(name, "linksopennewwindow")) {
2062 i->config_opennewwindow = atoi(value);
2063 } else if(!strcmp(name, "opennewwindow")) {
2064 i->config_opennewwindow = atoi(value);
2065 } else if(!strcmp(name, "storeallcharacters")) {
2066 i->config_storeallcharacters = atoi(value);
2067 } else if(!strcmp(name, "enablezlib")) {
2068 i->config_enablezlib = atoi(value);
2069 } else if(!strcmp(name, "bboxvars")) {
2070 i->config_bboxvars = atoi(value);
2071 } else if(!strcmp(name, "dots")) {
2072 i->config_dots = atoi(value);
2073 } else if(!strcmp(name, "frameresets")) {
2074 i->config_frameresets = atoi(value);
2075 } else if(!strcmp(name, "showclipshapes")) {
2076 i->config_showclipshapes = atoi(value);
2077 } else if(!strcmp(name, "reordertags")) {
2078 i->config_reordertags = atoi(value);
2079 } else if(!strcmp(name, "internallinkfunction")) {
2080 i->config_internallinkfunction = strdup(value);
2081 } else if(!strcmp(name, "externallinkfunction")) {
2082 i->config_externallinkfunction = strdup(value);
2083 } else if(!strcmp(name, "linkfunction")) { //sets both internallinkfunction and externallinkfunction
2084 i->config_internallinkfunction = strdup(value);
2085 i->config_externallinkfunction = strdup(value);
2086 } else if(!strcmp(name, "disable_polygon_conversion")) {
2087 i->config_disable_polygon_conversion = atoi(value);
2088 } else if(!strcmp(name, "normalize_polygon_positions")) {
2089 i->config_normalize_polygon_positions = atoi(value);
2090 } else if(!strcmp(name, "wxwindowparams")) {
2091 i->config_watermark = atoi(value);
2092 } else if(!strcmp(name, "insertstop")) {
2093 i->config_insertstoptag = atoi(value);
2094 } else if(!strcmp(name, "protect")) {
2095 i->config_protect = atoi(value);
2096 if(i->config_protect && i->tag) {
2097 i->tag = swf_InsertTag(i->tag, ST_PROTECT);
2099 } else if(!strcmp(name, "flashversion")) {
2100 i->config_flashversion = atoi(value);
2102 i->swf->fileVersion = i->config_flashversion;
2104 } else if(!strcmp(name, "framerate")) {
2105 i->config_framerate = atof(value);
2107 i->swf->frameRate = i->config_framerate*0x100;
2109 } else if(!strcmp(name, "minlinewidth")) {
2110 i->config_minlinewidth = atof(value);
2111 } else if(!strcmp(name, "caplinewidth")) {
2112 i->config_caplinewidth = atof(value);
2113 } else if(!strcmp(name, "linktarget")) {
2114 i->config_linktarget = strdup(value);
2115 } else if(!strcmp(name, "invisibletexttofront")) {
2116 i->config_invisibletexttofront = atoi(value);
2117 } else if(!strcmp(name, "noclips")) {
2118 i->config_noclips = atoi(value);
2119 } else if(!strcmp(name, "dumpfonts")) {
2120 i->config_dumpfonts = atoi(value);
2121 } else if(!strcmp(name, "animate")) {
2122 i->config_animate = atoi(value);
2123 } else if(!strcmp(name, "disablelinks")) {
2124 i->config_disablelinks = atoi(value);
2125 } else if(!strcmp(name, "simpleviewer")) {
2126 i->config_simpleviewer = atoi(value);
2127 } else if(!strcmp(name, "next_bitmap_is_jpeg")) {
2129 } else if(!strcmp(name, "jpegquality")) {
2130 int val = atoi(value);
2132 if(val>101) val=101;
2133 i->config_jpegquality = val;
2134 } else if(!strcmp(name, "splinequality")) {
2135 int v = atoi(value);
2136 v = 500-(v*5); // 100% = 0.25 pixel, 0% = 25 pixel
2138 i->config_splinemaxerror = v;
2139 } else if(!strcmp(name, "fontquality")) {
2140 int v = atoi(value);
2141 v = 500-(v*5); // 100% = 0.25 pixel, 0% = 25 pixel
2143 i->config_fontsplinemaxerror = v;
2144 } else if(!strcmp(name, "linkcolor")) {
2145 if(strlen(value)!=8) {
2146 fprintf(stderr, "Unknown format for option 'linkcolor'. (%s <-> RRGGBBAA)\n", value);
2149 # define NIBBLE(s) (((s)>='0' && (s)<='9')?((s)-'0'):((s)&0x0f)+9)
2150 i->config_linkcolor.r = NIBBLE(value[0])<<4 | NIBBLE(value[1]);
2151 i->config_linkcolor.g = NIBBLE(value[2])<<4 | NIBBLE(value[3]);
2152 i->config_linkcolor.b = NIBBLE(value[4])<<4 | NIBBLE(value[5]);
2153 i->config_linkcolor.a = NIBBLE(value[6])<<4 | NIBBLE(value[7]);
2154 } else if(!strcmp(name, "help")) {
2155 printf("\nSWF layer options:\n");
2156 printf("jpegsubpixels=<pixels> resolution adjustment for jpeg images (same as jpegdpi, but in pixels)\n");
2157 printf("ppmsubpixels=<pixels resolution adjustment for lossless images (same as ppmdpi, but in pixels)\n");
2158 printf("subpixels=<pixels> shortcut for setting both jpegsubpixels and ppmsubpixels\n");
2159 printf("drawonlyshapes convert everything to shapes (currently broken)\n");
2160 printf("ignoredraworder allow to perform a few optimizations for creating smaller SWFs\n");
2161 printf("linksopennewwindow make links open a new browser window\n");
2162 printf("linktarget target window name of new links\n");
2163 printf("linkcolor=<color) color of links (format: RRGGBBAA)\n");
2164 printf("linknameurl Link buttons will be named like the URL they refer to (handy for iterating through links with actionscript)\n");
2165 printf("storeallcharacters don't reduce the fonts to used characters in the output file\n");
2166 printf("enablezlib switch on zlib compression (also done if flashversion>=6)\n");
2167 printf("bboxvars store the bounding box of the SWF file in actionscript variables\n");
2168 printf("dots Take care to handle dots correctly\n");
2169 printf("reordertags=0/1 (default: 1) perform some tag optimizations\n");
2170 printf("internallinkfunction=<name> when the user clicks a internal link (to a different page) in the converted file, this actionscript function is called\n");
2171 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");
2172 printf("disable_polygon_conversion never convert strokes to polygons (will remove capstyles and joint styles)\n");
2173 printf("caplinewidth=<width> the minimum thichness a line needs to have so that capstyles become visible (and are converted)\n");
2174 printf("insertstop put an ActionScript \"STOP\" tag in every frame\n");
2175 printf("protect add a \"protect\" tag to the file, to prevent loading in the Flash editor\n");
2176 printf("flashversion=<version> the SWF fileversion (6)\n");
2177 printf("framerate=<fps> SWF framerate\n");
2178 printf("minlinewidth=<width> convert horizontal/vertical boxes smaller than this width to lines (0.05) \n");
2179 printf("simpleviewer Add next/previous buttons to the SWF\n");
2180 printf("animate insert a showframe tag after each placeobject (animate draw order of PDF files)\n");
2181 printf("jpegquality=<quality> set compression quality of jpeg images\n");
2182 printf("splinequality=<value> Set the quality of spline convertion to value (0-100, default: 100).\n");
2183 printf("disablelinks Disable links.\n");
2190 // --------------------------------------------------------------------
2192 static CXFORM gfxcxform_to_cxform(gfxcxform_t* c)
2195 swf_GetCXForm(0, &cx, 1);
2198 if(c->rg!=0 || c->rb!=0 || c->ra!=0 ||
2199 c->gr!=0 || c->gb!=0 || c->ga!=0 ||
2200 c->br!=0 || c->bg!=0 || c->ba!=0 ||
2201 c->ar!=0 || c->ag!=0 || c->ab!=0)
2202 msg("<warning> CXForm not SWF-compatible");
2204 cx.a0 = (S16)(c->aa*256);
2205 cx.r0 = (S16)(c->rr*256);
2206 cx.g0 = (S16)(c->gg*256);
2207 cx.b0 = (S16)(c->bb*256);
2216 static int imageInCache(gfxdevice_t*dev, void*data, int width, int height)
2220 static void addImageToCache(gfxdevice_t*dev, void*data, int width, int height)
2224 static int add_image(swfoutput_internal*i, gfximage_t*img, int targetwidth, int targetheight, int* newwidth, int* newheight)
2226 gfxdevice_t*dev = i->dev;
2228 RGBA*mem = (RGBA*)img->data;
2230 int sizex = img->width;
2231 int sizey = img->height;
2232 int is_jpeg = i->jpeg;
2235 int newsizex=sizex, newsizey=sizey;
2238 if(is_jpeg && i->config_jpegsubpixels) {
2239 newsizex = (int)(targetwidth*i->config_jpegsubpixels + 0.5);
2240 newsizey = (int)(targetheight*i->config_jpegsubpixels + 0.5);
2241 } else if(!is_jpeg && i->config_ppmsubpixels) {
2242 newsizex = (int)(targetwidth*i->config_ppmsubpixels + 0.5);
2243 newsizey = (int)(targetheight*i->config_ppmsubpixels + 0.5);
2247 if(sizex<=0 || sizey<=0)
2254 /* TODO: cache images */
2256 if(newsizex<sizex || newsizey<sizey) {
2257 msg("<verbose> Scaling %dx%d image to %dx%d", sizex, sizey, newsizex, newsizey);
2258 newpic = swf_ImageScale(mem, sizex, sizey, newsizex, newsizey);
2259 *newwidth = sizex = newsizex;
2260 *newheight = sizey = newsizey;
2263 *newwidth = newsizex = sizex;
2264 *newheight = newsizey = sizey;
2267 int num_colors = swf_ImageGetNumberOfPaletteEntries(mem,sizex,sizey,0);
2268 int has_alpha = swf_ImageHasAlpha(mem,sizex,sizey);
2270 msg("<verbose> Drawing %dx%d %s%simage (id %d) at size %dx%d (%dx%d), %s%d colors",
2272 has_alpha?(has_alpha==2?"semi-transparent ":"transparent "):"",
2273 is_jpeg?"jpeg-":"", i->currentswfid+1,
2275 targetwidth, targetheight,
2276 /*newsizex, newsizey,*/
2277 num_colors>256?">":"", num_colors>256?256:num_colors);
2279 /*RGBA* pal = (RGBA*)rfx_alloc(sizeof(RGBA)*num_colors);
2280 swf_ImageGetNumberOfPaletteEntries(mem,sizex,sizey,pal);
2282 for(t=0;t<num_colors;t++) {
2283 printf("%02x%02x%02x%02x ",
2284 pal[t].r, pal[t].g, pal[t].b, pal[t].a);
2291 int cacheid = imageInCache(dev, mem, sizex, sizey);
2294 bitid = getNewID(dev);
2296 i->tag = swf_AddImage(i->tag, bitid, mem, sizex, sizey, i->config_jpegquality);
2297 addImageToCache(dev, mem, sizex, sizey);
2307 static SRECT gfxline_getSWFbbox(gfxline_t*line)
2309 gfxbbox_t bbox = gfxline_getbbox(line);
2311 r.xmin = (int)(bbox.xmin*20);
2312 r.ymin = (int)(bbox.ymin*20);
2313 r.xmax = (int)(bbox.xmax*20);
2314 r.ymax = (int)(bbox.ymax*20);
2318 int line_is_empty(gfxline_t*line)
2321 if(line->type != gfx_moveTo)
2328 static void swf_fillbitmap(gfxdevice_t*dev, gfxline_t*line, gfximage_t*img, gfxmatrix_t*matrix, gfxcxform_t*cxform)
2330 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
2332 if(line_is_empty(line))
2338 int targetx = (int)(sqrt(matrix->m00*matrix->m00 + matrix->m01*matrix->m01)*img->width);
2339 int targety = (int)(sqrt(matrix->m10*matrix->m10 + matrix->m11*matrix->m11)*img->height);
2341 int newwidth=0,newheight=0;
2342 int bitid = add_image(i, img, targetx, targety, &newwidth, &newheight);
2345 double fx = (double)img->width / (double)newwidth;
2346 double fy = (double)img->height / (double)newheight;
2349 m.sx = (int)(65536*20*matrix->m00*fx); m.r1 = (int)(65536*20*matrix->m10*fy);
2350 m.r0 = (int)(65536*20*matrix->m01*fx); m.sy = (int)(65536*20*matrix->m11*fy);
2351 m.tx = (int)(matrix->tx*20);
2352 m.ty = (int)(matrix->ty*20);
2355 int myshapeid = getNewID(dev);
2356 i->tag = swf_InsertTag(i->tag,ST_DEFINESHAPE);
2358 swf_ShapeNew(&shape);
2359 int fsid = swf_ShapeAddBitmapFillStyle(shape,&m,bitid,1);
2360 swf_SetU16(i->tag, myshapeid);
2361 SRECT r = gfxline_getSWFbbox(line);
2362 r = swf_ClipRect(i->pagebbox, r);
2363 swf_SetRect(i->tag,&r);
2364 swf_SetShapeStyles(i->tag,shape);
2365 swf_ShapeCountBits(shape,NULL,NULL);
2366 swf_SetShapeBits(i->tag,shape);
2367 swf_ShapeSetAll(i->tag,shape,UNDEFINED_COORD,UNDEFINED_COORD,0,fsid,0);
2368 i->swflastx = i->swflasty = UNDEFINED_COORD;
2369 drawgfxline(dev, line, 1);
2370 swf_ShapeSetEnd(i->tag);
2371 swf_ShapeFree(shape);
2373 msg("<trace> Placing image, shape ID %d, bitmap ID %d", myshapeid, bitid);
2374 i->tag = swf_InsertTag(i->tag,ST_PLACEOBJECT2);
2375 CXFORM cxform2 = gfxcxform_to_cxform(cxform);
2376 swf_ObjectPlace(i->tag,myshapeid,getNewDepth(dev),&i->page_matrix,&cxform2,NULL);
2379 static RGBA col_black = {255,0,0,0};
2381 static void drawoutline(gfxdevice_t*dev, gfxline_t*line)
2383 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
2385 int myshapeid = getNewID(dev);
2386 i->tag = swf_InsertTag(i->tag,ST_DEFINESHAPE3);
2389 swf_ShapeNew(&shape);
2390 int lsid = swf_ShapeAddLineStyle(shape,1,&col_black);
2392 swf_SetU16(i->tag,myshapeid);
2393 SRECT r = gfxline_getSWFbbox(line);
2394 r = swf_ClipRect(i->pagebbox, r);
2395 swf_SetRect(i->tag,&r);
2396 swf_SetShapeStyles(i->tag,shape);
2397 swf_ShapeCountBits(shape,NULL,NULL);
2398 swf_SetShapeBits(i->tag,shape);
2399 swf_ShapeSetAll(i->tag,shape,UNDEFINED_COORD,UNDEFINED_COORD,lsid,0,0);
2400 drawgfxline(dev, line, 1);
2401 swf_ShapeSetEnd(i->tag);
2402 swf_ShapeFree(shape);
2404 i->tag = swf_InsertTag(i->tag,ST_PLACEOBJECT2);
2405 swf_ObjectPlace(i->tag, myshapeid, getNewDepth(dev), 0,0,0);
2408 static void swf_startclip(gfxdevice_t*dev, gfxline_t*line)
2410 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
2411 if(i->config_noclips)
2417 if(i->clippos >= 127)
2419 msg("<warning> Too many clip levels.");
2423 if(i->config_showclipshapes)
2424 drawoutline(dev, line);
2426 int myshapeid = getNewID(dev);
2427 i->tag = swf_InsertTag(i->tag,ST_DEFINESHAPE3);
2429 memset(&col, 0, sizeof(RGBA));
2432 swf_ShapeNew(&shape);
2433 int fsid = swf_ShapeAddSolidFillStyle(shape,&col);
2435 RGBA markcol = {0,i->mark[0],i->mark[1],i->mark[2]};
2436 swf_ShapeAddSolidFillStyle(shape,&markcol);
2438 swf_SetU16(i->tag,myshapeid);
2439 SRECT r = gfxline_getSWFbbox(line);
2440 r = swf_ClipRect(i->pagebbox, r);
2441 swf_SetRect(i->tag,&r);
2442 swf_SetShapeStyles(i->tag,shape);
2443 swf_ShapeCountBits(shape,NULL,NULL);
2444 swf_SetShapeBits(i->tag,shape);
2445 swf_ShapeSetAll(i->tag,shape,UNDEFINED_COORD,UNDEFINED_COORD,0,fsid,0);
2446 i->swflastx = i->swflasty = UNDEFINED_COORD;
2447 i->shapeisempty = 1;
2448 drawgfxline(dev, line, 1);
2449 if(i->shapeisempty) {
2450 /* an empty clip shape is equivalent to a shape with no area */
2451 int x = line?line->x:0;
2452 int y = line?line->y:0;
2453 moveto(dev, i->tag, x,y);
2454 lineto(dev, i->tag, x,y);
2455 lineto(dev, i->tag, x,y);
2457 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)) {
2458 if(i->config_watermark) {
2459 gfxbbox_t r; r.xmin = r.ymin = 0;r.xmax = i->max_x;r.ymax = i->max_y;
2460 draw_watermark(dev, r, 1);
2463 swf_ShapeSetEnd(i->tag);
2464 swf_ShapeFree(shape);
2466 /* TODO: remember the bbox, and check all shapes against it */
2468 msg("<trace> Placing clip ID %d", myshapeid);
2469 i->tag = swf_InsertTag(i->tag,ST_PLACEOBJECT2);
2470 i->cliptags[i->clippos] = i->tag;
2471 i->clipshapes[i->clippos] = myshapeid;
2472 i->clipdepths[i->clippos] = getNewDepth(dev);
2476 static void swf_endclip(gfxdevice_t*dev)
2478 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
2479 if(i->config_noclips)
2487 msg("<error> Invalid end of clipping region");
2491 /*swf_ObjectPlaceClip(i->cliptags[i->clippos],i->clipshapes[i->clippos],i->clipdepths[i->clippos],&i->page_matrix,NULL,NULL,
2492 / * clip to depth: * / i->depth <= i->clipdepths[i->clippos]? i->depth : i->depth - 1);
2494 swf_ObjectPlaceClip(i->cliptags[i->clippos],i->clipshapes[i->clippos],i->clipdepths[i->clippos],&i->page_matrix,NULL,NULL,i->depth);
2496 static int gfxline_type(gfxline_t*line)
2502 int haszerosegments=0;
2505 if(line->type == gfx_moveTo) {
2508 } else if(line->type == gfx_lineTo) {
2512 } else if(line->type == gfx_splineTo) {
2514 if(tmpsplines>lines)
2522 if(lines==0 && splines==0) return 0;
2523 else if(lines==1 && splines==0) return 1;
2524 else if(lines==0 && splines==1) return 2;
2525 else if(splines==0) return 3;
2529 static int gfxline_has_dots(gfxline_t*line)
2537 if(line->type == gfx_moveTo) {
2538 /* test the length of the preceding line, and assume it is a dot if
2539 it's length is less than 1.0. But *only* if there's a noticable
2540 gap between the previous line and the next moveTo. (I've come
2541 across a PDF where thousands of "dots" were stringed together,
2543 int last_short_gap = short_gap;
2544 if((fabs(line->x - x) + fabs(line->y - y)) < 1.0) {
2549 if(isline && dist < 1 && !short_gap && !last_short_gap) {
2554 } else if(line->type == gfx_lineTo) {
2555 dist += fabs(line->x - x) + fabs(line->y - y);
2557 } else if(line->type == gfx_splineTo) {
2558 dist += fabs(line->sx - x) + fabs(line->sy - y) +
2559 fabs(line->x - line->sx) + fabs(line->y - line->sy);
2566 if(isline && dist < 1 && !short_gap) {
2572 static int gfxline_fix_short_edges(gfxline_t*line)
2576 if(line->type == gfx_lineTo) {
2577 if(fabs(line->x - x) + fabs(line->y - y) < 0.01) {
2580 } else if(line->type == gfx_splineTo) {
2581 if(fabs(line->sx - x) + fabs(line->sy - y) +
2582 fabs(line->x - line->sx) + fabs(line->y - line->sy) < 0.01) {
2593 static char is_inside_page(gfxdevice_t*dev, gfxcoord_t x, gfxcoord_t y)
2595 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
2596 if(x<i->min_x || x>i->max_x) return 0;
2597 if(y<i->min_y || y>i->max_y) return 0;
2601 gfxline_t* gfxline_move(gfxline_t*line, double x, double y)
2603 gfxline_t*l = line = gfxline_clone(line);
2615 //#define NORMALIZE_POLYGON_POSITIONS
2617 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)
2619 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
2620 if(line_is_empty(line))
2622 int type = gfxline_type(line);
2623 int has_dots = gfxline_has_dots(line);
2624 gfxbbox_t r = gfxline_getbbox(line);
2625 int is_outside_page = !is_inside_page(dev, r.xmin, r.ymin) || !is_inside_page(dev, r.xmax, r.ymax);
2627 /* TODO: * split line into segments, and perform this check for all segments */
2629 if(i->config_disable_polygon_conversion || /*type>=5 ||*/
2631 (width <= i->config_caplinewidth
2632 || (cap_style == gfx_capRound && joint_style == gfx_joinRound)
2633 || (cap_style == gfx_capRound && type<=2))))
2637 /* convert line to polygon */
2638 msg("<trace> draw as polygon, type=%d dots=%d", type, has_dots);
2640 gfxline_fix_short_edges(line);
2641 /* we need to convert the line into a polygon */
2642 gfxpoly_t* poly = gfxpoly_from_stroke(line, width, cap_style, joint_style, miterLimit, DEFAULT_GRID);
2643 gfxline_t*gfxline = gfxline_from_gfxpoly(poly);
2644 dev->fill(dev, gfxline, color);
2645 gfxline_free(gfxline);
2646 gfxpoly_destroy(poly);
2650 msg("<trace> draw as stroke, type=%d dots=%d", type, has_dots);
2653 if(i->config_normalize_polygon_positions) {
2655 double startx = 0, starty = 0;
2656 if(line && line->type == gfx_moveTo) {
2660 line = gfxline_move(line, -startx, -starty);
2661 i->shapeposx = (int)(startx*20);
2662 i->shapeposy = (int)(starty*20);
2665 swfoutput_setstrokecolor(dev, color->r, color->g, color->b, color->a);
2666 swfoutput_setlinewidth(dev, width);
2669 drawgfxline(dev, line, 0);
2671 if(i->config_normalize_polygon_positions) {
2672 free(line); //account for _move
2677 static void swf_fill(gfxdevice_t*dev, gfxline_t*line, gfxcolor_t*color)
2679 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
2680 if(line_is_empty(line))
2684 gfxbbox_t r = gfxline_getbbox(line);
2685 int is_outside_page = !is_inside_page(dev, r.xmin, r.ymin) || !is_inside_page(dev, r.xmax, r.ymax);
2689 if(!i->config_ignoredraworder)
2692 if(i->config_normalize_polygon_positions) {
2694 double startx = 0, starty = 0;
2695 if(line && line->type == gfx_moveTo) {
2699 line = gfxline_move(line, -startx, -starty);
2700 i->shapeposx = (int)(startx*20);
2701 i->shapeposy = (int)(starty*20);
2704 swfoutput_setfillcolor(dev, color->r, color->g, color->b, color->a);
2707 drawgfxline(dev, line, 1);
2709 if(i->currentswfid==2 && r.xmin==0 && r.ymin==0 && r.xmax==i->max_x && r.ymax==i->max_y) {
2710 if(i->config_watermark) {
2711 draw_watermark(dev, r, 1);
2715 msg("<trace> end of swf_fill (shapeid=%d)", i->shapeid);
2717 if(i->config_normalize_polygon_positions) {
2718 free(line); //account for _move
2722 static GRADIENT* gfxgradient_to_GRADIENT(gfxgradient_t*gradient)
2725 gfxgradient_t*g = gradient;
2730 GRADIENT* swfgradient = malloc(sizeof(GRADIENT));
2731 swfgradient->num = num;
2732 swfgradient->rgba = malloc(sizeof(swfgradient->rgba[0])*num);
2733 swfgradient->ratios = malloc(sizeof(swfgradient->ratios[0])*num);
2738 swfgradient->ratios[num] = g->pos*255;
2739 swfgradient->rgba[num] = *(RGBA*)&g->color;
2746 static void swf_fillgradient(gfxdevice_t*dev, gfxline_t*line, gfxgradient_t*gradient, gfxgradienttype_t type, gfxmatrix_t*matrix)
2748 if(line_is_empty(line))
2750 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
2752 if(line_is_empty(line))
2755 GRADIENT* swfgradient = gfxgradient_to_GRADIENT(gradient);
2762 double f = type==gfxgradient_radial?4:4;
2764 m.sx = (int)(matrix->m00*20*f); m.r1 = (int)(matrix->m10*20*f);
2765 m.r0 = (int)(matrix->m01*20*f); m.sy = (int)(matrix->m11*20*f);
2766 m.tx = (int)(matrix->tx*20);
2767 m.ty = (int)(matrix->ty*20);
2770 int myshapeid = getNewID(dev);
2771 i->tag = swf_InsertTag(i->tag, ST_DEFINESHAPE2);
2773 swf_ShapeNew(&shape);
2774 int fsid = swf_ShapeAddGradientFillStyle(shape,&m,swfgradient,type==gfxgradient_radial);
2775 swf_SetU16(i->tag, myshapeid);
2776 SRECT r = gfxline_getSWFbbox(line);
2777 r = swf_ClipRect(i->pagebbox, r);
2778 swf_SetRect(i->tag,&r);
2779 swf_SetShapeStyles(i->tag,shape);
2780 swf_ShapeCountBits(shape,NULL,NULL);
2781 swf_SetShapeBits(i->tag,shape);
2782 swf_ShapeSetAll(i->tag,shape,UNDEFINED_COORD,UNDEFINED_COORD,0,fsid,0);
2783 i->swflastx = i->swflasty = UNDEFINED_COORD;
2784 drawgfxline(dev, line, 1);
2785 swf_ShapeSetEnd(i->tag);
2786 swf_ShapeFree(shape);
2788 int depth = getNewDepth(dev);
2789 msg("<trace> Placing gradient, shape ID %d, depth %d", myshapeid, depth);
2790 i->tag = swf_InsertTag(i->tag,ST_PLACEOBJECT2);
2791 swf_ObjectPlace(i->tag,myshapeid,depth,&i->page_matrix,NULL,NULL);
2793 swf_FreeGradient(swfgradient);free(swfgradient);
2796 static SWFFONT* gfxfont_to_swffont(gfxfont_t*font, const char* id, int version)
2798 SWFFONT*swffont = (SWFFONT*)rfx_calloc(sizeof(SWFFONT));
2800 SRECT bounds = {0,0,0,0};
2802 swffont->version = version;
2803 swffont->name = (U8*)strdup(id);
2804 swffont->layout = (SWFLAYOUT*)rfx_calloc(sizeof(SWFLAYOUT));
2805 swffont->layout->ascent = 0;
2806 swffont->layout->descent = 0;
2807 swffont->layout->leading = 0;
2808 swffont->layout->bounds = (SRECT*)rfx_calloc(sizeof(SRECT)*font->num_glyphs);
2809 swffont->encoding = FONT_ENCODING_UNICODE;
2810 swffont->numchars = font->num_glyphs;
2811 swffont->maxascii = font->max_unicode;
2812 swffont->ascii2glyph = (int*)rfx_calloc(sizeof(int)*swffont->maxascii);
2813 swffont->glyph2ascii = (U16*)rfx_calloc(sizeof(U16)*swffont->numchars);
2814 swffont->glyph = (SWFGLYPH*)rfx_calloc(sizeof(SWFGLYPH)*swffont->numchars);
2815 swffont->glyphnames = (char**)rfx_calloc(sizeof(char*)*swffont->numchars);
2817 SRECT max = {0,0,0,0};
2818 for(t=0;t<font->num_glyphs;t++) {
2822 int u = font->glyphs[t].unicode;
2825 for(s=0;s<font->num_glyphs;s++) {
2826 if(swffont->glyph2ascii[s]==u)
2829 if(u >= 0xe000 || u == 0x0000 || twice) {
2830 /* flash 8 flashtype requires unique unicode IDs for each character.
2831 We use the Unicode private user area to assign characters, hoping that
2832 the font doesn't contain more than 2048 glyphs */
2833 u = 0xe000 + (t&0x1fff);
2835 swffont->glyph2ascii[t] = u;
2837 if(font->glyphs[t].name) {
2838 swffont->glyphnames[t] = strdup(font->glyphs[t].name);
2840 swffont->glyphnames[t] = 0;
2842 advance = font->glyphs[t].advance;
2844 swf_Shape01DrawerInit(&draw, 0);
2845 line = font->glyphs[t].line;
2847 const double scale = GLYPH_SCALE;
2850 c.x = line->sx * scale; c.y = -line->sy * scale;
2851 //to.x = floor(line->x * scale); to.y = floor(-line->y * scale);
2852 to.x = line->x * scale; to.y = -line->y * scale;
2854 /*if(strstr(swffont->name, "BIRNU") && t==90) {
2858 if(line->type == gfx_moveTo) {
2859 draw.moveTo(&draw, &to);
2860 } else if(line->type == gfx_lineTo) {
2861 draw.lineTo(&draw, &to);
2862 } else if(line->type == gfx_splineTo) {
2863 draw.splineTo(&draw, &c, &to);
2868 swffont->glyph[t].shape = swf_ShapeDrawerToShape(&draw);
2870 SRECT bbox = swf_ShapeDrawerGetBBox(&draw);
2871 swf_ExpandRect2(&max, &bbox);
2873 swffont->layout->bounds[t] = bbox;
2875 if(advance<32768.0/20) {
2876 swffont->glyph[t].advance = (int)(advance*20);
2878 //msg("<warning> Advance value overflow in glyph %d", t);
2879 swffont->glyph[t].advance = 32767;
2882 draw.dealloc(&draw);
2884 swf_ExpandRect2(&bounds, &swffont->layout->bounds[t]);
2887 for(t=0;t<font->num_glyphs;t++) {
2888 SRECT bbox = swffont->layout->bounds[t];
2890 /* if the glyph doesn't have a bounding box, use the
2891 combined bounding box (necessary e.g. for space characters) */
2892 if(!(bbox.xmin|bbox.ymin|bbox.xmax|bbox.ymax)) {
2893 swffont->layout->bounds[t] = bbox = max;
2896 /* check that the advance value is reasonable, by comparing it
2897 with the bounding box */
2898 if(bbox.xmax>0 && (bbox.xmax*10 < swffont->glyph[t].advance || !swffont->glyph[t].advance)) {
2899 if(swffont->glyph[t].advance)
2900 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);
2901 swffont->glyph[t].advance = bbox.xmax;
2903 //swffont->glyph[t].advance = bbox.xmax - bbox.xmin;
2907 /* Flash player will use the advance value from the char, and the ascent/descent values
2908 from the layout for text selection.
2909 ascent will extend the char into negative y direction, from the baseline, while descent
2910 will extend in positive y direction, also from the baseline.
2911 The baseline is defined as the y-position zero
2914 swffont->layout->ascent = bounds.ymin<0?-bounds.ymin:0;
2915 swffont->layout->descent = bounds.ymax>0?bounds.ymax:0;
2916 swffont->layout->leading = bounds.ymax - bounds.ymin;
2918 /* if the font has proper ascent/descent values (>0) and those define
2919 greater line spacing that what we estimated from the bounding boxes,
2920 use the font's parameters */
2921 if(font->ascent*20 > swffont->layout->ascent)
2922 swffont->layout->ascent = font->ascent*20;
2923 if(font->descent*20 > swffont->layout->descent)
2924 swffont->layout->descent = font->descent*20;
2926 swf_FontSort(swffont);
2930 static void swf_addfont(gfxdevice_t*dev, gfxfont_t*font)
2932 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
2934 if(i->swffont && i->swffont->name && !strcmp((char*)i->swffont->name,font->id))
2935 return; // the requested font is the current font
2937 fontlist_t*last=0,*l = i->fontlist;
2940 if(!strcmp((char*)l->swffont->name, font->id)) {
2941 return; // we already know this font
2945 l = (fontlist_t*)rfx_calloc(sizeof(fontlist_t));
2946 l->swffont = gfxfont_to_swffont(font, font->id, (i->config_flashversion>=8 && !NO_FONT3)?3:2);
2953 swf_FontSetID(l->swffont, getNewID(i->dev));
2955 if(getScreenLogLevel() >= LOGLEVEL_DEBUG) {
2957 // print font information
2958 msg("<debug> Font %s",font->id);
2959 msg("<debug> | ID: %d", l->swffont->id);
2960 msg("<debug> | Version: %d", l->swffont->version);
2961 msg("<debug> | Name: %s", l->swffont->name);
2962 msg("<debug> | Numchars: %d", l->swffont->numchars);
2963 msg("<debug> | Maxascii: %d", l->swffont->maxascii);
2964 msg("<debug> | Style: %d", l->swffont->style);
2965 msg("<debug> | Encoding: %d", l->swffont->encoding);
2966 for(iii=0; iii<l->swffont->numchars;iii++) {
2967 msg("<debug> | Glyph %d) name=%s, unicode=%d size=%d bbox=(%.2f,%.2f,%.2f,%.2f)\n", iii, l->swffont->glyphnames?l->swffont->glyphnames[iii]:"<nonames>", l->swffont->glyph2ascii[iii], l->swffont->glyph[iii].shape->bitlen,
2968 l->swffont->layout->bounds[iii].xmin/20.0,
2969 l->swffont->layout->bounds[iii].ymin/20.0,
2970 l->swffont->layout->bounds[iii].xmax/20.0,
2971 l->swffont->layout->bounds[iii].ymax/20.0
2974 for(t=0;t<l->swffont->maxascii;t++) {
2975 if(l->swffont->ascii2glyph[t] == iii)
2976 msg("<debug> | - maps to %d",t);
2982 static void swf_switchfont(gfxdevice_t*dev, const char*fontid)
2984 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
2986 if(i->swffont && i->swffont->name && !strcmp((char*)i->swffont->name,fontid))
2987 return; // the requested font is the current font
2989 fontlist_t*l = i->fontlist;
2991 if(!strcmp((char*)l->swffont->name, fontid)) {
2992 i->swffont = l->swffont;
2997 msg("<error> Unknown font id: %s", fontid);
3001 /* sets the matrix which is to be applied to characters drawn by swfoutput_drawchar() */
3002 static void setfontscale(gfxdevice_t*dev,double m11,double m12, double m21,double m22,double x, double y, char force)
3008 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
3009 if(i->lastfontm11 == m11 &&
3010 i->lastfontm12 == m12 &&
3011 i->lastfontm21 == m21 &&
3012 i->lastfontm22 == m22 && !force)
3017 i->lastfontm11 = m11;
3018 i->lastfontm12 = m12;
3019 i->lastfontm21 = m21;
3020 i->lastfontm22 = m22;
3022 double xsize = sqrt(m11*m11 + m12*m12);
3023 double ysize = sqrt(m21*m21 + m22*m22);
3026 if(i->config_flashversion>=8 && !NO_FONT3)
3029 i->current_font_size = (xsize>ysize?xsize:ysize)*extrazoom;
3030 if(i->current_font_size < 1)
3031 i->current_font_size = 1;
3034 swf_GetMatrix(0, &m);
3036 if(m21 || m12 || fabs(m11+m22)>0.001) {
3037 double ifs = (double)extrazoom/(i->current_font_size);
3038 m.sx = (S32)((m11*ifs)*65536); m.r1 = -(S32)((m21*ifs)*65536);
3039 m.r0 = (S32)((m12*ifs)*65536); m.sy = -(S32)((m22*ifs)*65536);
3042 /* this is the position of the first char to set a new fontmatrix-
3043 we hope that it's close enough to all other characters using the
3044 font, so we use its position as origin for the matrix */
3051 static void swf_drawchar(gfxdevice_t*dev, gfxfont_t*font, int glyph, gfxcolor_t*color, gfxmatrix_t*matrix)
3053 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
3055 msg("<error> swf_drawchar called (glyph %d) without font", glyph);
3059 if(i->config_drawonlyshapes) {
3060 gfxglyph_t*g = &font->glyphs[glyph];
3061 gfxline_t*line2 = gfxline_clone(g->line);
3062 gfxline_transform(line2, matrix);
3063 dev->fill(dev, line2, color);
3064 gfxline_free(line2);
3068 if(!i->swffont || !i->swffont->name || strcmp((char*)i->swffont->name,font->id)) // not equal to current font
3070 swf_switchfont(dev, font->id); // set the current font
3074 msg("<warning> swf_drawchar: Font is NULL");
3077 if(glyph<0 || glyph>=i->swffont->numchars) {
3078 msg("<warning> No character %d in font %s (%d chars)", glyph, FIXNULL((char*)i->swffont->name), i->swffont->numchars);
3081 glyph = i->swffont->glyph2glyph[glyph];
3083 setfontscale(dev, matrix->m00, matrix->m01, matrix->m10, matrix->m11, matrix->tx, matrix->ty, 0);
3085 double det = i->fontmatrix.sx/65536.0 * i->fontmatrix.sy/65536.0 -
3086 i->fontmatrix.r0/65536.0 * i->fontmatrix.r1/65536.0;
3087 if(fabs(det) < 0.0005) {
3088 /* x direction equals y direction- the text is invisible */
3089 msg("<verbose> Not drawing invisible character %d (det=%f, m=[%f %f;%f %f]\n", glyph,
3091 i->fontmatrix.sx/65536.0, i->fontmatrix.r1/65536.0,
3092 i->fontmatrix.r0/65536.0, i->fontmatrix.sy/65536.0);
3096 /*if(i->swffont->glyph[glyph].shape->bitlen <= 16) {
3097 msg("<warning> Glyph %d in current charset (%s, %d characters) is empty",
3098 glyph, FIXNULL((char*)i->swffont->name), i->swffont->numchars);
3102 /* calculate character position with respect to the current font matrix */
3103 double s = 20 * GLYPH_SCALE / det;
3104 double px = matrix->tx - i->fontmatrix.tx/20.0;
3105 double py = matrix->ty - i->fontmatrix.ty/20.0;
3106 int x = (SCOORD)(( px * i->fontmatrix.sy/65536.0 - py * i->fontmatrix.r1/65536.0)*s);
3107 int y = (SCOORD)((- px * i->fontmatrix.r0/65536.0 + py * i->fontmatrix.sx/65536.0)*s);
3108 if(x>32767 || x<-32768 || y>32767 || y<-32768) {
3109 msg("<verbose> Moving character origin to %f %f\n", matrix->tx, matrix->ty);
3111 setfontscale(dev, matrix->m00, matrix->m01, matrix->m10, matrix->m11, matrix->tx, matrix->ty, 1);
3112 /* since we just moved the char origin to the current char's position,
3113 it now has the relative position (0,0) */
3122 msg("<trace> Drawing char %d in font %d at %d,%d in color %02x%02x%02x%02x",
3123 glyph, i->swffont->id, x, y, color->r, color->g, color->b, color->a);
3125 if(color->a == 0 && i->config_invisibletexttofront) {
3126 RGBA color2 = *(RGBA*)color;
3127 if(i->config_flashversion>=8) {
3128 // use "multiply" blend mode
3129 color2.a = color2.r = color2.g = color2.b = 255;
3131 i->topchardata = charbuffer_append(i->topchardata, i->swffont, glyph, x, y, i->current_font_size, color2, &i->fontmatrix);
3133 i->chardata = charbuffer_append(i->chardata, i->swffont, glyph, x, y, i->current_font_size, *(RGBA*)color, &i->fontmatrix);
3135 swf_FontUseGlyph(i->swffont, glyph, i->current_font_size);