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"
45 #include "../gfximage.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_showimages;
106 int config_watermark;
108 int config_flashversion;
109 int config_reordertags;
110 int config_showclipshapes;
111 int config_splinemaxerror;
112 int config_fontsplinemaxerror;
113 int config_filloverlap;
116 int config_disable_polygon_conversion;
117 int config_normalize_polygon_positions;
118 int config_alignfonts;
119 char config_disablelinks;
120 RGBA config_linkcolor;
121 float config_minlinewidth;
122 double config_caplinewidth;
123 char* config_linktarget;
124 char*config_internallinkfunction;
125 char*config_externallinkfunction;
127 double config_framerate;
131 fontlist_t* fontlist;
170 int pic_height[1024];
177 char fillstylechanged;
179 int jpeg; //next image type
186 charbuffer_t* chardata;
187 charbuffer_t* topchardata; //chars supposed to be above everything else
194 int current_font_size;
196 double lastfontm11,lastfontm12,lastfontm21,lastfontm22;
207 } swfoutput_internal;
209 static const int NO_FONT3=0;
211 static void swf_fillbitmap(gfxdevice_t*dev, gfxline_t*line, gfximage_t*img, gfxmatrix_t*matrix, gfxcxform_t*cxform);
212 static int swf_setparameter(gfxdevice_t*driver, const char*key, const char*value);
213 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);
214 static void swf_startclip(gfxdevice_t*dev, gfxline_t*line);
215 static void swf_endclip(gfxdevice_t*dev);
216 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);
217 static void swf_fill(gfxdevice_t*dev, gfxline_t*line, gfxcolor_t*color);
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;
964 charbuffer_writetodevandfree(dev, i->topchardata, 1);
971 if(i->config_watermark) {
972 insert_watermark(dev, 1);
978 static void addViewer(gfxdevice_t* dev)
980 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
983 RGBA button_colors[3]= {{0xbf,0x00,0x00,0x80},{0xbf,0x20,0x20,0xc0}, {0xbf,0xc0,0xc0,0xff}};
985 int button_sizex = 20;
986 int button_sizey = 20;
988 RGBA black = {255,0,0,0};
990 i->tag = swf_InsertTag(i->tag,ST_DEFINESHAPE3);
992 int ls1 = swf_ShapeAddLineStyle(s,40,&black);
993 int fs1 = swf_ShapeAddSolidFillStyle(s,&button_colors[t/2]);
994 int shapeid = ids[t] = getNewID(dev);
995 swf_SetU16(i->tag,shapeid);
997 r.xmin = -20*button_sizex;
998 r.xmax = 20*button_sizex;
1000 r.ymax = 40*button_sizey;
1001 swf_SetRect(i->tag,&r); // set shape bounds
1002 swf_SetShapeHeader(i->tag,s); // write all styles to tag
1003 swf_ShapeSetAll(i->tag,s,0*button_sizex,0,ls1,fs1,0);
1004 swf_ShapeSetLine(i->tag,s,(1-(t&1)*2)*20*button_sizex,20*button_sizey);
1005 swf_ShapeSetLine(i->tag,s,-(1-(t&1)*2)*20*button_sizex,20*button_sizey);
1006 swf_ShapeSetLine(i->tag,s,0,-40*button_sizey);
1007 swf_ShapeSetEnd(i->tag); // finish drawing
1008 swf_ShapeFree(s); // clean shape structure (which isn't needed anymore after writing the tag)
1010 ActionTAG*a1=0,*a2=0,*a3=0;
1011 a1 = action_NextFrame(a1);
1012 a1 = action_Stop(a1);
1013 a1 = action_End(a1);
1015 a2 = action_PreviousFrame(a2);
1016 a2 = action_Stop(a2);
1017 a2 = action_End(a2);
1019 a3 = action_Stop(a3);
1020 a3 = action_End(a3);
1022 i->tag = swf_InsertTag(i->tag, ST_DOACTION);
1023 swf_ActionSet(i->tag,a3);
1025 i->tag = swf_InsertTag(i->tag,ST_DEFINEBUTTON);
1026 int buttonid1 = getNewID(dev);
1027 swf_SetU16(i->tag, buttonid1);
1028 swf_ButtonSetRecord(i->tag,BS_UP|BS_HIT,ids[0],1,NULL,NULL);
1029 swf_ButtonSetRecord(i->tag,BS_OVER,ids[2],1,NULL,NULL);
1030 swf_ButtonSetRecord(i->tag,BS_DOWN,ids[4],1,NULL,NULL);
1031 swf_SetU8(i->tag,0); // end of button records
1032 swf_ActionSet(i->tag,a1);
1034 i->tag = swf_InsertTag(i->tag,ST_DEFINEBUTTON);
1035 int buttonid2 = getNewID(dev);
1036 swf_SetU16(i->tag, buttonid2);
1037 swf_ButtonSetRecord(i->tag,BS_UP|BS_HIT,ids[1],1,NULL,NULL);
1038 swf_ButtonSetRecord(i->tag,BS_OVER,ids[3],1,NULL,NULL);
1039 swf_ButtonSetRecord(i->tag,BS_DOWN,ids[5],1,NULL,NULL);
1040 swf_SetU8(i->tag,0); // end of button records
1041 swf_ActionSet(i->tag,a2);
1043 i->tag = swf_InsertTag(i->tag,ST_PLACEOBJECT2);
1045 swf_GetMatrix(0, &m);
1046 m.tx = button_sizex*20+200;
1047 swf_ObjectPlace(i->tag, buttonid2, 65534,&m,0,0);
1048 i->tag = swf_InsertTag(i->tag,ST_PLACEOBJECT2);
1049 m.tx = button_sizex*20+200+200;
1050 swf_ObjectPlace(i->tag, buttonid1, 65535,&m,0,0);
1054 void swf_startframe(gfxdevice_t*dev, int width, int height)
1056 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1058 if(i->config_protect) {
1059 i->tag = swf_InsertTag(i->tag, ST_PROTECT);
1060 i->config_protect = 0;
1062 if(i->config_simpleviewer) {
1067 if(!i->firstpage && !i->pagefinished)
1070 msg("<verbose> Starting new SWF page of size %dx%d", width, height);
1072 swf_GetMatrix(0, &i->page_matrix);
1073 i->page_matrix.tx = 0;
1074 i->page_matrix.ty = 0;
1081 /* create a bbox structure with the page size. This is used
1082 for clipping shape and text bounding boxes. As we don't want to
1083 generate bounding boxes which extend beyond the movie size (in
1084 order to not confuse Flash), we clip everything against i->pagebbox */
1085 i->pagebbox.xmin = 0;
1086 i->pagebbox.ymin = 0;
1087 i->pagebbox.xmax = width*20;
1088 i->pagebbox.ymax = height*20;
1090 /* increase SWF's bounding box */
1091 swf_ExpandRect2(&i->swf->movieSize, &i->pagebbox);
1093 i->lastframeno = i->frameno;
1095 i->pagefinished = 0;
1099 void swf_endframe(gfxdevice_t*dev)
1101 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1103 if(!i->pagefinished)
1106 if( (i->swf->fileVersion <= 8) && (i->config_insertstoptag) ) {
1108 atag = action_Stop(atag);
1109 atag = action_End(atag);
1110 i->tag = swf_InsertTag(i->tag,ST_DOACTION);
1111 swf_ActionSet(i->tag,atag);
1113 i->tag = swf_InsertTag(i->tag,ST_SHOWFRAME);
1116 for(i->depth;i->depth>i->startdepth;i->depth--) {
1117 i->tag = swf_InsertTag(i->tag,ST_REMOVEOBJECT2);
1118 swf_SetU16(i->tag,i->depth);
1120 i->depth = i->startdepth;
1122 if(i->config_frameresets) {
1123 for(i->currentswfid;i->currentswfid>i->startids;i->currentswfid--) {
1124 i->tag = swf_InsertTag(i->tag,ST_FREECHARACTER);
1125 swf_SetU16(i->tag,i->currentswfid);
1127 i->currentswfid = i->startids;
1131 static void setBackground(gfxdevice_t*dev, int x1, int y1, int x2, int y2)
1133 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1135 rgb.a = rgb.r = rgb.g = rgb.b = 0xff;
1139 int shapeid = getNewID(dev);
1144 i->tag = swf_InsertTag(i->tag, ST_DEFINESHAPE);
1146 fs1 = swf_ShapeAddSolidFillStyle(s, &rgb);
1147 swf_SetU16(i->tag,shapeid);
1148 swf_SetRect(i->tag,&r);
1149 swf_SetShapeHeader(i->tag,s);
1150 swf_ShapeSetAll(i->tag,s,x1,y1,ls1,fs1,0);
1151 swf_ShapeSetLine(i->tag,s,(x2-x1),0);
1152 swf_ShapeSetLine(i->tag,s,0,(y2-y1));
1153 swf_ShapeSetLine(i->tag,s,(x1-x2),0);
1154 swf_ShapeSetLine(i->tag,s,0,(y1-y2));
1155 swf_ShapeSetEnd(i->tag);
1157 i->tag = swf_InsertTag(i->tag, ST_PLACEOBJECT2);
1158 swf_ObjectPlace(i->tag,shapeid,getNewDepth(dev),0,0,0);
1159 i->tag = swf_InsertTag(i->tag, ST_PLACEOBJECT2);
1160 swf_ObjectPlaceClip(i->tag,shapeid,getNewDepth(dev),0,0,0,65535);
1163 /* initialize the swf writer */
1164 void gfxdevice_swf_init(gfxdevice_t* dev)
1166 memset(dev, 0, sizeof(gfxdevice_t));
1170 dev->internal = init_internal_struct(); // set config to default values
1172 dev->startpage = swf_startframe;
1173 dev->endpage = swf_endframe;
1174 dev->finish = swf_finish;
1175 dev->fillbitmap = swf_fillbitmap;
1176 dev->setparameter = swf_setparameter;
1177 dev->stroke = swf_stroke;
1178 dev->startclip = swf_startclip;
1179 dev->endclip = swf_endclip;
1180 dev->fill = swf_fill;
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, "linknameurl")) {
2124 i->config_linknameurl = atoi(value);
2125 } else if(!strcmp(name, "showimages")) {
2126 i->config_showimages = atoi(value);
2127 } else if(!strcmp(name, "disablelinks")) {
2128 i->config_disablelinks = atoi(value);
2129 } else if(!strcmp(name, "simpleviewer")) {
2130 i->config_simpleviewer = atoi(value);
2131 } else if(!strcmp(name, "next_bitmap_is_jpeg")) {
2133 } else if(!strcmp(name, "jpegquality")) {
2134 int val = atoi(value);
2136 if(val>101) val=101;
2137 i->config_jpegquality = val;
2138 } else if(!strcmp(name, "splinequality")) {
2139 int v = atoi(value);
2140 v = 500-(v*5); // 100% = 0.25 pixel, 0% = 25 pixel
2142 i->config_splinemaxerror = v;
2143 } else if(!strcmp(name, "fontquality")) {
2144 int v = atoi(value);
2145 v = 500-(v*5); // 100% = 0.25 pixel, 0% = 25 pixel
2147 i->config_fontsplinemaxerror = v;
2148 } else if(!strcmp(name, "linkcolor")) {
2149 if(strlen(value)!=8) {
2150 fprintf(stderr, "Unknown format for option 'linkcolor'. (%s <-> RRGGBBAA)\n", value);
2153 # define NIBBLE(s) (((s)>='0' && (s)<='9')?((s)-'0'):((s)&0x0f)+9)
2154 i->config_linkcolor.r = NIBBLE(value[0])<<4 | NIBBLE(value[1]);
2155 i->config_linkcolor.g = NIBBLE(value[2])<<4 | NIBBLE(value[3]);
2156 i->config_linkcolor.b = NIBBLE(value[4])<<4 | NIBBLE(value[5]);
2157 i->config_linkcolor.a = NIBBLE(value[6])<<4 | NIBBLE(value[7]);
2158 } else if(!strcmp(name, "help")) {
2159 printf("\nSWF layer options:\n");
2160 printf("jpegsubpixels=<pixels> resolution adjustment for jpeg images (same as jpegdpi, but in pixels)\n");
2161 printf("ppmsubpixels=<pixels resolution adjustment for lossless images (same as ppmdpi, but in pixels)\n");
2162 printf("subpixels=<pixels> shortcut for setting both jpegsubpixels and ppmsubpixels\n");
2163 printf("drawonlyshapes convert everything to shapes (currently broken)\n");
2164 printf("ignoredraworder allow to perform a few optimizations for creating smaller SWFs\n");
2165 printf("linksopennewwindow make links open a new browser window\n");
2166 printf("linktarget target window name of new links\n");
2167 printf("linkcolor=<color) color of links (format: RRGGBBAA)\n");
2168 printf("linknameurl Link buttons will be named like the URL they refer to (handy for iterating through links with actionscript)\n");
2169 printf("storeallcharacters don't reduce the fonts to used characters in the output file\n");
2170 printf("enablezlib switch on zlib compression (also done if flashversion>=6)\n");
2171 printf("bboxvars store the bounding box of the SWF file in actionscript variables\n");
2172 printf("dots Take care to handle dots correctly\n");
2173 printf("reordertags=0/1 (default: 1) perform some tag optimizations\n");
2174 printf("internallinkfunction=<name> when the user clicks a internal link (to a different page) in the converted file, this actionscript function is called\n");
2175 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");
2176 printf("disable_polygon_conversion never convert strokes to polygons (will remove capstyles and joint styles)\n");
2177 printf("caplinewidth=<width> the minimum thichness a line needs to have so that capstyles become visible (and are converted)\n");
2178 printf("insertstop put an ActionScript \"STOP\" tag in every frame\n");
2179 printf("protect add a \"protect\" tag to the file, to prevent loading in the Flash editor\n");
2180 printf("flashversion=<version> the SWF fileversion (6)\n");
2181 printf("framerate=<fps> SWF framerate\n");
2182 printf("minlinewidth=<width> convert horizontal/vertical boxes smaller than this width to lines (0.05) \n");
2183 printf("simpleviewer Add next/previous buttons to the SWF\n");
2184 printf("animate insert a showframe tag after each placeobject (animate draw order of PDF files)\n");
2185 printf("jpegquality=<quality> set compression quality of jpeg images\n");
2186 printf("splinequality=<value> Set the quality of spline convertion to value (0-100, default: 100).\n");
2187 printf("disablelinks Disable links.\n");
2194 // --------------------------------------------------------------------
2196 static CXFORM gfxcxform_to_cxform(gfxcxform_t* c)
2199 swf_GetCXForm(0, &cx, 1);
2202 if(c->rg!=0 || c->rb!=0 || c->ra!=0 ||
2203 c->gr!=0 || c->gb!=0 || c->ga!=0 ||
2204 c->br!=0 || c->bg!=0 || c->ba!=0 ||
2205 c->ar!=0 || c->ag!=0 || c->ab!=0)
2206 msg("<warning> CXForm not SWF-compatible");
2208 cx.a0 = (S16)(c->aa*256);
2209 cx.r0 = (S16)(c->rr*256);
2210 cx.g0 = (S16)(c->gg*256);
2211 cx.b0 = (S16)(c->bb*256);
2220 static int imageInCache(gfxdevice_t*dev, void*data, int width, int height)
2224 static void addImageToCache(gfxdevice_t*dev, void*data, int width, int height)
2228 static int add_image(swfoutput_internal*i, gfximage_t*img, int targetwidth, int targetheight, int* newwidth, int* newheight)
2230 gfxdevice_t*dev = i->dev;
2232 RGBA*mem = (RGBA*)img->data;
2234 int sizex = img->width;
2235 int sizey = img->height;
2236 int is_jpeg = i->jpeg;
2239 int newsizex=sizex, newsizey=sizey;
2242 if(is_jpeg && i->config_jpegsubpixels) {
2243 newsizex = (int)(targetwidth*i->config_jpegsubpixels + 0.5);
2244 newsizey = (int)(targetheight*i->config_jpegsubpixels + 0.5);
2245 } else if(!is_jpeg && i->config_ppmsubpixels) {
2246 newsizex = (int)(targetwidth*i->config_ppmsubpixels + 0.5);
2247 newsizey = (int)(targetheight*i->config_ppmsubpixels + 0.5);
2251 if(sizex<=0 || sizey<=0)
2258 /* TODO: cache images */
2260 if(newsizex<sizex || newsizey<sizey) {
2261 msg("<verbose> Scaling %dx%d image to %dx%d", sizex, sizey, newsizex, newsizey);
2262 gfximage_t*ni = gfximage_rescale(img, newsizex, newsizey);
2263 newpic = (RGBA*)ni->data;
2265 *newwidth = sizex = newsizex;
2266 *newheight = sizey = newsizey;
2269 *newwidth = newsizex = sizex;
2270 *newheight = newsizey = sizey;
2273 int num_colors = swf_ImageGetNumberOfPaletteEntries(mem,sizex,sizey,0);
2274 int has_alpha = swf_ImageHasAlpha(mem,sizex,sizey);
2276 msg("<verbose> Drawing %dx%d %s%simage (id %d) at size %dx%d (%dx%d), %s%d colors",
2278 has_alpha?(has_alpha==2?"semi-transparent ":"transparent "):"",
2279 is_jpeg?"jpeg-":"", i->currentswfid+1,
2281 targetwidth, targetheight,
2282 /*newsizex, newsizey,*/
2283 num_colors>256?">":"", num_colors>256?256:num_colors);
2285 /*RGBA* pal = (RGBA*)rfx_alloc(sizeof(RGBA)*num_colors);
2286 swf_ImageGetNumberOfPaletteEntries(mem,sizex,sizey,pal);
2288 for(t=0;t<num_colors;t++) {
2289 printf("%02x%02x%02x%02x ",
2290 pal[t].r, pal[t].g, pal[t].b, pal[t].a);
2297 int cacheid = imageInCache(dev, mem, sizex, sizey);
2300 bitid = getNewID(dev);
2302 i->tag = swf_AddImage(i->tag, bitid, mem, sizex, sizey, i->config_jpegquality);
2303 addImageToCache(dev, mem, sizex, sizey);
2313 static SRECT gfxline_getSWFbbox(gfxline_t*line)
2315 gfxbbox_t bbox = gfxline_getbbox(line);
2317 r.xmin = (int)(bbox.xmin*20);
2318 r.ymin = (int)(bbox.ymin*20);
2319 r.xmax = (int)(bbox.xmax*20);
2320 r.ymax = (int)(bbox.ymax*20);
2324 int line_is_empty(gfxline_t*line)
2327 if(line->type != gfx_moveTo)
2334 static void swf_fillbitmap(gfxdevice_t*dev, gfxline_t*line, gfximage_t*img, gfxmatrix_t*matrix, gfxcxform_t*cxform)
2336 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
2338 if(line_is_empty(line))
2344 int targetx = (int)(sqrt(matrix->m00*matrix->m00 + matrix->m01*matrix->m01)*img->width);
2345 int targety = (int)(sqrt(matrix->m10*matrix->m10 + matrix->m11*matrix->m11)*img->height);
2347 int newwidth=0,newheight=0;
2348 int bitid = add_image(i, img, targetx, targety, &newwidth, &newheight);
2351 double fx = (double)img->width / (double)newwidth;
2352 double fy = (double)img->height / (double)newheight;
2355 m.sx = (int)(65536*20*matrix->m00*fx); m.r1 = (int)(65536*20*matrix->m10*fy);
2356 m.r0 = (int)(65536*20*matrix->m01*fx); m.sy = (int)(65536*20*matrix->m11*fy);
2357 m.tx = (int)(matrix->tx*20);
2358 m.ty = (int)(matrix->ty*20);
2361 int myshapeid = getNewID(dev);
2362 i->tag = swf_InsertTag(i->tag,ST_DEFINESHAPE);
2364 swf_ShapeNew(&shape);
2365 int fsid = swf_ShapeAddBitmapFillStyle(shape,&m,bitid,1);
2367 if(i->config_showimages) {
2368 RGBA pink = {255,255,0,255};
2369 lsid = swf_ShapeAddLineStyle(shape, 20, &pink);
2371 swf_SetU16(i->tag, myshapeid);
2372 SRECT r = gfxline_getSWFbbox(line);
2373 r = swf_ClipRect(i->pagebbox, r);
2374 swf_SetRect(i->tag,&r);
2375 swf_SetShapeStyles(i->tag,shape);
2376 swf_ShapeCountBits(shape,NULL,NULL);
2377 swf_SetShapeBits(i->tag,shape);
2378 swf_ShapeSetAll(i->tag,shape,UNDEFINED_COORD,UNDEFINED_COORD,lsid,fsid,0);
2379 i->swflastx = i->swflasty = UNDEFINED_COORD;
2380 drawgfxline(dev, line, 1);
2381 swf_ShapeSetEnd(i->tag);
2382 swf_ShapeFree(shape);
2384 msg("<trace> Placing image, shape ID %d, bitmap ID %d", myshapeid, bitid);
2385 i->tag = swf_InsertTag(i->tag,ST_PLACEOBJECT2);
2386 CXFORM cxform2 = gfxcxform_to_cxform(cxform);
2387 swf_ObjectPlace(i->tag,myshapeid,getNewDepth(dev),&i->page_matrix,&cxform2,NULL);
2390 static RGBA col_black = {255,0,0,0};
2392 static void drawoutline(gfxdevice_t*dev, gfxline_t*line)
2394 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
2396 int myshapeid = getNewID(dev);
2397 i->tag = swf_InsertTag(i->tag,ST_DEFINESHAPE3);
2400 swf_ShapeNew(&shape);
2401 int lsid = swf_ShapeAddLineStyle(shape,1,&col_black);
2403 swf_SetU16(i->tag,myshapeid);
2404 SRECT r = gfxline_getSWFbbox(line);
2405 r = swf_ClipRect(i->pagebbox, r);
2406 swf_SetRect(i->tag,&r);
2407 swf_SetShapeStyles(i->tag,shape);
2408 swf_ShapeCountBits(shape,NULL,NULL);
2409 swf_SetShapeBits(i->tag,shape);
2410 swf_ShapeSetAll(i->tag,shape,UNDEFINED_COORD,UNDEFINED_COORD,lsid,0,0);
2411 drawgfxline(dev, line, 1);
2412 swf_ShapeSetEnd(i->tag);
2413 swf_ShapeFree(shape);
2415 i->tag = swf_InsertTag(i->tag,ST_PLACEOBJECT2);
2416 swf_ObjectPlace(i->tag, myshapeid, getNewDepth(dev), 0,0,0);
2419 static void swf_startclip(gfxdevice_t*dev, gfxline_t*line)
2421 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
2422 if(i->config_noclips)
2428 if(i->clippos >= 127)
2430 msg("<warning> Too many clip levels.");
2434 if(i->config_showclipshapes)
2435 drawoutline(dev, line);
2437 int myshapeid = getNewID(dev);
2438 i->tag = swf_InsertTag(i->tag,ST_DEFINESHAPE3);
2440 memset(&col, 0, sizeof(RGBA));
2443 swf_ShapeNew(&shape);
2444 int fsid = swf_ShapeAddSolidFillStyle(shape,&col);
2446 RGBA markcol = {0,i->mark[0],i->mark[1],i->mark[2]};
2447 swf_ShapeAddSolidFillStyle(shape,&markcol);
2449 swf_SetU16(i->tag,myshapeid);
2450 SRECT r = gfxline_getSWFbbox(line);
2451 r = swf_ClipRect(i->pagebbox, r);
2452 swf_SetRect(i->tag,&r);
2453 swf_SetShapeStyles(i->tag,shape);
2454 swf_ShapeCountBits(shape,NULL,NULL);
2455 swf_SetShapeBits(i->tag,shape);
2456 swf_ShapeSetAll(i->tag,shape,UNDEFINED_COORD,UNDEFINED_COORD,0,fsid,0);
2457 i->swflastx = i->swflasty = UNDEFINED_COORD;
2458 i->shapeisempty = 1;
2459 drawgfxline(dev, line, 1);
2460 if(i->shapeisempty) {
2461 /* an empty clip shape is equivalent to a shape with no area */
2462 int x = line?line->x:0;
2463 int y = line?line->y:0;
2464 moveto(dev, i->tag, x,y);
2465 lineto(dev, i->tag, x,y);
2466 lineto(dev, i->tag, x,y);
2468 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)) {
2469 if(i->config_watermark) {
2470 gfxbbox_t r; r.xmin = r.ymin = 0;r.xmax = i->max_x;r.ymax = i->max_y;
2471 draw_watermark(dev, r, 1);
2474 swf_ShapeSetEnd(i->tag);
2475 swf_ShapeFree(shape);
2477 /* TODO: remember the bbox, and check all shapes against it */
2479 msg("<trace> Placing clip ID %d", myshapeid);
2480 i->tag = swf_InsertTag(i->tag,ST_PLACEOBJECT2);
2481 i->cliptags[i->clippos] = i->tag;
2482 i->clipshapes[i->clippos] = myshapeid;
2483 i->clipdepths[i->clippos] = getNewDepth(dev);
2487 static void swf_endclip(gfxdevice_t*dev)
2489 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
2490 if(i->config_noclips)
2498 msg("<error> Invalid end of clipping region");
2502 /*swf_ObjectPlaceClip(i->cliptags[i->clippos],i->clipshapes[i->clippos],i->clipdepths[i->clippos],&i->page_matrix,NULL,NULL,
2503 / * clip to depth: * / i->depth <= i->clipdepths[i->clippos]? i->depth : i->depth - 1);
2505 swf_ObjectPlaceClip(i->cliptags[i->clippos],i->clipshapes[i->clippos],i->clipdepths[i->clippos],&i->page_matrix,NULL,NULL,i->depth);
2507 static int gfxline_type(gfxline_t*line)
2513 int haszerosegments=0;
2516 if(line->type == gfx_moveTo) {
2519 } else if(line->type == gfx_lineTo) {
2523 } else if(line->type == gfx_splineTo) {
2525 if(tmpsplines>lines)
2533 if(lines==0 && splines==0) return 0;
2534 else if(lines==1 && splines==0) return 1;
2535 else if(lines==0 && splines==1) return 2;
2536 else if(splines==0) return 3;
2540 static int gfxline_has_dots(gfxline_t*line)
2548 if(line->type == gfx_moveTo) {
2549 /* test the length of the preceding line, and assume it is a dot if
2550 it's length is less than 1.0. But *only* if there's a noticable
2551 gap between the previous line and the next moveTo. (I've come
2552 across a PDF where thousands of "dots" were stringed together,
2554 int last_short_gap = short_gap;
2555 if((fabs(line->x - x) + fabs(line->y - y)) < 1.0) {
2560 if(isline && dist < 1 && !short_gap && !last_short_gap) {
2565 } else if(line->type == gfx_lineTo) {
2566 dist += fabs(line->x - x) + fabs(line->y - y);
2568 } else if(line->type == gfx_splineTo) {
2569 dist += fabs(line->sx - x) + fabs(line->sy - y) +
2570 fabs(line->x - line->sx) + fabs(line->y - line->sy);
2577 if(isline && dist < 1 && !short_gap) {
2583 static int gfxline_fix_short_edges(gfxline_t*line)
2587 if(line->type == gfx_lineTo) {
2588 if(fabs(line->x - x) + fabs(line->y - y) < 0.01) {
2591 } else if(line->type == gfx_splineTo) {
2592 if(fabs(line->sx - x) + fabs(line->sy - y) +
2593 fabs(line->x - line->sx) + fabs(line->y - line->sy) < 0.01) {
2604 static char is_inside_page(gfxdevice_t*dev, gfxcoord_t x, gfxcoord_t y)
2606 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
2607 if(x<i->min_x || x>i->max_x) return 0;
2608 if(y<i->min_y || y>i->max_y) return 0;
2612 gfxline_t* gfxline_move(gfxline_t*line, double x, double y)
2614 gfxline_t*l = line = gfxline_clone(line);
2626 //#define NORMALIZE_POLYGON_POSITIONS
2628 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)
2630 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
2631 if(line_is_empty(line))
2633 int type = gfxline_type(line);
2634 int has_dots = gfxline_has_dots(line);
2635 gfxbbox_t r = gfxline_getbbox(line);
2636 int is_outside_page = !is_inside_page(dev, r.xmin, r.ymin) || !is_inside_page(dev, r.xmax, r.ymax);
2638 /* TODO: * split line into segments, and perform this check for all segments */
2640 if(i->config_disable_polygon_conversion || /*type>=5 ||*/
2642 (width <= i->config_caplinewidth
2643 || (cap_style == gfx_capRound && joint_style == gfx_joinRound)
2644 || (cap_style == gfx_capRound && type<=2))))
2648 /* convert line to polygon */
2649 msg("<trace> draw as polygon, type=%d dots=%d", type, has_dots);
2651 gfxline_fix_short_edges(line);
2652 /* we need to convert the line into a polygon */
2653 gfxpoly_t* poly = gfxpoly_from_stroke(line, width, cap_style, joint_style, miterLimit, DEFAULT_GRID);
2654 gfxline_t*gfxline = gfxline_from_gfxpoly(poly);
2655 dev->fill(dev, gfxline, color);
2656 gfxline_free(gfxline);
2657 gfxpoly_destroy(poly);
2661 msg("<trace> draw as stroke, type=%d dots=%d", type, has_dots);
2664 if(i->config_normalize_polygon_positions) {
2666 double startx = 0, starty = 0;
2667 if(line && line->type == gfx_moveTo) {
2671 line = gfxline_move(line, -startx, -starty);
2672 i->shapeposx = (int)(startx*20);
2673 i->shapeposy = (int)(starty*20);
2676 swfoutput_setstrokecolor(dev, color->r, color->g, color->b, color->a);
2677 swfoutput_setlinewidth(dev, width);
2680 drawgfxline(dev, line, 0);
2682 if(i->config_normalize_polygon_positions) {
2683 free(line); //account for _move
2688 static void swf_fill(gfxdevice_t*dev, gfxline_t*line, gfxcolor_t*color)
2690 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
2691 if(line_is_empty(line))
2695 gfxbbox_t r = gfxline_getbbox(line);
2696 int is_outside_page = !is_inside_page(dev, r.xmin, r.ymin) || !is_inside_page(dev, r.xmax, r.ymax);
2700 if(!i->config_ignoredraworder)
2703 if(i->config_normalize_polygon_positions) {
2705 double startx = 0, starty = 0;
2706 if(line && line->type == gfx_moveTo) {
2710 line = gfxline_move(line, -startx, -starty);
2711 i->shapeposx = (int)(startx*20);
2712 i->shapeposy = (int)(starty*20);
2715 swfoutput_setfillcolor(dev, color->r, color->g, color->b, color->a);
2718 drawgfxline(dev, line, 1);
2720 if(i->currentswfid==2 && r.xmin==0 && r.ymin==0 && r.xmax==i->max_x && r.ymax==i->max_y) {
2721 if(i->config_watermark) {
2722 draw_watermark(dev, r, 1);
2726 msg("<trace> end of swf_fill (shapeid=%d)", i->shapeid);
2728 if(i->config_normalize_polygon_positions) {
2729 free(line); //account for _move
2733 static GRADIENT* gfxgradient_to_GRADIENT(gfxgradient_t*gradient)
2736 gfxgradient_t*g = gradient;
2741 GRADIENT* swfgradient = malloc(sizeof(GRADIENT));
2742 swfgradient->num = num;
2743 swfgradient->rgba = malloc(sizeof(swfgradient->rgba[0])*num);
2744 swfgradient->ratios = malloc(sizeof(swfgradient->ratios[0])*num);
2749 swfgradient->ratios[num] = g->pos*255;
2750 swfgradient->rgba[num] = *(RGBA*)&g->color;
2757 static void swf_fillgradient(gfxdevice_t*dev, gfxline_t*line, gfxgradient_t*gradient, gfxgradienttype_t type, gfxmatrix_t*matrix)
2759 if(line_is_empty(line))
2761 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
2763 if(line_is_empty(line))
2766 GRADIENT* swfgradient = gfxgradient_to_GRADIENT(gradient);
2773 double f = type==gfxgradient_radial?4:4;
2775 m.sx = (int)(matrix->m00*20*f); m.r1 = (int)(matrix->m10*20*f);
2776 m.r0 = (int)(matrix->m01*20*f); m.sy = (int)(matrix->m11*20*f);
2777 m.tx = (int)(matrix->tx*20);
2778 m.ty = (int)(matrix->ty*20);
2781 int myshapeid = getNewID(dev);
2782 i->tag = swf_InsertTag(i->tag, ST_DEFINESHAPE2);
2784 swf_ShapeNew(&shape);
2785 int fsid = swf_ShapeAddGradientFillStyle(shape,&m,swfgradient,type==gfxgradient_radial);
2786 swf_SetU16(i->tag, myshapeid);
2787 SRECT r = gfxline_getSWFbbox(line);
2788 r = swf_ClipRect(i->pagebbox, r);
2789 swf_SetRect(i->tag,&r);
2790 swf_SetShapeStyles(i->tag,shape);
2791 swf_ShapeCountBits(shape,NULL,NULL);
2792 swf_SetShapeBits(i->tag,shape);
2793 swf_ShapeSetAll(i->tag,shape,UNDEFINED_COORD,UNDEFINED_COORD,0,fsid,0);
2794 i->swflastx = i->swflasty = UNDEFINED_COORD;
2795 drawgfxline(dev, line, 1);
2796 swf_ShapeSetEnd(i->tag);
2797 swf_ShapeFree(shape);
2799 int depth = getNewDepth(dev);
2800 msg("<trace> Placing gradient, shape ID %d, depth %d", myshapeid, depth);
2801 i->tag = swf_InsertTag(i->tag,ST_PLACEOBJECT2);
2802 swf_ObjectPlace(i->tag,myshapeid,depth,&i->page_matrix,NULL,NULL);
2804 swf_FreeGradient(swfgradient);free(swfgradient);
2807 static SWFFONT* gfxfont_to_swffont(gfxfont_t*font, const char* id, int version)
2809 SWFFONT*swffont = (SWFFONT*)rfx_calloc(sizeof(SWFFONT));
2811 SRECT bounds = {0,0,0,0};
2813 swffont->version = version;
2814 swffont->name = (U8*)strdup(id);
2815 swffont->layout = (SWFLAYOUT*)rfx_calloc(sizeof(SWFLAYOUT));
2816 swffont->layout->ascent = 0;
2817 swffont->layout->descent = 0;
2818 swffont->layout->leading = 0;
2819 swffont->layout->bounds = (SRECT*)rfx_calloc(sizeof(SRECT)*font->num_glyphs);
2820 swffont->encoding = FONT_ENCODING_UNICODE;
2821 swffont->numchars = font->num_glyphs;
2822 swffont->maxascii = font->max_unicode;
2823 swffont->ascii2glyph = (int*)rfx_calloc(sizeof(int)*swffont->maxascii);
2824 swffont->glyph2ascii = (U16*)rfx_calloc(sizeof(U16)*swffont->numchars);
2825 swffont->glyph = (SWFGLYPH*)rfx_calloc(sizeof(SWFGLYPH)*swffont->numchars);
2826 swffont->glyphnames = (char**)rfx_calloc(sizeof(char*)*swffont->numchars);
2828 SRECT max = {0,0,0,0};
2829 for(t=0;t<font->num_glyphs;t++) {
2833 int u = font->glyphs[t].unicode;
2836 for(s=0;s<font->num_glyphs;s++) {
2837 if(swffont->glyph2ascii[s]==u)
2840 if(u >= 0xe000 || u == 0x0000 || twice) {
2841 /* flash 8 flashtype requires unique unicode IDs for each character.
2842 We use the Unicode private user area to assign characters, hoping that
2843 the font doesn't contain more than 2048 glyphs */
2844 u = 0xe000 + (t&0x1fff);
2846 swffont->glyph2ascii[t] = u;
2848 if(font->glyphs[t].name) {
2849 swffont->glyphnames[t] = strdup(font->glyphs[t].name);
2851 swffont->glyphnames[t] = 0;
2853 advance = font->glyphs[t].advance;
2855 swf_Shape01DrawerInit(&draw, 0);
2856 line = font->glyphs[t].line;
2858 const double scale = GLYPH_SCALE;
2861 c.x = line->sx * scale; c.y = -line->sy * scale;
2862 //to.x = floor(line->x * scale); to.y = floor(-line->y * scale);
2863 to.x = line->x * scale; to.y = -line->y * scale;
2865 /*if(strstr(swffont->name, "BIRNU") && t==90) {
2869 if(line->type == gfx_moveTo) {
2870 draw.moveTo(&draw, &to);
2871 } else if(line->type == gfx_lineTo) {
2872 draw.lineTo(&draw, &to);
2873 } else if(line->type == gfx_splineTo) {
2874 draw.splineTo(&draw, &c, &to);
2879 swffont->glyph[t].shape = swf_ShapeDrawerToShape(&draw);
2881 SRECT bbox = swf_ShapeDrawerGetBBox(&draw);
2882 swf_ExpandRect2(&max, &bbox);
2884 swffont->layout->bounds[t] = bbox;
2886 if(advance<32768.0/20) {
2887 swffont->glyph[t].advance = (int)(advance*20);
2889 //msg("<warning> Advance value overflow in glyph %d", t);
2890 swffont->glyph[t].advance = 32767;
2893 draw.dealloc(&draw);
2895 swf_ExpandRect2(&bounds, &swffont->layout->bounds[t]);
2898 for(t=0;t<font->num_glyphs;t++) {
2899 SRECT bbox = swffont->layout->bounds[t];
2901 /* if the glyph doesn't have a bounding box, use the
2902 combined bounding box (necessary e.g. for space characters) */
2903 if(!(bbox.xmin|bbox.ymin|bbox.xmax|bbox.ymax)) {
2904 swffont->layout->bounds[t] = bbox = max;
2907 /* check that the advance value is reasonable, by comparing it
2908 with the bounding box */
2909 if(bbox.xmax>0 && (bbox.xmax*10 < swffont->glyph[t].advance || !swffont->glyph[t].advance)) {
2910 if(swffont->glyph[t].advance)
2911 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);
2912 swffont->glyph[t].advance = bbox.xmax;
2914 //swffont->glyph[t].advance = bbox.xmax - bbox.xmin;
2918 /* Flash player will use the advance value from the char, and the ascent/descent values
2919 from the layout for text selection.
2920 ascent will extend the char into negative y direction, from the baseline, while descent
2921 will extend in positive y direction, also from the baseline.
2922 The baseline is defined as the y-position zero
2925 swffont->layout->ascent = bounds.ymin<0?-bounds.ymin:0;
2926 swffont->layout->descent = bounds.ymax>0?bounds.ymax:0;
2927 swffont->layout->leading = bounds.ymax - bounds.ymin;
2929 /* if the font has proper ascent/descent values (>0) and those define
2930 greater line spacing that what we estimated from the bounding boxes,
2931 use the font's parameters */
2932 if(font->ascent*20 > swffont->layout->ascent)
2933 swffont->layout->ascent = font->ascent*20;
2934 if(font->descent*20 > swffont->layout->descent)
2935 swffont->layout->descent = font->descent*20;
2937 swf_FontSort(swffont);
2941 static void swf_addfont(gfxdevice_t*dev, gfxfont_t*font)
2943 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
2945 if(i->swffont && i->swffont->name && !strcmp((char*)i->swffont->name,font->id))
2946 return; // the requested font is the current font
2948 fontlist_t*last=0,*l = i->fontlist;
2951 if(!strcmp((char*)l->swffont->name, font->id)) {
2952 return; // we already know this font
2956 l = (fontlist_t*)rfx_calloc(sizeof(fontlist_t));
2957 l->swffont = gfxfont_to_swffont(font, font->id, (i->config_flashversion>=8 && !NO_FONT3)?3:2);
2964 swf_FontSetID(l->swffont, getNewID(i->dev));
2966 if(getScreenLogLevel() >= LOGLEVEL_DEBUG) {
2968 // print font information
2969 msg("<debug> Font %s",font->id);
2970 msg("<debug> | ID: %d", l->swffont->id);
2971 msg("<debug> | Version: %d", l->swffont->version);
2972 msg("<debug> | Name: %s", l->swffont->name);
2973 msg("<debug> | Numchars: %d", l->swffont->numchars);
2974 msg("<debug> | Maxascii: %d", l->swffont->maxascii);
2975 msg("<debug> | Style: %d", l->swffont->style);
2976 msg("<debug> | Encoding: %d", l->swffont->encoding);
2977 for(iii=0; iii<l->swffont->numchars;iii++) {
2978 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,
2979 l->swffont->layout->bounds[iii].xmin/20.0,
2980 l->swffont->layout->bounds[iii].ymin/20.0,
2981 l->swffont->layout->bounds[iii].xmax/20.0,
2982 l->swffont->layout->bounds[iii].ymax/20.0
2988 static void swf_switchfont(gfxdevice_t*dev, const char*fontid)
2990 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
2992 if(i->swffont && i->swffont->name && !strcmp((char*)i->swffont->name,fontid))
2993 return; // the requested font is the current font
2995 fontlist_t*l = i->fontlist;
2997 if(!strcmp((char*)l->swffont->name, fontid)) {
2998 i->swffont = l->swffont;
3003 msg("<error> Unknown font id: %s", fontid);
3007 /* sets the matrix which is to be applied to characters drawn by swfoutput_drawchar() */
3008 static void setfontscale(gfxdevice_t*dev,double m11,double m12, double m21,double m22,double x, double y, char force)
3014 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
3015 if(i->lastfontm11 == m11 &&
3016 i->lastfontm12 == m12 &&
3017 i->lastfontm21 == m21 &&
3018 i->lastfontm22 == m22 && !force)
3023 i->lastfontm11 = m11;
3024 i->lastfontm12 = m12;
3025 i->lastfontm21 = m21;
3026 i->lastfontm22 = m22;
3028 double xsize = sqrt(m11*m11 + m12*m12);
3029 double ysize = sqrt(m21*m21 + m22*m22);
3032 if(i->config_flashversion>=8 && !NO_FONT3)
3035 i->current_font_size = (xsize>ysize?xsize:ysize)*extrazoom;
3036 if(i->current_font_size < 1)
3037 i->current_font_size = 1;
3040 swf_GetMatrix(0, &m);
3042 if(m21 || m12 || fabs(m11+m22)>0.001 || m11<0) {
3043 double ifs = (double)extrazoom/(i->current_font_size);
3044 m.sx = (S32)((m11*ifs)*65536); m.r1 = -(S32)((m21*ifs)*65536);
3045 m.r0 = (S32)((m12*ifs)*65536); m.sy = -(S32)((m22*ifs)*65536);
3048 /* this is the position of the first char to set a new fontmatrix-
3049 we hope that it's close enough to all other characters using the
3050 font, so we use its position as origin for the matrix */
3057 static void swf_drawchar(gfxdevice_t*dev, gfxfont_t*font, int glyph, gfxcolor_t*color, gfxmatrix_t*matrix)
3059 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
3061 msg("<error> swf_drawchar called (glyph %d) without font", glyph);
3065 if(i->config_drawonlyshapes) {
3066 gfxglyph_t*g = &font->glyphs[glyph];
3067 gfxline_t*line2 = gfxline_clone(g->line);
3068 gfxline_transform(line2, matrix);
3069 dev->fill(dev, line2, color);
3070 gfxline_free(line2);
3074 if(!i->swffont || !i->swffont->name || strcmp((char*)i->swffont->name,font->id)) // not equal to current font
3076 swf_switchfont(dev, font->id); // set the current font
3080 msg("<warning> swf_drawchar: Font is NULL");
3083 if(glyph<0 || glyph>=i->swffont->numchars) {
3084 msg("<warning> No character %d in font %s (%d chars)", glyph, FIXNULL((char*)i->swffont->name), i->swffont->numchars);
3087 glyph = i->swffont->glyph2glyph[glyph];
3089 setfontscale(dev, matrix->m00, matrix->m01, matrix->m10, matrix->m11, matrix->tx, matrix->ty, 0);
3091 double det = i->fontmatrix.sx/65536.0 * i->fontmatrix.sy/65536.0 -
3092 i->fontmatrix.r0/65536.0 * i->fontmatrix.r1/65536.0;
3093 if(fabs(det) < 0.0005) {
3094 /* x direction equals y direction- the text is invisible */
3095 msg("<verbose> Not drawing invisible character %d (det=%f, m=[%f %f;%f %f]\n", glyph,
3097 i->fontmatrix.sx/65536.0, i->fontmatrix.r1/65536.0,
3098 i->fontmatrix.r0/65536.0, i->fontmatrix.sy/65536.0);
3102 /*if(i->swffont->glyph[glyph].shape->bitlen <= 16) {
3103 msg("<warning> Glyph %d in current charset (%s, %d characters) is empty",
3104 glyph, FIXNULL((char*)i->swffont->name), i->swffont->numchars);
3108 /* calculate character position with respect to the current font matrix */
3109 double s = 20 * GLYPH_SCALE / det;
3110 double px = matrix->tx - i->fontmatrix.tx/20.0;
3111 double py = matrix->ty - i->fontmatrix.ty/20.0;
3112 int x = (SCOORD)(( px * i->fontmatrix.sy/65536.0 - py * i->fontmatrix.r1/65536.0)*s);
3113 int y = (SCOORD)((- px * i->fontmatrix.r0/65536.0 + py * i->fontmatrix.sx/65536.0)*s);
3114 if(x>32767 || x<-32768 || y>32767 || y<-32768) {
3115 msg("<verbose> Moving character origin to %f %f\n", matrix->tx, matrix->ty);
3117 setfontscale(dev, matrix->m00, matrix->m01, matrix->m10, matrix->m11, matrix->tx, matrix->ty, 1);
3118 /* since we just moved the char origin to the current char's position,
3119 it now has the relative position (0,0) */
3128 msg("<trace> Drawing char %d in font %d at %d,%d in color %02x%02x%02x%02x",
3129 glyph, i->swffont->id, x, y, color->r, color->g, color->b, color->a);
3131 if(color->a == 0 && i->config_invisibletexttofront) {
3132 RGBA color2 = *(RGBA*)color;
3133 if(i->config_flashversion>=8) {
3134 // use "multiply" blend mode
3135 color2.a = color2.r = color2.g = color2.b = 255;
3137 i->topchardata = charbuffer_append(i->topchardata, i->swffont, glyph, x, y, i->current_font_size, color2, &i->fontmatrix);
3139 i->chardata = charbuffer_append(i->chardata, i->swffont, glyph, x, y, i->current_font_size, *(RGBA*)color, &i->fontmatrix);
3141 swf_FontUseGlyph(i->swffont, glyph, i->current_font_size);