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(use_font3 && i->config_alignfonts) {
1479 // needs to be done before the reduce
1480 swf_FontCreateAlignZones(iterator->swffont);
1482 if(!i->config_storeallcharacters) {
1483 msg("<debug> Reducing font %s", iterator->swffont->name);
1484 swf_FontReduce(iterator->swffont);
1486 int used = iterator->swffont->use && iterator->swffont->use->used_glyphs;
1489 mtag = swf_InsertTag(mtag, ST_DEFINEFONT2);
1490 swf_FontSetDefine2(mtag, iterator->swffont);
1492 mtag = swf_InsertTag(mtag, ST_DEFINEFONT3);
1493 swf_FontSetDefine2(mtag, iterator->swffont);
1498 iterator = iterator->next;
1501 i->tag = swf_InsertTag(i->tag,ST_END);
1502 TAG* tag = i->tag->prev;
1504 if(i->config_storeallcharacters) {
1505 swf_FontPostprocess(i->swf); // generate alignment information
1508 /* remove the removeobject2 tags between the last ST_SHOWFRAME
1509 and the ST_END- they confuse the flash player */
1510 while(tag->id == ST_REMOVEOBJECT2) {
1511 TAG* prev = tag->prev;
1512 swf_DeleteTag(i->swf, tag);
1519 if(i->config_enablezlib || i->config_flashversion>=6) {
1520 i->swf->compressed = 1;
1523 /* Add AVM2 actionscript */
1524 if(i->config_flashversion>=9 &&
1525 (i->config_insertstoptag || i->hasbuttons) && !i->config_linknameurl) {
1526 swf_AddButtonLinks(i->swf, i->config_insertstoptag,
1527 i->config_internallinkfunction||i->config_externallinkfunction);
1529 // if(i->config_reordertags)
1530 // swf_Optimize(i->swf);
1533 int swfresult_save(gfxresult_t*gfx, const char*filename)
1535 SWF*swf = (SWF*)gfx->internal;
1538 fi = open(filename, O_BINARY|O_CREAT|O_TRUNC|O_WRONLY, 0777);
1543 msg("<fatal> Could not create \"%s\". ", FIXNULL(filename));
1547 if FAILED(swf_WriteSWF(fi,swf))
1548 msg("<error> WriteSWF() failed.\n");
1554 void* swfresult_get(gfxresult_t*gfx, const char*name)
1556 SWF*swf = (SWF*)gfx->internal;
1557 if(!strcmp(name, "swf")) {
1558 return (void*)swf_CopySWF(swf);
1559 } else if(!strcmp(name, "xmin")) {
1560 return (void*)(ptroff_t)(swf->movieSize.xmin/20);
1561 } else if(!strcmp(name, "ymin")) {
1562 return (void*)(ptroff_t)(swf->movieSize.ymin/20);
1563 } else if(!strcmp(name, "xmax")) {
1564 return (void*)(ptroff_t)(swf->movieSize.xmax/20);
1565 } else if(!strcmp(name, "ymax")) {
1566 return (void*)(ptroff_t)(swf->movieSize.ymax/20);
1567 } else if(!strcmp(name, "width")) {
1568 return (void*)(ptroff_t)((swf->movieSize.xmax - swf->movieSize.xmin)/20);
1569 } else if(!strcmp(name, "height")) {
1570 return (void*)(ptroff_t)((swf->movieSize.ymax - swf->movieSize.ymin)/20);
1574 void swfresult_destroy(gfxresult_t*gfx)
1577 swf_FreeTags((SWF*)gfx->internal);
1578 free(gfx->internal);
1581 memset(gfx, 0, sizeof(gfxresult_t));
1585 static void swfoutput_destroy(gfxdevice_t* dev);
1587 gfxresult_t* swf_finish(gfxdevice_t* dev)
1589 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1592 if(i->config_linktarget) {
1593 free(i->config_linktarget);
1594 i->config_linktarget = 0;
1597 swfoutput_finalize(dev);
1598 SWF* swf = i->swf;i->swf = 0;
1599 swfoutput_destroy(dev);
1601 result = (gfxresult_t*)rfx_calloc(sizeof(gfxresult_t));
1602 result->internal = swf;
1603 result->save = swfresult_save;
1605 result->get = swfresult_get;
1606 result->destroy = swfresult_destroy;
1610 /* Perform cleaning up */
1611 static void swfoutput_destroy(gfxdevice_t* dev)
1613 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1615 /* not initialized yet- nothing to destroy */
1619 fontlist_t *tmp,*iterator = i->fontlist;
1621 if(iterator->swffont) {
1622 swf_FontFree(iterator->swffont);iterator->swffont=0;
1625 iterator = iterator->next;
1628 if(i->swf) {swf_FreeTags(i->swf);free(i->swf);i->swf = 0;}
1631 memset(dev, 0, sizeof(gfxdevice_t));
1634 static void swfoutput_setstrokecolor(gfxdevice_t* dev, U8 r, U8 g, U8 b, U8 a)
1636 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1637 if(i->strokergb.r == r &&
1638 i->strokergb.g == g &&
1639 i->strokergb.b == b &&
1640 i->strokergb.a == a) return;
1650 //#define ROUND_UP 19
1651 //#define ROUND_UP 10
1653 static void swfoutput_setlinewidth(gfxdevice_t*dev, double _linewidth)
1655 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1656 if(i->linewidth == (U16)(_linewidth*20+19.0/20.0))
1660 i->linewidth = (U16)(_linewidth*20+19.0/20.0);
1664 static void drawlink(gfxdevice_t*dev, ActionTAG*,ActionTAG*, gfxline_t*points, char mouseover, char*type, const char*url);
1665 static void swfoutput_namedlink(gfxdevice_t*dev, char*name, gfxline_t*points);
1666 static void swfoutput_linktopage(gfxdevice_t*dev, int page, gfxline_t*points);
1667 static void swfoutput_linktourl(gfxdevice_t*dev, const char*url, gfxline_t*points);
1669 /*void swfoutput_drawlink(gfxdevice_t*dev, char*url, gfxline_t*points)
1671 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1672 dev->drawlink(dev, points, url);
1675 void swf_drawlink(gfxdevice_t*dev, gfxline_t*points, const char*url)
1677 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1679 if(i->config_disablelinks)
1682 if(!strncmp("http://pdf2swf:", url, 15)) {
1683 char*tmp = strdup(url);
1684 int l = strlen(tmp);
1687 swfoutput_namedlink(dev, tmp+15, points);
1690 } else if(!strncmp("page", url, 4)) {
1693 if(url[t]<'0' || url[t]>'9')
1696 int page = atoi(&url[4]);
1697 if(page<0) page = 0;
1698 swfoutput_linktopage(dev, page, points);
1701 swfoutput_linktourl(dev, url, points);
1704 void swfoutput_linktourl(gfxdevice_t*dev, const char*url, gfxline_t*points)
1706 ActionTAG* actions = 0;
1707 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1713 /* TODO: escape special characters in url */
1715 if(i->config_externallinkfunction && i->config_flashversion<=8) {
1716 actions = action_PushString(actions, url); //parameter
1717 actions = action_PushInt(actions, 1); //number of parameters (1)
1718 actions = action_PushString(actions, i->config_externallinkfunction); //function name
1719 actions = action_CallFunction(actions);
1720 } else if(!i->config_linktarget) {
1721 if(!i->config_opennewwindow)
1722 actions = action_GetUrl(actions, url, "_parent");
1724 actions = action_GetUrl(actions, url, "_this");
1726 actions = action_GetUrl(actions, url, i->config_linktarget);
1728 actions = action_End(actions);
1730 drawlink(dev, actions, 0, points, 0, "url", url);
1732 swf_ActionFree(actions);
1734 void swfoutput_linktopage(gfxdevice_t*dev, int page, gfxline_t*points)
1736 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1737 ActionTAG* actions = 0;
1744 if(!i->config_internallinkfunction || i->config_flashversion>=9) {
1745 actions = action_GotoFrame(actions, page-1);
1746 actions = action_End(actions);
1748 actions = action_PushInt(actions, page); //parameter
1749 actions = action_PushInt(actions, 1); //number of parameters (1)
1750 actions = action_PushString(actions, i->config_internallinkfunction); //function name
1751 actions = action_CallFunction(actions);
1752 actions = action_End(actions);
1756 sprintf(name, "page%d", page);
1758 drawlink(dev, actions, 0, points, 0, "page", name);
1760 swf_ActionFree(actions);
1763 /* Named Links (a.k.a. Acrobatmenu) are used to implement various gadgets
1764 of the viewer objects, like subtitles, index elements etc.
1766 void swfoutput_namedlink(gfxdevice_t*dev, char*name, gfxline_t*points)
1768 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1769 ActionTAG *actions1,*actions2;
1770 char*tmp = strdup(name);
1779 if(!strncmp(tmp, "call:", 5))
1781 char*x = strchr(&tmp[5], ':');
1783 actions1 = action_PushInt(0, 0); //number of parameters (0)
1784 actions1 = action_PushString(actions1, &tmp[5]); //function name
1785 actions1 = action_CallFunction(actions1);
1786 actions1 = action_End(actions1);
1789 actions1 = action_PushString(0, x+1); //parameter
1790 actions1 = action_PushInt(actions1, 1); //number of parameters (1)
1791 actions1 = action_PushString(actions1, &tmp[5]); //function name
1792 actions1 = action_CallFunction(actions1);
1793 actions1 = action_End(actions1);
1795 actions2 = action_End(0);
1801 actions1 = action_PushString(0, "/:subtitle");
1802 actions1 = action_PushString(actions1, name);
1803 actions1 = action_SetVariable(actions1);
1804 actions1 = action_End(actions1);
1806 actions2 = action_PushString(0, "/:subtitle");
1807 actions2 = action_PushString(actions2, "");
1808 actions2 = action_SetVariable(actions2);
1809 actions2 = action_End(actions2);
1813 drawlink(dev, actions1, actions2, points, mouseover, type, name);
1815 swf_ActionFree(actions1);
1816 swf_ActionFree(actions2);
1820 static void drawgfxline(gfxdevice_t*dev, gfxline_t*line, int fill)
1822 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1823 gfxcoord_t lastx=0,lasty=0,px=0,py=0;
1825 int lines= 0, splines=0;
1832 /* check whether the next segment is zero */
1833 if(line->type == gfx_moveTo) {
1834 moveto(dev, i->tag, line->x, line->y);
1835 px = lastx = line->x;
1836 py = lasty = line->y;
1838 } if(line->type == gfx_lineTo) {
1839 lineto(dev, i->tag, line->x, line->y);
1844 } else if(line->type == gfx_splineTo) {
1846 s.x = line->sx;p.x = line->x;
1847 s.y = line->sy;p.y = line->y;
1848 splineto(dev, i->tag, s, p);
1856 msg("<trace> drawgfxline, %d lines, %d splines", lines, splines);
1860 static void drawlink(gfxdevice_t*dev, ActionTAG*actions1, ActionTAG*actions2, gfxline_t*points, char mouseover, char*type, const char*url)
1862 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1871 int buttonid = getNewID(dev);
1872 gfxbbox_t bbox = gfxline_getbbox(points);
1874 if(i->config_linknameurl) {
1882 myshapeid = getNewID(dev);
1883 i->tag = swf_InsertTag(i->tag,ST_DEFINESHAPE3);
1884 swf_ShapeNew(&i->shape);
1885 rgb.r = rgb.b = rgb.a = rgb.g = 0;
1886 fsid = swf_ShapeAddSolidFillStyle(i->shape,&rgb);
1887 swf_SetU16(i->tag, myshapeid);
1888 r.xmin = (int)(bbox.xmin*20);
1889 r.ymin = (int)(bbox.ymin*20);
1890 r.xmax = (int)(bbox.xmax*20);
1891 r.ymax = (int)(bbox.ymax*20);
1892 r = swf_ClipRect(i->pagebbox, r);
1893 swf_SetRect(i->tag,&r);
1894 swf_SetShapeStyles(i->tag,i->shape);
1895 swf_ShapeCountBits(i->shape,NULL,NULL);
1896 swf_SetShapeBits(i->tag,i->shape);
1897 swf_ShapeSetAll(i->tag,i->shape,/*x*/0,/*y*/0,0,fsid,0);
1898 i->swflastx = i->swflasty = 0;
1899 drawgfxline(dev, points, 1);
1900 swf_ShapeSetEnd(i->tag);
1901 swf_ShapeFree(i->shape);
1904 myshapeid2 = getNewID(dev);
1905 i->tag = swf_InsertTag(i->tag,ST_DEFINESHAPE3);
1906 swf_ShapeNew(&i->shape);
1908 rgb = i->config_linkcolor;
1910 fsid = swf_ShapeAddSolidFillStyle(i->shape,&rgb);
1911 swf_SetU16(i->tag, myshapeid2);
1912 r.xmin = (int)(bbox.xmin*20);
1913 r.ymin = (int)(bbox.ymin*20);
1914 r.xmax = (int)(bbox.xmax*20);
1915 r.ymax = (int)(bbox.ymax*20);
1916 r = swf_ClipRect(i->pagebbox, r);
1917 swf_SetRect(i->tag,&r);
1918 swf_SetShapeStyles(i->tag,i->shape);
1919 swf_ShapeCountBits(i->shape,NULL,NULL);
1920 swf_SetShapeBits(i->tag,i->shape);
1921 swf_ShapeSetAll(i->tag,i->shape,/*x*/0,/*y*/0,0,fsid,0);
1922 i->swflastx = i->swflasty = 0;
1923 drawgfxline(dev, points, 1);
1924 swf_ShapeSetEnd(i->tag);
1925 swf_ShapeFree(i->shape);
1929 i->tag = swf_InsertTag(i->tag,ST_DEFINEBUTTON);
1930 swf_SetU16(i->tag,buttonid); //id
1931 swf_ButtonSetFlags(i->tag, 0); //menu=no
1932 swf_ButtonSetRecord(i->tag,0x01,myshapeid,i->depth,0,0);
1933 swf_ButtonSetRecord(i->tag,0x02,myshapeid2,i->depth,0,0);
1934 swf_ButtonSetRecord(i->tag,0x04,myshapeid2,i->depth,0,0);
1935 swf_ButtonSetRecord(i->tag,0x08,myshapeid,i->depth,0,0);
1936 swf_SetU8(i->tag,0);
1937 swf_ActionSet(i->tag,actions1);
1938 swf_SetU8(i->tag,0);
1942 i->tag = swf_InsertTag(i->tag,ST_DEFINEBUTTON2);
1943 swf_SetU16(i->tag,buttonid); //id
1944 swf_ButtonSetFlags(i->tag, 0); //menu=no
1945 swf_ButtonSetRecord(i->tag,0x01,myshapeid,i->depth,0,0);
1946 swf_ButtonSetRecord(i->tag,0x02,myshapeid2,i->depth,0,0);
1947 swf_ButtonSetRecord(i->tag,0x04,myshapeid2,i->depth,0,0);
1948 swf_ButtonSetRecord(i->tag,0x08,myshapeid,i->depth,0,0);
1949 swf_SetU8(i->tag,0); // end of button records
1950 swf_ButtonSetCondition(i->tag, BC_IDLE_OVERUP);
1951 swf_ActionSet(i->tag,actions1);
1953 swf_ButtonSetCondition(i->tag, BC_OVERUP_IDLE);
1954 swf_ActionSet(i->tag,actions2);
1955 swf_SetU8(i->tag,0);
1956 swf_ButtonPostProcess(i->tag, 2);
1958 swf_SetU8(i->tag,0);
1959 swf_ButtonPostProcess(i->tag, 1);
1965 const char* name = 0;
1966 if(i->config_linknameurl) {
1967 buf2 = malloc(strlen(type)+strlen(url)+2);
1968 sprintf(buf2, "%s:%s", type, url);
1972 sprintf(buf, "button%d", buttonid);
1975 msg("<trace> Placing link ID %d", buttonid);
1976 i->tag = swf_InsertTag(i->tag,ST_PLACEOBJECT2);
1978 if(posx!=0 || posy!=0) {
1980 p.x = (int)(posx*20);
1981 p.y = (int)(posy*20);
1982 p = swf_TurnPoint(p, &i->page_matrix);
1987 swf_ObjectPlace(i->tag, buttonid, getNewDepth(dev),&m,0,(U8*)name);
1989 swf_ObjectPlace(i->tag, buttonid, getNewDepth(dev),&i->page_matrix,0,(U8*)name);
1999 for(t=0;t<picpos;t++)
2001 if(pic_xids[t] == xid &&
2002 pic_yids[t] == yid) {
2003 width = pic_width[t];
2004 height = pic_height[t];
2008 pic_ids[picpos] = swfoutput_drawimagelosslessN(&output, pic, pal, width, height, x1,y1,x2,y2,x3,y3,x4,y4, numpalette);
2009 pic_xids[picpos] = xid;
2010 pic_yids[picpos] = yid;
2011 pic_width[picpos] = width;
2012 pic_height[picpos] = height;
2015 pic[width*y+x] = buf[0];
2019 xid += pal[1].r*3 + pal[1].g*11 + pal[1].b*17;
2020 yid += pal[1].r*7 + pal[1].g*5 + pal[1].b*23;
2024 xid += x*r+x*b*3+x*g*7+x*a*11;
2025 yid += y*r*3+y*b*17+y*g*19+y*a*11;
2027 for(t=0;t<picpos;t++)
2029 if(pic_xids[t] == xid &&
2030 pic_yids[t] == yid) {
2039 int swf_setparameter(gfxdevice_t*dev, const char*name, const char*value)
2041 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
2043 msg("<trace> swfdevice: %s=%s", name, value);
2044 if(!strcmp(name, "jpegsubpixels")) {
2045 i->config_jpegsubpixels = atof(value);
2046 } else if(!strcmp(name, "ppmsubpixels")) {
2047 i->config_ppmsubpixels = atof(value);
2048 } else if(!strcmp(name, "subpixels")) {
2049 i->config_ppmsubpixels = i->config_jpegsubpixels = atof(value);
2050 } else if(!strcmp(name, "drawonlyshapes")) {
2051 i->config_drawonlyshapes = atoi(value);
2052 } else if(!strcmp(name, "ignoredraworder")) {
2053 i->config_ignoredraworder = atoi(value);
2054 } else if(!strcmp(name, "mark")) {
2055 if(!value || !value[0]) {
2056 if(i->mark) free(i->mark);
2060 i->mark = strdup("...");
2061 for(t=0;t<3;t++) if(value[t]) i->mark[t] = value[t];
2063 } else if(!strcmp(name, "filloverlap")) {
2064 i->config_filloverlap = atoi(value);
2065 } else if(!strcmp(name, "linksopennewwindow")) {
2066 i->config_opennewwindow = atoi(value);
2067 } else if(!strcmp(name, "opennewwindow")) {
2068 i->config_opennewwindow = atoi(value);
2069 } else if(!strcmp(name, "storeallcharacters")) {
2070 i->config_storeallcharacters = atoi(value);
2071 } else if(!strcmp(name, "enablezlib")) {
2072 i->config_enablezlib = atoi(value);
2073 } else if(!strcmp(name, "bboxvars")) {
2074 i->config_bboxvars = atoi(value);
2075 } else if(!strcmp(name, "dots")) {
2076 i->config_dots = atoi(value);
2077 } else if(!strcmp(name, "frameresets")) {
2078 i->config_frameresets = atoi(value);
2079 } else if(!strcmp(name, "showclipshapes")) {
2080 i->config_showclipshapes = atoi(value);
2081 } else if(!strcmp(name, "reordertags")) {
2082 i->config_reordertags = atoi(value);
2083 } else if(!strcmp(name, "internallinkfunction")) {
2084 i->config_internallinkfunction = strdup(value);
2085 } else if(!strcmp(name, "externallinkfunction")) {
2086 i->config_externallinkfunction = strdup(value);
2087 } else if(!strcmp(name, "linkfunction")) { //sets both internallinkfunction and externallinkfunction
2088 i->config_internallinkfunction = strdup(value);
2089 i->config_externallinkfunction = strdup(value);
2090 } else if(!strcmp(name, "disable_polygon_conversion")) {
2091 i->config_disable_polygon_conversion = atoi(value);
2092 } else if(!strcmp(name, "normalize_polygon_positions")) {
2093 i->config_normalize_polygon_positions = atoi(value);
2094 } else if(!strcmp(name, "wxwindowparams")) {
2095 i->config_watermark = atoi(value);
2096 } else if(!strcmp(name, "insertstop")) {
2097 i->config_insertstoptag = atoi(value);
2098 } else if(!strcmp(name, "protect")) {
2099 i->config_protect = atoi(value);
2100 if(i->config_protect && i->tag) {
2101 i->tag = swf_InsertTag(i->tag, ST_PROTECT);
2103 } else if(!strcmp(name, "flashversion")) {
2104 i->config_flashversion = atoi(value);
2106 i->swf->fileVersion = i->config_flashversion;
2108 } else if(!strcmp(name, "framerate")) {
2109 i->config_framerate = atof(value);
2111 i->swf->frameRate = i->config_framerate*0x100;
2113 } else if(!strcmp(name, "minlinewidth")) {
2114 i->config_minlinewidth = atof(value);
2115 } else if(!strcmp(name, "caplinewidth")) {
2116 i->config_caplinewidth = atof(value);
2117 } else if(!strcmp(name, "linktarget")) {
2118 i->config_linktarget = strdup(value);
2119 } else if(!strcmp(name, "invisibletexttofront")) {
2120 i->config_invisibletexttofront = atoi(value);
2121 } else if(!strcmp(name, "noclips")) {
2122 i->config_noclips = atoi(value);
2123 } else if(!strcmp(name, "dumpfonts")) {
2124 i->config_dumpfonts = atoi(value);
2125 } else if(!strcmp(name, "animate")) {
2126 i->config_animate = 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 newpic = swf_ImageScale(mem, sizex, sizey, newsizex, newsizey);
2263 *newwidth = sizex = newsizex;
2264 *newheight = sizey = newsizey;
2267 *newwidth = newsizex = sizex;
2268 *newheight = newsizey = sizey;
2271 int num_colors = swf_ImageGetNumberOfPaletteEntries(mem,sizex,sizey,0);
2272 int has_alpha = swf_ImageHasAlpha(mem,sizex,sizey);
2274 msg("<verbose> Drawing %dx%d %s%simage (id %d) at size %dx%d (%dx%d), %s%d colors",
2276 has_alpha?(has_alpha==2?"semi-transparent ":"transparent "):"",
2277 is_jpeg?"jpeg-":"", i->currentswfid+1,
2279 targetwidth, targetheight,
2280 /*newsizex, newsizey,*/
2281 num_colors>256?">":"", num_colors>256?256:num_colors);
2283 /*RGBA* pal = (RGBA*)rfx_alloc(sizeof(RGBA)*num_colors);
2284 swf_ImageGetNumberOfPaletteEntries(mem,sizex,sizey,pal);
2286 for(t=0;t<num_colors;t++) {
2287 printf("%02x%02x%02x%02x ",
2288 pal[t].r, pal[t].g, pal[t].b, pal[t].a);
2295 int cacheid = imageInCache(dev, mem, sizex, sizey);
2298 bitid = getNewID(dev);
2300 i->tag = swf_AddImage(i->tag, bitid, mem, sizex, sizey, i->config_jpegquality);
2301 addImageToCache(dev, mem, sizex, sizey);
2311 static SRECT gfxline_getSWFbbox(gfxline_t*line)
2313 gfxbbox_t bbox = gfxline_getbbox(line);
2315 r.xmin = (int)(bbox.xmin*20);
2316 r.ymin = (int)(bbox.ymin*20);
2317 r.xmax = (int)(bbox.xmax*20);
2318 r.ymax = (int)(bbox.ymax*20);
2322 int line_is_empty(gfxline_t*line)
2325 if(line->type != gfx_moveTo)
2332 static void swf_fillbitmap(gfxdevice_t*dev, gfxline_t*line, gfximage_t*img, gfxmatrix_t*matrix, gfxcxform_t*cxform)
2334 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
2336 if(line_is_empty(line))
2342 int targetx = (int)(sqrt(matrix->m00*matrix->m00 + matrix->m01*matrix->m01)*img->width);
2343 int targety = (int)(sqrt(matrix->m10*matrix->m10 + matrix->m11*matrix->m11)*img->height);
2345 int newwidth=0,newheight=0;
2346 int bitid = add_image(i, img, targetx, targety, &newwidth, &newheight);
2349 double fx = (double)img->width / (double)newwidth;
2350 double fy = (double)img->height / (double)newheight;
2353 m.sx = (int)(65536*20*matrix->m00*fx); m.r1 = (int)(65536*20*matrix->m10*fy);
2354 m.r0 = (int)(65536*20*matrix->m01*fx); m.sy = (int)(65536*20*matrix->m11*fy);
2355 m.tx = (int)(matrix->tx*20);
2356 m.ty = (int)(matrix->ty*20);
2359 int myshapeid = getNewID(dev);
2360 i->tag = swf_InsertTag(i->tag,ST_DEFINESHAPE);
2362 swf_ShapeNew(&shape);
2363 int fsid = swf_ShapeAddBitmapFillStyle(shape,&m,bitid,1);
2364 swf_SetU16(i->tag, myshapeid);
2365 SRECT r = gfxline_getSWFbbox(line);
2366 r = swf_ClipRect(i->pagebbox, r);
2367 swf_SetRect(i->tag,&r);
2368 swf_SetShapeStyles(i->tag,shape);
2369 swf_ShapeCountBits(shape,NULL,NULL);
2370 swf_SetShapeBits(i->tag,shape);
2371 swf_ShapeSetAll(i->tag,shape,UNDEFINED_COORD,UNDEFINED_COORD,0,fsid,0);
2372 i->swflastx = i->swflasty = UNDEFINED_COORD;
2373 drawgfxline(dev, line, 1);
2374 swf_ShapeSetEnd(i->tag);
2375 swf_ShapeFree(shape);
2377 msg("<trace> Placing image, shape ID %d, bitmap ID %d", myshapeid, bitid);
2378 i->tag = swf_InsertTag(i->tag,ST_PLACEOBJECT2);
2379 CXFORM cxform2 = gfxcxform_to_cxform(cxform);
2380 swf_ObjectPlace(i->tag,myshapeid,getNewDepth(dev),&i->page_matrix,&cxform2,NULL);
2383 static RGBA col_black = {255,0,0,0};
2385 static void drawoutline(gfxdevice_t*dev, gfxline_t*line)
2387 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
2389 int myshapeid = getNewID(dev);
2390 i->tag = swf_InsertTag(i->tag,ST_DEFINESHAPE3);
2393 swf_ShapeNew(&shape);
2394 int lsid = swf_ShapeAddLineStyle(shape,1,&col_black);
2396 swf_SetU16(i->tag,myshapeid);
2397 SRECT r = gfxline_getSWFbbox(line);
2398 r = swf_ClipRect(i->pagebbox, r);
2399 swf_SetRect(i->tag,&r);
2400 swf_SetShapeStyles(i->tag,shape);
2401 swf_ShapeCountBits(shape,NULL,NULL);
2402 swf_SetShapeBits(i->tag,shape);
2403 swf_ShapeSetAll(i->tag,shape,UNDEFINED_COORD,UNDEFINED_COORD,lsid,0,0);
2404 drawgfxline(dev, line, 1);
2405 swf_ShapeSetEnd(i->tag);
2406 swf_ShapeFree(shape);
2408 i->tag = swf_InsertTag(i->tag,ST_PLACEOBJECT2);
2409 swf_ObjectPlace(i->tag, myshapeid, getNewDepth(dev), 0,0,0);
2412 static void swf_startclip(gfxdevice_t*dev, gfxline_t*line)
2414 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
2415 if(i->config_noclips)
2421 if(i->clippos >= 127)
2423 msg("<warning> Too many clip levels.");
2427 if(i->config_showclipshapes)
2428 drawoutline(dev, line);
2430 int myshapeid = getNewID(dev);
2431 i->tag = swf_InsertTag(i->tag,ST_DEFINESHAPE3);
2433 memset(&col, 0, sizeof(RGBA));
2436 swf_ShapeNew(&shape);
2437 int fsid = swf_ShapeAddSolidFillStyle(shape,&col);
2439 RGBA markcol = {0,i->mark[0],i->mark[1],i->mark[2]};
2440 swf_ShapeAddSolidFillStyle(shape,&markcol);
2442 swf_SetU16(i->tag,myshapeid);
2443 SRECT r = gfxline_getSWFbbox(line);
2444 r = swf_ClipRect(i->pagebbox, r);
2445 swf_SetRect(i->tag,&r);
2446 swf_SetShapeStyles(i->tag,shape);
2447 swf_ShapeCountBits(shape,NULL,NULL);
2448 swf_SetShapeBits(i->tag,shape);
2449 swf_ShapeSetAll(i->tag,shape,UNDEFINED_COORD,UNDEFINED_COORD,0,fsid,0);
2450 i->swflastx = i->swflasty = UNDEFINED_COORD;
2451 i->shapeisempty = 1;
2452 drawgfxline(dev, line, 1);
2453 if(i->shapeisempty) {
2454 /* an empty clip shape is equivalent to a shape with no area */
2455 int x = line?line->x:0;
2456 int y = line?line->y:0;
2457 moveto(dev, i->tag, x,y);
2458 lineto(dev, i->tag, x,y);
2459 lineto(dev, i->tag, x,y);
2461 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)) {
2462 if(i->config_watermark) {
2463 gfxbbox_t r; r.xmin = r.ymin = 0;r.xmax = i->max_x;r.ymax = i->max_y;
2464 draw_watermark(dev, r, 1);
2467 swf_ShapeSetEnd(i->tag);
2468 swf_ShapeFree(shape);
2470 /* TODO: remember the bbox, and check all shapes against it */
2472 msg("<trace> Placing clip ID %d", myshapeid);
2473 i->tag = swf_InsertTag(i->tag,ST_PLACEOBJECT2);
2474 i->cliptags[i->clippos] = i->tag;
2475 i->clipshapes[i->clippos] = myshapeid;
2476 i->clipdepths[i->clippos] = getNewDepth(dev);
2480 static void swf_endclip(gfxdevice_t*dev)
2482 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
2483 if(i->config_noclips)
2491 msg("<error> Invalid end of clipping region");
2495 /*swf_ObjectPlaceClip(i->cliptags[i->clippos],i->clipshapes[i->clippos],i->clipdepths[i->clippos],&i->page_matrix,NULL,NULL,
2496 / * clip to depth: * / i->depth <= i->clipdepths[i->clippos]? i->depth : i->depth - 1);
2498 swf_ObjectPlaceClip(i->cliptags[i->clippos],i->clipshapes[i->clippos],i->clipdepths[i->clippos],&i->page_matrix,NULL,NULL,i->depth);
2500 static int gfxline_type(gfxline_t*line)
2506 int haszerosegments=0;
2509 if(line->type == gfx_moveTo) {
2512 } else if(line->type == gfx_lineTo) {
2516 } else if(line->type == gfx_splineTo) {
2518 if(tmpsplines>lines)
2526 if(lines==0 && splines==0) return 0;
2527 else if(lines==1 && splines==0) return 1;
2528 else if(lines==0 && splines==1) return 2;
2529 else if(splines==0) return 3;
2533 static int gfxline_has_dots(gfxline_t*line)
2541 if(line->type == gfx_moveTo) {
2542 /* test the length of the preceding line, and assume it is a dot if
2543 it's length is less than 1.0. But *only* if there's a noticable
2544 gap between the previous line and the next moveTo. (I've come
2545 across a PDF where thousands of "dots" were stringed together,
2547 int last_short_gap = short_gap;
2548 if((fabs(line->x - x) + fabs(line->y - y)) < 1.0) {
2553 if(isline && dist < 1 && !short_gap && !last_short_gap) {
2558 } else if(line->type == gfx_lineTo) {
2559 dist += fabs(line->x - x) + fabs(line->y - y);
2561 } else if(line->type == gfx_splineTo) {
2562 dist += fabs(line->sx - x) + fabs(line->sy - y) +
2563 fabs(line->x - line->sx) + fabs(line->y - line->sy);
2570 if(isline && dist < 1 && !short_gap) {
2576 static int gfxline_fix_short_edges(gfxline_t*line)
2580 if(line->type == gfx_lineTo) {
2581 if(fabs(line->x - x) + fabs(line->y - y) < 0.01) {
2584 } else if(line->type == gfx_splineTo) {
2585 if(fabs(line->sx - x) + fabs(line->sy - y) +
2586 fabs(line->x - line->sx) + fabs(line->y - line->sy) < 0.01) {
2597 static char is_inside_page(gfxdevice_t*dev, gfxcoord_t x, gfxcoord_t y)
2599 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
2600 if(x<i->min_x || x>i->max_x) return 0;
2601 if(y<i->min_y || y>i->max_y) return 0;
2605 gfxline_t* gfxline_move(gfxline_t*line, double x, double y)
2607 gfxline_t*l = line = gfxline_clone(line);
2619 //#define NORMALIZE_POLYGON_POSITIONS
2621 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)
2623 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
2624 if(line_is_empty(line))
2626 int type = gfxline_type(line);
2627 int has_dots = gfxline_has_dots(line);
2628 gfxbbox_t r = gfxline_getbbox(line);
2629 int is_outside_page = !is_inside_page(dev, r.xmin, r.ymin) || !is_inside_page(dev, r.xmax, r.ymax);
2631 /* TODO: * split line into segments, and perform this check for all segments */
2633 if(i->config_disable_polygon_conversion || /*type>=5 ||*/
2635 (width <= i->config_caplinewidth
2636 || (cap_style == gfx_capRound && joint_style == gfx_joinRound)
2637 || (cap_style == gfx_capRound && type<=2))))
2641 /* convert line to polygon */
2642 msg("<trace> draw as polygon, type=%d dots=%d", type, has_dots);
2644 gfxline_fix_short_edges(line);
2645 /* we need to convert the line into a polygon */
2646 gfxpoly_t* poly = gfxpoly_from_stroke(line, width, cap_style, joint_style, miterLimit, DEFAULT_GRID);
2647 gfxline_t*gfxline = gfxline_from_gfxpoly(poly);
2648 dev->fill(dev, gfxline, color);
2649 gfxline_free(gfxline);
2650 gfxpoly_destroy(poly);
2654 msg("<trace> draw as stroke, type=%d dots=%d", type, has_dots);
2657 if(i->config_normalize_polygon_positions) {
2659 double startx = 0, starty = 0;
2660 if(line && line->type == gfx_moveTo) {
2664 line = gfxline_move(line, -startx, -starty);
2665 i->shapeposx = (int)(startx*20);
2666 i->shapeposy = (int)(starty*20);
2669 swfoutput_setstrokecolor(dev, color->r, color->g, color->b, color->a);
2670 swfoutput_setlinewidth(dev, width);
2673 drawgfxline(dev, line, 0);
2675 if(i->config_normalize_polygon_positions) {
2676 free(line); //account for _move
2681 static void swf_fill(gfxdevice_t*dev, gfxline_t*line, gfxcolor_t*color)
2683 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
2684 if(line_is_empty(line))
2688 gfxbbox_t r = gfxline_getbbox(line);
2689 int is_outside_page = !is_inside_page(dev, r.xmin, r.ymin) || !is_inside_page(dev, r.xmax, r.ymax);
2693 if(!i->config_ignoredraworder)
2696 if(i->config_normalize_polygon_positions) {
2698 double startx = 0, starty = 0;
2699 if(line && line->type == gfx_moveTo) {
2703 line = gfxline_move(line, -startx, -starty);
2704 i->shapeposx = (int)(startx*20);
2705 i->shapeposy = (int)(starty*20);
2708 swfoutput_setfillcolor(dev, color->r, color->g, color->b, color->a);
2711 drawgfxline(dev, line, 1);
2713 if(i->currentswfid==2 && r.xmin==0 && r.ymin==0 && r.xmax==i->max_x && r.ymax==i->max_y) {
2714 if(i->config_watermark) {
2715 draw_watermark(dev, r, 1);
2719 msg("<trace> end of swf_fill (shapeid=%d)", i->shapeid);
2721 if(i->config_normalize_polygon_positions) {
2722 free(line); //account for _move
2726 static GRADIENT* gfxgradient_to_GRADIENT(gfxgradient_t*gradient)
2729 gfxgradient_t*g = gradient;
2734 GRADIENT* swfgradient = malloc(sizeof(GRADIENT));
2735 swfgradient->num = num;
2736 swfgradient->rgba = malloc(sizeof(swfgradient->rgba[0])*num);
2737 swfgradient->ratios = malloc(sizeof(swfgradient->ratios[0])*num);
2742 swfgradient->ratios[num] = g->pos*255;
2743 swfgradient->rgba[num] = *(RGBA*)&g->color;
2750 static void swf_fillgradient(gfxdevice_t*dev, gfxline_t*line, gfxgradient_t*gradient, gfxgradienttype_t type, gfxmatrix_t*matrix)
2752 if(line_is_empty(line))
2754 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
2756 if(line_is_empty(line))
2759 GRADIENT* swfgradient = gfxgradient_to_GRADIENT(gradient);
2766 double f = type==gfxgradient_radial?4:4;
2768 m.sx = (int)(matrix->m00*20*f); m.r1 = (int)(matrix->m10*20*f);
2769 m.r0 = (int)(matrix->m01*20*f); m.sy = (int)(matrix->m11*20*f);
2770 m.tx = (int)(matrix->tx*20);
2771 m.ty = (int)(matrix->ty*20);
2774 int myshapeid = getNewID(dev);
2775 i->tag = swf_InsertTag(i->tag, ST_DEFINESHAPE2);
2777 swf_ShapeNew(&shape);
2778 int fsid = swf_ShapeAddGradientFillStyle(shape,&m,swfgradient,type==gfxgradient_radial);
2779 swf_SetU16(i->tag, myshapeid);
2780 SRECT r = gfxline_getSWFbbox(line);
2781 r = swf_ClipRect(i->pagebbox, r);
2782 swf_SetRect(i->tag,&r);
2783 swf_SetShapeStyles(i->tag,shape);
2784 swf_ShapeCountBits(shape,NULL,NULL);
2785 swf_SetShapeBits(i->tag,shape);
2786 swf_ShapeSetAll(i->tag,shape,UNDEFINED_COORD,UNDEFINED_COORD,0,fsid,0);
2787 i->swflastx = i->swflasty = UNDEFINED_COORD;
2788 drawgfxline(dev, line, 1);
2789 swf_ShapeSetEnd(i->tag);
2790 swf_ShapeFree(shape);
2792 int depth = getNewDepth(dev);
2793 msg("<trace> Placing gradient, shape ID %d, depth %d", myshapeid, depth);
2794 i->tag = swf_InsertTag(i->tag,ST_PLACEOBJECT2);
2795 swf_ObjectPlace(i->tag,myshapeid,depth,&i->page_matrix,NULL,NULL);
2797 swf_FreeGradient(swfgradient);free(swfgradient);
2800 static SWFFONT* gfxfont_to_swffont(gfxfont_t*font, const char* id, int version)
2802 SWFFONT*swffont = (SWFFONT*)rfx_calloc(sizeof(SWFFONT));
2804 SRECT bounds = {0,0,0,0};
2806 swffont->version = version;
2807 swffont->name = (U8*)strdup(id);
2808 swffont->layout = (SWFLAYOUT*)rfx_calloc(sizeof(SWFLAYOUT));
2809 swffont->layout->ascent = 0;
2810 swffont->layout->descent = 0;
2811 swffont->layout->leading = 0;
2812 swffont->layout->bounds = (SRECT*)rfx_calloc(sizeof(SRECT)*font->num_glyphs);
2813 swffont->encoding = FONT_ENCODING_UNICODE;
2814 swffont->numchars = font->num_glyphs;
2815 swffont->maxascii = font->max_unicode;
2816 swffont->ascii2glyph = (int*)rfx_calloc(sizeof(int)*swffont->maxascii);
2817 swffont->glyph2ascii = (U16*)rfx_calloc(sizeof(U16)*swffont->numchars);
2818 swffont->glyph = (SWFGLYPH*)rfx_calloc(sizeof(SWFGLYPH)*swffont->numchars);
2819 swffont->glyphnames = (char**)rfx_calloc(sizeof(char*)*swffont->numchars);
2820 for(t=0;t<font->max_unicode;t++) {
2821 swffont->ascii2glyph[t] = font->unicode2glyph[t];
2823 SRECT max = {0,0,0,0};
2824 for(t=0;t<font->num_glyphs;t++) {
2828 swffont->glyph2ascii[t] = font->glyphs[t].unicode;
2829 if(swffont->glyph2ascii[t] == 0xffff || swffont->glyph2ascii[t] == 0x0000) {
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 swffont->glyph2ascii[t] = 0xe000 + (t&0x1fff);
2836 if(font->glyphs[t].name) {
2837 swffont->glyphnames[t] = strdup(font->glyphs[t].name);
2839 swffont->glyphnames[t] = 0;
2841 advance = font->glyphs[t].advance;
2843 swf_Shape01DrawerInit(&draw, 0);
2844 line = font->glyphs[t].line;
2846 const double scale = GLYPH_SCALE;
2849 c.x = line->sx * scale; c.y = -line->sy * scale;
2850 //to.x = floor(line->x * scale); to.y = floor(-line->y * scale);
2851 to.x = line->x * scale; to.y = -line->y * scale;
2852 if(line->type == gfx_moveTo) {
2853 draw.moveTo(&draw, &to);
2854 } else if(line->type == gfx_lineTo) {
2855 draw.lineTo(&draw, &to);
2856 } else if(line->type == gfx_splineTo) {
2857 draw.splineTo(&draw, &c, &to);
2862 swffont->glyph[t].shape = swf_ShapeDrawerToShape(&draw);
2864 SRECT bbox = swf_ShapeDrawerGetBBox(&draw);
2865 swf_ExpandRect2(&max, &bbox);
2867 swffont->layout->bounds[t] = bbox;
2869 if(advance<32768.0/20) {
2870 swffont->glyph[t].advance = (int)(advance*20);
2872 //msg("<warning> Advance value overflow in glyph %d", t);
2873 swffont->glyph[t].advance = 32767;
2876 draw.dealloc(&draw);
2878 swf_ExpandRect2(&bounds, &swffont->layout->bounds[t]);
2880 for(t=0;t<font->num_glyphs;t++) {
2881 SRECT bbox = swffont->layout->bounds[t];
2883 /* if the glyph doesn't have a bounding box, use the
2884 combined bounding box (necessary e.g. for space characters) */
2885 if(!(bbox.xmin|bbox.ymin|bbox.xmax|bbox.ymax)) {
2886 swffont->layout->bounds[t] = bbox = max;
2889 /* check that the advance value is reasonable, by comparing it
2890 with the bounding box */
2891 if(bbox.xmax>0 && (bbox.xmax*10 < swffont->glyph[t].advance || !swffont->glyph[t].advance)) {
2892 if(swffont->glyph[t].advance)
2893 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);
2894 swffont->glyph[t].advance = bbox.xmax;
2896 //swffont->glyph[t].advance = bbox.xmax - bbox.xmin;
2900 /* Flash player will use the advance value from the char, and the ascent/descent values
2901 from the layout for text selection.
2902 ascent will extend the char into negative y direction, from the baseline, while descent
2903 will extend in positive y direction, also from the baseline.
2904 The baseline is defined as the y-position zero
2907 swffont->layout->ascent = bounds.ymin<0?-bounds.ymin:0;
2908 swffont->layout->descent = bounds.ymax>0?bounds.ymax:0;
2909 swffont->layout->leading = bounds.ymax - bounds.ymin;
2911 /* if the font has proper ascent/descent values (>0) and those define
2912 greater line spacing that what we estimated from the bounding boxes,
2913 use the font's parameters */
2914 if(font->ascent*20 > swffont->layout->ascent)
2915 swffont->layout->ascent = font->ascent*20;
2916 if(font->descent*20 > swffont->layout->descent)
2917 swffont->layout->descent = font->descent*20;
2922 static void swf_addfont(gfxdevice_t*dev, gfxfont_t*font)
2924 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
2926 if(i->swffont && i->swffont->name && !strcmp((char*)i->swffont->name,font->id))
2927 return; // the requested font is the current font
2929 fontlist_t*last=0,*l = i->fontlist;
2932 if(!strcmp((char*)l->swffont->name, font->id)) {
2933 return; // we already know this font
2937 l = (fontlist_t*)rfx_calloc(sizeof(fontlist_t));
2938 l->swffont = gfxfont_to_swffont(font, font->id, (i->config_flashversion>=8 && !NO_FONT3)?3:2);
2945 swf_FontSetID(l->swffont, getNewID(i->dev));
2947 if(getScreenLogLevel() >= LOGLEVEL_DEBUG) {
2949 // print font information
2950 msg("<debug> Font %s",font->id);
2951 msg("<debug> | ID: %d", l->swffont->id);
2952 msg("<debug> | Version: %d", l->swffont->version);
2953 msg("<debug> | Name: %s", l->swffont->name);
2954 msg("<debug> | Numchars: %d", l->swffont->numchars);
2955 msg("<debug> | Maxascii: %d", l->swffont->maxascii);
2956 msg("<debug> | Style: %d", l->swffont->style);
2957 msg("<debug> | Encoding: %d", l->swffont->encoding);
2958 for(iii=0; iii<l->swffont->numchars;iii++) {
2959 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,
2960 l->swffont->layout->bounds[iii].xmin/20.0,
2961 l->swffont->layout->bounds[iii].ymin/20.0,
2962 l->swffont->layout->bounds[iii].xmax/20.0,
2963 l->swffont->layout->bounds[iii].ymax/20.0
2966 for(t=0;t<l->swffont->maxascii;t++) {
2967 if(l->swffont->ascii2glyph[t] == iii)
2968 msg("<debug> | - maps to %d",t);
2974 static void swf_switchfont(gfxdevice_t*dev, const char*fontid)
2976 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
2978 if(i->swffont && i->swffont->name && !strcmp((char*)i->swffont->name,fontid))
2979 return; // the requested font is the current font
2981 fontlist_t*l = i->fontlist;
2983 if(!strcmp((char*)l->swffont->name, fontid)) {
2984 i->swffont = l->swffont;
2989 msg("<error> Unknown font id: %s", fontid);
2993 /* sets the matrix which is to be applied to characters drawn by swfoutput_drawchar() */
2994 static void setfontscale(gfxdevice_t*dev,double m11,double m12, double m21,double m22,double x, double y, char force)
3000 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
3001 if(i->lastfontm11 == m11 &&
3002 i->lastfontm12 == m12 &&
3003 i->lastfontm21 == m21 &&
3004 i->lastfontm22 == m22 && !force)
3009 i->lastfontm11 = m11;
3010 i->lastfontm12 = m12;
3011 i->lastfontm21 = m21;
3012 i->lastfontm22 = m22;
3014 double xsize = sqrt(m11*m11 + m12*m12);
3015 double ysize = sqrt(m21*m21 + m22*m22);
3018 if(i->config_flashversion>=8 && !NO_FONT3)
3021 i->current_font_size = (xsize>ysize?xsize:ysize)*extrazoom;
3022 if(i->current_font_size < 1)
3023 i->current_font_size = 1;
3026 swf_GetMatrix(0, &m);
3028 if(m21 || m12 || fabs(m11+m22)>0.001) {
3029 double ifs = (double)extrazoom/(i->current_font_size);
3030 m.sx = (S32)((m11*ifs)*65536); m.r1 = -(S32)((m21*ifs)*65536);
3031 m.r0 = (S32)((m12*ifs)*65536); m.sy = -(S32)((m22*ifs)*65536);
3034 /* this is the position of the first char to set a new fontmatrix-
3035 we hope that it's close enough to all other characters using the
3036 font, so we use its position as origin for the matrix */
3043 static void swf_drawchar(gfxdevice_t*dev, gfxfont_t*font, int glyph, gfxcolor_t*color, gfxmatrix_t*matrix)
3045 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
3047 msg("<error> swf_drawchar called (glyph %d) without font", glyph);
3051 if(i->config_drawonlyshapes) {
3052 gfxglyph_t*g = &font->glyphs[glyph];
3053 gfxline_t*line2 = gfxline_clone(g->line);
3054 gfxline_transform(line2, matrix);
3055 dev->fill(dev, line2, color);
3056 gfxline_free(line2);
3060 if(!i->swffont || !i->swffont->name || strcmp((char*)i->swffont->name,font->id)) // not equal to current font
3062 swf_switchfont(dev, font->id); // set the current font
3066 msg("<warning> swf_drawchar: Font is NULL");
3069 if(glyph<0 || glyph>=i->swffont->numchars) {
3070 msg("<warning> No character %d in font %s (%d chars)", glyph, FIXNULL((char*)i->swffont->name), i->swffont->numchars);
3074 setfontscale(dev, matrix->m00, matrix->m01, matrix->m10, matrix->m11, matrix->tx, matrix->ty, 0);
3076 double det = i->fontmatrix.sx/65536.0 * i->fontmatrix.sy/65536.0 -
3077 i->fontmatrix.r0/65536.0 * i->fontmatrix.r1/65536.0;
3078 if(fabs(det) < 0.0005) {
3079 /* x direction equals y direction- the text is invisible */
3080 msg("<verbose> Not drawing invisible character %d (det=%f, m=[%f %f;%f %f]\n", glyph,
3082 i->fontmatrix.sx/65536.0, i->fontmatrix.r1/65536.0,
3083 i->fontmatrix.r0/65536.0, i->fontmatrix.sy/65536.0);
3087 /*if(i->swffont->glyph[glyph].shape->bitlen <= 16) {
3088 msg("<warning> Glyph %d in current charset (%s, %d characters) is empty",
3089 glyph, FIXNULL((char*)i->swffont->name), i->swffont->numchars);
3093 /* calculate character position with respect to the current font matrix */
3094 double s = 20 * GLYPH_SCALE / det;
3095 double px = matrix->tx - i->fontmatrix.tx/20.0;
3096 double py = matrix->ty - i->fontmatrix.ty/20.0;
3097 int x = (SCOORD)(( px * i->fontmatrix.sy/65536.0 - py * i->fontmatrix.r1/65536.0)*s);
3098 int y = (SCOORD)((- px * i->fontmatrix.r0/65536.0 + py * i->fontmatrix.sx/65536.0)*s);
3099 if(x>32767 || x<-32768 || y>32767 || y<-32768) {
3100 msg("<verbose> Moving character origin to %f %f\n", matrix->tx, matrix->ty);
3102 setfontscale(dev, matrix->m00, matrix->m01, matrix->m10, matrix->m11, matrix->tx, matrix->ty, 1);
3103 /* since we just moved the char origin to the current char's position,
3104 it now has the relative position (0,0) */
3113 msg("<trace> Drawing char %d in font %d at %d,%d in color %02x%02x%02x%02x",
3114 glyph, i->swffont->id, x, y, color->r, color->g, color->b, color->a);
3116 if(color->a == 0 && i->config_invisibletexttofront) {
3117 RGBA color2 = *(RGBA*)color;
3118 if(i->config_flashversion>=8) {
3119 // use "multiply" blend mode
3120 color2.a = color2.r = color2.g = color2.b = 255;
3122 i->topchardata = charbuffer_append(i->topchardata, i->swffont, glyph, x, y, i->current_font_size, color2, &i->fontmatrix);
3124 i->chardata = charbuffer_append(i->chardata, i->swffont, glyph, x, y, i->current_font_size, *(RGBA*)color, &i->fontmatrix);
3126 swf_FontUseGlyph(i->swffont, glyph, i->current_font_size);