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;
744 if(pass==1 && lastchar!=chr->charid) {
745 swf_FontUsePair(chr->font, lastchar, chr->charid);
746 swf_FontUsePair(chr->font, chr->charid, lastchar);
754 if(t<chardata->pos-1) nextx = chardata->chr[t+1].x;
755 if(t==chardata->pos-1 && chardata->next) nextx = chardata->next->chr[0].x;
756 int dx = nextx-chr->x;
759 if(dx>=0 && (dx<(1<<(advancebits-1)) || pass==0)) {
767 charids[charstorepos] = chr->charid;
768 charadvance[charstorepos] = advance;
769 lastchar = chr->charid;
772 chardata = chardata->next;
777 static void chararray_destroy(chararray_t*chr)
780 chararray_t*next = chr->next;
787 static inline int matrix_diff(MATRIX*m1, MATRIX*m2)
789 return memcmp(m1,m2,sizeof(MATRIX));
791 static charbuffer_t*charbuffer_append(charbuffer_t*buf, SWFFONT*font, int charid, int x,int y, int size, RGBA color, MATRIX*m)
793 if(!buf || matrix_diff(&buf->matrix,m)) {
794 charbuffer_t*n = rfx_calloc(sizeof(charbuffer_t));
799 if(!buf->last || buf->last->pos == CHARDATAMAX) {
800 chararray_t*n = rfx_calloc(sizeof(chararray_t));
802 buf->array = buf->last = n;
808 chararray_t*a = buf->last;
809 a->chr[a->pos].font = font;
810 a->chr[a->pos].charid = charid;
811 a->chr[a->pos].x = x;
812 a->chr[a->pos].y = y;
813 a->chr[a->pos].color = color;
814 a->chr[a->pos].size = size;
819 /* Notice: we can only put chars in the range -1639,1638 (-32768/20,32768/20).
820 So if we set this value to high, the char coordinates will overflow.
821 If we set it to low, however, the char positions will be inaccurate */
822 #define GLYPH_SCALE 1
824 static void chararray_writetodev(gfxdevice_t*dev, chararray_t*array, MATRIX*matrix, char invisible)
826 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
828 int textid = getNewID(dev);
829 i->tag = swf_InsertTag(i->tag,ST_DEFINETEXT2);
830 swf_SetU16(i->tag, textid);
832 r = getcharacterbbox(array, matrix, i->config_flashversion);
833 r = swf_ClipRect(i->pagebbox, r);
834 swf_SetRect(i->tag,&r);
835 swf_SetMatrix(i->tag, matrix);
836 msg("<trace> Placing text as ID %d", textid);
837 chararray_writetotag(array, i->tag);
842 if(i->swf->fileVersion >= 8) {
843 i->tag = swf_InsertTag(i->tag, ST_CSMTEXTSETTINGS);
844 swf_SetU16(i->tag, textid);
846 //swf_SetU8(i->tag, /*subpixel grid*/(2<<3)|/*flashtype*/0x40);
847 swf_SetU8(i->tag, /*grid*/(1<<3)|/*flashtype*/0x40);
848 //swf_SetU8(i->tag, /*no grid*/(0<<3)|/*flashtype*/0x40);
850 swf_SetU32(i->tag, 0);//thickness
851 swf_SetU32(i->tag, 0);//sharpness
852 //swf_SetU32(i->tag, 0x20000);//thickness
853 //swf_SetU32(i->tag, 0x800000);//sharpness
854 swf_SetU8(i->tag, 0);//reserved
856 if(invisible && i->config_flashversion>=8) {
857 i->tag = swf_InsertTag(i->tag,ST_PLACEOBJECT3);
858 swf_ObjectPlaceBlend(i->tag,textid,getNewDepth(dev),&i->page_matrix,NULL,NULL,BLENDMODE_MULTIPLY);
860 i->tag = swf_InsertTag(i->tag,ST_PLACEOBJECT2);
861 swf_ObjectPlace(i->tag,textid,getNewDepth(dev),&i->page_matrix,NULL,NULL);
865 static void charbuffer_writetodevandfree(gfxdevice_t*dev, charbuffer_t*buf, char invisible)
868 charbuffer_t*next = buf->next;buf->next = 0;
869 chararray_writetodev(dev, buf->array, &buf->matrix, invisible);
870 chararray_destroy(buf->array);
876 static void endtext(gfxdevice_t*dev)
878 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
881 charbuffer_writetodevandfree(dev, i->chardata, 0);i->chardata = 0;
885 static int watermark2_width=47;
886 static int watermark2_height=11;
887 static int watermark2[47] = {95,1989,71,0,2015,337,1678,0,2015,5,1921,320,1938,25,2006,1024,
888 1042,21,13,960,1039,976,8,2000,1359,1088,31,1989,321,1728,0,1152,
889 1344,832,0,1984,0,896,1088,1088,896,0,1984,128,256,512,1984};
891 static void draw_watermark(gfxdevice_t*dev, gfxbbox_t r, char drawall)
893 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
894 double wx = r.xmax / 5.0;
895 double tx = r.xmax*4.0 / 5.0;
896 double ty = r.ymax-wx*watermark2_height/watermark2_width;
897 double sx = (r.xmax - tx) / watermark2_width;
898 double sy = (r.ymax - ty) / watermark2_height;
901 if(ty > 0 && px > 1.0 && py > 1.0) {
903 for(y=0;y<watermark2_height;y++)
904 for(x=0;x<watermark2_width;x++) {
905 if(((watermark2[x]>>y)&1)) {
906 if(!drawall && rand()%5)
908 unsigned int b = rand();
909 moveto(dev, i->tag, x*sx+tx+((b>>1)&1)/20.0, y*sy+ty+((b>>3)&1)/20.0);
910 lineto(dev, i->tag, x*sx+px+tx+((b>>2)&1)/20.0, y*sy+ty+((b>>3)&1)/20.0);
911 lineto(dev, i->tag, x*sx+px+tx+((b>>2)&1)/20.0, y*sy+py+ty+((b>>4)&1)/20.0);
912 lineto(dev, i->tag, x*sx+tx+((b>>1)&1)/20.0, y*sy+py+ty+((b>>4)&1)/20.0);
913 lineto(dev, i->tag, x*sx+tx+((b>>1)&1)/20.0, y*sy+ty+((b>>3)&1)/20.0);
919 static void swfoutput_setfillcolor(gfxdevice_t* dev, U8 r, U8 g, U8 b, U8 a)
921 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
922 if(i->fillrgb.r == r &&
925 i->fillrgb.a == a) return;
934 static void insert_watermark(gfxdevice_t*dev, char drawall)
936 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
937 if(!drawall && i->watermarks>20)
943 swfoutput_setfillcolor(dev, 0,0,255,192);
945 swfoutput_setfillcolor(dev, rand(),rand(),rand(),(rand()&127)+128);
950 gfxbbox_t r; r.xmin = r.ymin = 0;
953 draw_watermark(dev, r, drawall);
959 static void endpage(gfxdevice_t*dev)
961 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
969 charbuffer_writetodevandfree(dev, i->topchardata, 1);
976 if(i->config_watermark) {
977 insert_watermark(dev, 1);
983 static void addViewer(gfxdevice_t* dev)
985 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
988 RGBA button_colors[3]= {{0xbf,0x00,0x00,0x80},{0xbf,0x20,0x20,0xc0}, {0xbf,0xc0,0xc0,0xff}};
990 int button_sizex = 20;
991 int button_sizey = 20;
993 RGBA black = {255,0,0,0};
995 i->tag = swf_InsertTag(i->tag,ST_DEFINESHAPE3);
997 int ls1 = swf_ShapeAddLineStyle(s,40,&black);
998 int fs1 = swf_ShapeAddSolidFillStyle(s,&button_colors[t/2]);
999 int shapeid = ids[t] = getNewID(dev);
1000 swf_SetU16(i->tag,shapeid);
1002 r.xmin = -20*button_sizex;
1003 r.xmax = 20*button_sizex;
1005 r.ymax = 40*button_sizey;
1006 swf_SetRect(i->tag,&r); // set shape bounds
1007 swf_SetShapeHeader(i->tag,s); // write all styles to tag
1008 swf_ShapeSetAll(i->tag,s,0*button_sizex,0,ls1,fs1,0);
1009 swf_ShapeSetLine(i->tag,s,(1-(t&1)*2)*20*button_sizex,20*button_sizey);
1010 swf_ShapeSetLine(i->tag,s,-(1-(t&1)*2)*20*button_sizex,20*button_sizey);
1011 swf_ShapeSetLine(i->tag,s,0,-40*button_sizey);
1012 swf_ShapeSetEnd(i->tag); // finish drawing
1013 swf_ShapeFree(s); // clean shape structure (which isn't needed anymore after writing the tag)
1015 ActionTAG*a1=0,*a2=0,*a3=0;
1016 a1 = action_NextFrame(a1);
1017 a1 = action_Stop(a1);
1018 a1 = action_End(a1);
1020 a2 = action_PreviousFrame(a2);
1021 a2 = action_Stop(a2);
1022 a2 = action_End(a2);
1024 a3 = action_Stop(a3);
1025 a3 = action_End(a3);
1027 i->tag = swf_InsertTag(i->tag, ST_DOACTION);
1028 swf_ActionSet(i->tag,a3);
1030 i->tag = swf_InsertTag(i->tag,ST_DEFINEBUTTON);
1031 int buttonid1 = getNewID(dev);
1032 swf_SetU16(i->tag, buttonid1);
1033 swf_ButtonSetRecord(i->tag,BS_UP|BS_HIT,ids[0],1,NULL,NULL);
1034 swf_ButtonSetRecord(i->tag,BS_OVER,ids[2],1,NULL,NULL);
1035 swf_ButtonSetRecord(i->tag,BS_DOWN,ids[4],1,NULL,NULL);
1036 swf_SetU8(i->tag,0); // end of button records
1037 swf_ActionSet(i->tag,a1);
1039 i->tag = swf_InsertTag(i->tag,ST_DEFINEBUTTON);
1040 int buttonid2 = getNewID(dev);
1041 swf_SetU16(i->tag, buttonid2);
1042 swf_ButtonSetRecord(i->tag,BS_UP|BS_HIT,ids[1],1,NULL,NULL);
1043 swf_ButtonSetRecord(i->tag,BS_OVER,ids[3],1,NULL,NULL);
1044 swf_ButtonSetRecord(i->tag,BS_DOWN,ids[5],1,NULL,NULL);
1045 swf_SetU8(i->tag,0); // end of button records
1046 swf_ActionSet(i->tag,a2);
1048 i->tag = swf_InsertTag(i->tag,ST_PLACEOBJECT2);
1050 swf_GetMatrix(0, &m);
1051 m.tx = button_sizex*20+200;
1052 swf_ObjectPlace(i->tag, buttonid2, 65534,&m,0,0);
1053 i->tag = swf_InsertTag(i->tag,ST_PLACEOBJECT2);
1054 m.tx = button_sizex*20+200+200;
1055 swf_ObjectPlace(i->tag, buttonid1, 65535,&m,0,0);
1059 void swf_startframe(gfxdevice_t*dev, int width, int height)
1061 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1063 if(i->config_protect) {
1064 i->tag = swf_InsertTag(i->tag, ST_PROTECT);
1065 i->config_protect = 0;
1067 if(i->config_simpleviewer) {
1072 if(!i->firstpage && !i->pagefinished)
1075 msg("<verbose> Starting new SWF page of size %dx%d", width, height);
1077 swf_GetMatrix(0, &i->page_matrix);
1078 i->page_matrix.tx = 0;
1079 i->page_matrix.ty = 0;
1086 /* create a bbox structure with the page size. This is used
1087 for clipping shape and text bounding boxes. As we don't want to
1088 generate bounding boxes which extend beyond the movie size (in
1089 order to not confuse Flash), we clip everything against i->pagebbox */
1090 i->pagebbox.xmin = 0;
1091 i->pagebbox.ymin = 0;
1092 i->pagebbox.xmax = width*20;
1093 i->pagebbox.ymax = height*20;
1095 /* increase SWF's bounding box */
1096 swf_ExpandRect2(&i->swf->movieSize, &i->pagebbox);
1098 i->lastframeno = i->frameno;
1100 i->pagefinished = 0;
1104 void swf_endframe(gfxdevice_t*dev)
1106 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1108 if(!i->pagefinished)
1111 if( (i->swf->fileVersion <= 8) && (i->config_insertstoptag) ) {
1113 atag = action_Stop(atag);
1114 atag = action_End(atag);
1115 i->tag = swf_InsertTag(i->tag,ST_DOACTION);
1116 swf_ActionSet(i->tag,atag);
1118 i->tag = swf_InsertTag(i->tag,ST_SHOWFRAME);
1121 for(i->depth;i->depth>i->startdepth;i->depth--) {
1122 i->tag = swf_InsertTag(i->tag,ST_REMOVEOBJECT2);
1123 swf_SetU16(i->tag,i->depth);
1125 i->depth = i->startdepth;
1127 if(i->config_frameresets) {
1128 for(i->currentswfid;i->currentswfid>i->startids;i->currentswfid--) {
1129 i->tag = swf_InsertTag(i->tag,ST_FREECHARACTER);
1130 swf_SetU16(i->tag,i->currentswfid);
1132 i->currentswfid = i->startids;
1136 static void setBackground(gfxdevice_t*dev, int x1, int y1, int x2, int y2)
1138 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1140 rgb.a = rgb.r = rgb.g = rgb.b = 0xff;
1144 int shapeid = getNewID(dev);
1149 i->tag = swf_InsertTag(i->tag, ST_DEFINESHAPE);
1151 fs1 = swf_ShapeAddSolidFillStyle(s, &rgb);
1152 swf_SetU16(i->tag,shapeid);
1153 swf_SetRect(i->tag,&r);
1154 swf_SetShapeHeader(i->tag,s);
1155 swf_ShapeSetAll(i->tag,s,x1,y1,ls1,fs1,0);
1156 swf_ShapeSetLine(i->tag,s,(x2-x1),0);
1157 swf_ShapeSetLine(i->tag,s,0,(y2-y1));
1158 swf_ShapeSetLine(i->tag,s,(x1-x2),0);
1159 swf_ShapeSetLine(i->tag,s,0,(y1-y2));
1160 swf_ShapeSetEnd(i->tag);
1162 i->tag = swf_InsertTag(i->tag, ST_PLACEOBJECT2);
1163 swf_ObjectPlace(i->tag,shapeid,getNewDepth(dev),0,0,0);
1164 i->tag = swf_InsertTag(i->tag, ST_PLACEOBJECT2);
1165 swf_ObjectPlaceClip(i->tag,shapeid,getNewDepth(dev),0,0,0,65535);
1168 /* initialize the swf writer */
1169 void gfxdevice_swf_init(gfxdevice_t* dev)
1171 memset(dev, 0, sizeof(gfxdevice_t));
1175 dev->internal = init_internal_struct(); // set config to default values
1177 dev->startpage = swf_startframe;
1178 dev->endpage = swf_endframe;
1179 dev->finish = swf_finish;
1180 dev->fillbitmap = swf_fillbitmap;
1181 dev->setparameter = swf_setparameter;
1182 dev->stroke = swf_stroke;
1183 dev->startclip = swf_startclip;
1184 dev->endclip = swf_endclip;
1185 dev->fill = swf_fill;
1186 dev->fillbitmap = swf_fillbitmap;
1187 dev->fillgradient = swf_fillgradient;
1188 dev->addfont = swf_addfont;
1189 dev->drawchar = swf_drawchar;
1190 dev->drawlink = swf_drawlink;
1192 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1195 msg("<verbose> initializing swf output\n", i->max_x,i->max_y);
1199 i->swf = (SWF*)rfx_calloc(sizeof(SWF));
1200 i->swf->fileVersion = 0;
1201 i->swf->frameRate = 0x80;
1202 i->swf->movieSize.xmin = 0;
1203 i->swf->movieSize.ymin = 0;
1204 i->swf->movieSize.xmax = 0;
1205 i->swf->movieSize.ymax = 0;
1206 i->swf->fileAttributes = 9; // as3, local-with-network
1208 i->swf->firstTag = swf_InsertTag(NULL,ST_SETBACKGROUNDCOLOR);
1209 i->tag = i->swf->firstTag;
1211 rgb.a = rgb.r = rgb.g = rgb.b = 0xff;
1213 swf_SetRGB(i->tag,&rgb);
1215 i->startdepth = i->depth = 0;
1216 i->startids = i->currentswfid = 0;
1219 static void startshape(gfxdevice_t*dev)
1221 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1226 //if(i->chardatapos && i->chardata[i->chardatapos-1].color.a)
1229 i->tag = swf_InsertTag(i->tag,ST_DEFINESHAPE3);
1231 swf_ShapeNew(&i->shape);
1232 i->linestyleid = swf_ShapeAddLineStyle(i->shape,i->linewidth,&i->strokergb);
1233 i->fillstyleid = swf_ShapeAddSolidFillStyle(i->shape,&i->fillrgb);
1235 RGBA markcol = {0,i->mark[0],i->mark[1],i->mark[2]};
1236 swf_ShapeAddSolidFillStyle(i->shape,&markcol);
1239 i->shapeid = getNewID(dev);
1241 msg("<debug> Using shape id %d", i->shapeid);
1243 swf_SetU16(i->tag,i->shapeid); // ID
1245 i->bboxrectpos = i->tag->len;
1247 swf_SetRect(i->tag,&i->pagebbox);
1249 memset(&i->bboxrect, 0, sizeof(i->bboxrect));
1251 swf_SetShapeStyles(i->tag,i->shape);
1252 swf_ShapeCountBits(i->shape,NULL,NULL);
1253 swf_SetShapeBits(i->tag,i->shape);
1255 /* TODO: do we really need this? */
1256 //swf_ShapeSetAll(i->tag,i->shape,/*x*/0,/*y*/0,i->linestyleid,0,0);
1257 //swf_ShapeSetAll(i->tag,i->shape,/*x*/UNDEFINED_COORD,/*y*/UNDEFINED_COORD,i->linestyleid,0,0);
1258 i->swflastx=i->swflasty=UNDEFINED_COORD;
1259 i->lastwasfill = -1;
1260 i->shapeisempty = 1;
1263 static void starttext(gfxdevice_t*dev)
1265 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1269 if(i->config_watermark) {
1270 insert_watermark(dev, 0);
1273 i->swflastx=i->swflasty=0;
1277 /* TODO: move to ../lib/rfxswf */
1278 void changeRect(gfxdevice_t*dev, TAG*tag, int pos, SRECT*newrect)
1280 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1281 /* determine length of old rect */
1285 swf_GetRect(tag, &old);
1286 swf_ResetReadBits(tag);
1287 int pos_end = tag->pos;
1289 int len = tag->len - pos_end;
1290 U8*data = (U8*)malloc(len);
1291 memcpy(data, &tag->data[pos_end], len);
1294 swf_SetRect(tag, newrect);
1295 swf_SetBlock(tag, data, len);
1297 tag->pos = tag->readBit = 0;
1300 void cancelshape(gfxdevice_t*dev)
1302 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1303 /* delete old shape tag */
1305 i->tag = i->tag->prev;
1306 swf_DeleteTag(0, todel);
1307 if(i->shape) {swf_ShapeFree(i->shape);i->shape=0;}
1309 i->bboxrectpos = -1;
1311 // i->currentswfid--; // doesn't work, for some reason
1314 void fixAreas(gfxdevice_t*dev)
1316 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1317 if(!i->shapeisempty && i->fill &&
1318 (i->bboxrect.xmin == i->bboxrect.xmax ||
1319 i->bboxrect.ymin == i->bboxrect.ymax) &&
1320 i->config_minlinewidth >= 0.001
1322 msg("<debug> Shape has size 0: width=%.2f height=%.2f",
1323 (i->bboxrect.xmax-i->bboxrect.xmin)/20.0,
1324 (i->bboxrect.ymax-i->bboxrect.ymin)/20.0
1327 SRECT r = i->bboxrect;
1329 if(r.xmin == r.xmax && r.ymin == r.ymax) {
1330 /* this thing comes down to a single dot- nothing to fix here */
1336 RGBA save_col = i->strokergb;
1337 int save_width = i->linewidth;
1339 i->strokergb = i->fillrgb;
1340 i->linewidth = (int)(i->config_minlinewidth*20);
1341 if(i->linewidth==0) i->linewidth = 1;
1346 moveto(dev, i->tag, r.xmin/20.0,r.ymin/20.0);
1347 lineto(dev, i->tag, r.xmax/20.0,r.ymax/20.0);
1349 i->strokergb = save_col;
1350 i->linewidth = save_width;
1355 static void endshape_noput(gfxdevice_t*dev)
1357 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1360 //changeRect(dev, i->tag, i->bboxrectpos, &i->bboxrect);
1363 swf_ShapeFree(i->shape);
1371 static void endshape(gfxdevice_t*dev)
1373 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1379 if(i->shapeisempty ||
1381 (i->bboxrect.xmin == i->bboxrect.xmax &&
1382 i->bboxrect.ymin == i->bboxrect.ymax))
1384 // delete the shape again, we didn't do anything
1385 msg("<debug> cancelling shape: bbox is (%f,%f,%f,%f)",
1386 i->bboxrect.xmin /20.0,
1387 i->bboxrect.ymin /20.0,
1388 i->bboxrect.xmax /20.0,
1389 i->bboxrect.ymax /20.0
1395 swf_ShapeSetEnd(i->tag);
1397 SRECT r = swf_ClipRect(i->pagebbox, i->bboxrect);
1398 changeRect(dev, i->tag, i->bboxrectpos, &r);
1400 msg("<trace> Placing shape ID %d", i->shapeid);
1402 i->tag = swf_InsertTag(i->tag,ST_PLACEOBJECT2);
1403 MATRIX m = i->page_matrix;
1404 m.tx += i->shapeposx;
1405 m.ty += i->shapeposy;
1406 swf_ObjectPlace(i->tag,i->shapeid,getNewDepth(dev),&m,NULL,NULL);
1408 if(i->config_animate) {
1409 i->tag = swf_InsertTag(i->tag,ST_SHOWFRAME);
1412 swf_ShapeFree(i->shape);
1415 i->bboxrectpos = -1;
1422 void wipeSWF(SWF*swf)
1424 TAG*tag = swf->firstTag;
1426 TAG*next = tag->next;
1427 if(tag->id != ST_SETBACKGROUNDCOLOR &&
1428 tag->id != ST_END &&
1429 tag->id != ST_DOACTION &&
1430 tag->id != ST_SHOWFRAME) {
1431 swf_DeleteTag(swf, tag);
1437 void swfoutput_finalize(gfxdevice_t*dev)
1439 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1441 if(i->tag && i->tag->id == ST_END)
1442 return; //already done
1444 i->swf->fileVersion = i->config_flashversion;
1445 i->swf->frameRate = i->config_framerate*0x100;
1447 if(i->config_bboxvars) {
1448 TAG* tag = swf_InsertTag(i->swf->firstTag, ST_DOACTION);
1450 a = action_PushString(a, "xmin");
1451 a = action_PushFloat(a, i->swf->movieSize.xmin / 20.0);
1452 a = action_SetVariable(a);
1453 a = action_PushString(a, "ymin");
1454 a = action_PushFloat(a, i->swf->movieSize.ymin / 20.0);
1455 a = action_SetVariable(a);
1456 a = action_PushString(a, "xmax");
1457 a = action_PushFloat(a, i->swf->movieSize.xmax / 20.0);
1458 a = action_SetVariable(a);
1459 a = action_PushString(a, "ymax");
1460 a = action_PushFloat(a, i->swf->movieSize.ymax / 20.0);
1461 a = action_SetVariable(a);
1462 a = action_PushString(a, "width");
1463 a = action_PushFloat(a, (i->swf->movieSize.xmax - i->swf->movieSize.xmin) / 20.0);
1464 a = action_SetVariable(a);
1465 a = action_PushString(a, "height");
1466 a = action_PushFloat(a, (i->swf->movieSize.ymax - i->swf->movieSize.ymin) / 20.0);
1467 a = action_SetVariable(a);
1469 swf_ActionSet(tag, a);
1474 free(i->mark);i->mark = 0;
1478 fontlist_t *iterator = i->fontlist;
1479 char use_font3 = i->config_flashversion>=8 && !NO_FONT3;
1481 TAG*mtag = i->swf->firstTag;
1482 if(iterator->swffont) {
1483 if(use_font3 && i->config_alignfonts) {
1484 // needs to be done before the reduce
1485 swf_FontCreateAlignZones(iterator->swffont);
1487 if(!i->config_storeallcharacters) {
1488 msg("<debug> Reducing font %s", iterator->swffont->name);
1489 swf_FontReduce(iterator->swffont);
1491 int used = iterator->swffont->use && iterator->swffont->use->used_glyphs;
1494 mtag = swf_InsertTag(mtag, ST_DEFINEFONT2);
1495 swf_FontSetDefine2(mtag, iterator->swffont);
1497 mtag = swf_InsertTag(mtag, ST_DEFINEFONT3);
1498 swf_FontSetDefine2(mtag, iterator->swffont);
1499 if(iterator->swffont->alignzones) {
1500 mtag = swf_InsertTag(mtag, ST_DEFINEFONTALIGNZONES);
1501 swf_FontSetAlignZones(mtag, iterator->swffont);
1507 iterator = iterator->next;
1510 i->tag = swf_InsertTag(i->tag,ST_END);
1511 TAG* tag = i->tag->prev;
1513 /* remove the removeobject2 tags between the last ST_SHOWFRAME
1514 and the ST_END- they confuse the flash player */
1515 while(tag->id == ST_REMOVEOBJECT2) {
1516 TAG* prev = tag->prev;
1517 swf_DeleteTag(i->swf, tag);
1524 if(i->config_enablezlib || i->config_flashversion>=6) {
1525 i->swf->compressed = 1;
1528 /* Add AVM2 actionscript */
1529 if(i->config_flashversion>=9 &&
1530 (i->config_insertstoptag || i->hasbuttons) && !i->config_linknameurl) {
1531 swf_AddButtonLinks(i->swf, i->config_insertstoptag,
1532 i->config_internallinkfunction||i->config_externallinkfunction);
1534 // if(i->config_reordertags)
1535 // swf_Optimize(i->swf);
1538 int swfresult_save(gfxresult_t*gfx, const char*filename)
1540 SWF*swf = (SWF*)gfx->internal;
1543 fi = open(filename, O_BINARY|O_CREAT|O_TRUNC|O_WRONLY, 0777);
1548 msg("<fatal> Could not create \"%s\". ", FIXNULL(filename));
1552 if FAILED(swf_WriteSWF(fi,swf))
1553 msg("<error> WriteSWF() failed.\n");
1559 void* swfresult_get(gfxresult_t*gfx, const char*name)
1561 SWF*swf = (SWF*)gfx->internal;
1562 if(!strcmp(name, "swf")) {
1563 return (void*)swf_CopySWF(swf);
1564 } else if(!strcmp(name, "xmin")) {
1565 return (void*)(ptroff_t)(swf->movieSize.xmin/20);
1566 } else if(!strcmp(name, "ymin")) {
1567 return (void*)(ptroff_t)(swf->movieSize.ymin/20);
1568 } else if(!strcmp(name, "xmax")) {
1569 return (void*)(ptroff_t)(swf->movieSize.xmax/20);
1570 } else if(!strcmp(name, "ymax")) {
1571 return (void*)(ptroff_t)(swf->movieSize.ymax/20);
1572 } else if(!strcmp(name, "width")) {
1573 return (void*)(ptroff_t)((swf->movieSize.xmax - swf->movieSize.xmin)/20);
1574 } else if(!strcmp(name, "height")) {
1575 return (void*)(ptroff_t)((swf->movieSize.ymax - swf->movieSize.ymin)/20);
1579 void swfresult_destroy(gfxresult_t*gfx)
1582 swf_FreeTags((SWF*)gfx->internal);
1583 free(gfx->internal);
1586 memset(gfx, 0, sizeof(gfxresult_t));
1590 static void swfoutput_destroy(gfxdevice_t* dev);
1592 gfxresult_t* swf_finish(gfxdevice_t* dev)
1594 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1597 if(i->config_linktarget) {
1598 free(i->config_linktarget);
1599 i->config_linktarget = 0;
1602 swfoutput_finalize(dev);
1603 SWF* swf = i->swf;i->swf = 0;
1604 swfoutput_destroy(dev);
1606 result = (gfxresult_t*)rfx_calloc(sizeof(gfxresult_t));
1607 result->internal = swf;
1608 result->save = swfresult_save;
1610 result->get = swfresult_get;
1611 result->destroy = swfresult_destroy;
1615 /* Perform cleaning up */
1616 static void swfoutput_destroy(gfxdevice_t* dev)
1618 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1620 /* not initialized yet- nothing to destroy */
1624 fontlist_t *tmp,*iterator = i->fontlist;
1626 if(iterator->swffont) {
1627 swf_FontFree(iterator->swffont);iterator->swffont=0;
1630 iterator = iterator->next;
1633 if(i->swf) {swf_FreeTags(i->swf);free(i->swf);i->swf = 0;}
1636 memset(dev, 0, sizeof(gfxdevice_t));
1639 static void swfoutput_setstrokecolor(gfxdevice_t* dev, U8 r, U8 g, U8 b, U8 a)
1641 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1642 if(i->strokergb.r == r &&
1643 i->strokergb.g == g &&
1644 i->strokergb.b == b &&
1645 i->strokergb.a == a) return;
1655 //#define ROUND_UP 19
1656 //#define ROUND_UP 10
1658 static void swfoutput_setlinewidth(gfxdevice_t*dev, double _linewidth)
1660 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1661 if(i->linewidth == (U16)(_linewidth*20+19.0/20.0))
1665 i->linewidth = (U16)(_linewidth*20+19.0/20.0);
1669 static void drawlink(gfxdevice_t*dev, ActionTAG*,ActionTAG*, gfxline_t*points, char mouseover, char*type, const char*url);
1670 static void swfoutput_namedlink(gfxdevice_t*dev, char*name, gfxline_t*points);
1671 static void swfoutput_linktopage(gfxdevice_t*dev, int page, gfxline_t*points);
1672 static void swfoutput_linktourl(gfxdevice_t*dev, const char*url, gfxline_t*points);
1674 /*void swfoutput_drawlink(gfxdevice_t*dev, char*url, gfxline_t*points)
1676 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1677 dev->drawlink(dev, points, url);
1680 void swf_drawlink(gfxdevice_t*dev, gfxline_t*points, const char*url)
1682 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1684 if(i->config_disablelinks)
1687 if(!strncmp("http://pdf2swf:", url, 15)) {
1688 char*tmp = strdup(url);
1689 int l = strlen(tmp);
1692 swfoutput_namedlink(dev, tmp+15, points);
1695 } else if(!strncmp("page", url, 4)) {
1698 if(url[t]<'0' || url[t]>'9')
1701 int page = atoi(&url[4]);
1702 if(page<0) page = 0;
1703 swfoutput_linktopage(dev, page, points);
1706 swfoutput_linktourl(dev, url, points);
1709 void swfoutput_linktourl(gfxdevice_t*dev, const char*url, gfxline_t*points)
1711 ActionTAG* actions = 0;
1712 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1718 /* TODO: escape special characters in url */
1720 if(i->config_externallinkfunction && i->config_flashversion<=8) {
1721 actions = action_PushString(actions, url); //parameter
1722 actions = action_PushInt(actions, 1); //number of parameters (1)
1723 actions = action_PushString(actions, i->config_externallinkfunction); //function name
1724 actions = action_CallFunction(actions);
1725 } else if(!i->config_linktarget) {
1726 if(!i->config_opennewwindow)
1727 actions = action_GetUrl(actions, url, "_parent");
1729 actions = action_GetUrl(actions, url, "_this");
1731 actions = action_GetUrl(actions, url, i->config_linktarget);
1733 actions = action_End(actions);
1735 drawlink(dev, actions, 0, points, 0, "url", url);
1737 swf_ActionFree(actions);
1739 void swfoutput_linktopage(gfxdevice_t*dev, int page, gfxline_t*points)
1741 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1742 ActionTAG* actions = 0;
1749 if(!i->config_internallinkfunction || i->config_flashversion>=9) {
1750 actions = action_GotoFrame(actions, page-1);
1751 actions = action_End(actions);
1753 actions = action_PushInt(actions, page); //parameter
1754 actions = action_PushInt(actions, 1); //number of parameters (1)
1755 actions = action_PushString(actions, i->config_internallinkfunction); //function name
1756 actions = action_CallFunction(actions);
1757 actions = action_End(actions);
1761 sprintf(name, "page%d", page);
1763 drawlink(dev, actions, 0, points, 0, "page", name);
1765 swf_ActionFree(actions);
1768 /* Named Links (a.k.a. Acrobatmenu) are used to implement various gadgets
1769 of the viewer objects, like subtitles, index elements etc.
1771 void swfoutput_namedlink(gfxdevice_t*dev, char*name, gfxline_t*points)
1773 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1774 ActionTAG *actions1,*actions2;
1775 char*tmp = strdup(name);
1784 if(!strncmp(tmp, "call:", 5))
1786 char*x = strchr(&tmp[5], ':');
1788 actions1 = action_PushInt(0, 0); //number of parameters (0)
1789 actions1 = action_PushString(actions1, &tmp[5]); //function name
1790 actions1 = action_CallFunction(actions1);
1791 actions1 = action_End(actions1);
1794 actions1 = action_PushString(0, x+1); //parameter
1795 actions1 = action_PushInt(actions1, 1); //number of parameters (1)
1796 actions1 = action_PushString(actions1, &tmp[5]); //function name
1797 actions1 = action_CallFunction(actions1);
1798 actions1 = action_End(actions1);
1800 actions2 = action_End(0);
1806 actions1 = action_PushString(0, "/:subtitle");
1807 actions1 = action_PushString(actions1, name);
1808 actions1 = action_SetVariable(actions1);
1809 actions1 = action_End(actions1);
1811 actions2 = action_PushString(0, "/:subtitle");
1812 actions2 = action_PushString(actions2, "");
1813 actions2 = action_SetVariable(actions2);
1814 actions2 = action_End(actions2);
1818 drawlink(dev, actions1, actions2, points, mouseover, type, name);
1820 swf_ActionFree(actions1);
1821 swf_ActionFree(actions2);
1825 static void drawgfxline(gfxdevice_t*dev, gfxline_t*line, int fill)
1827 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1828 gfxcoord_t lastx=0,lasty=0,px=0,py=0;
1830 int lines= 0, splines=0;
1837 /* check whether the next segment is zero */
1838 if(line->type == gfx_moveTo) {
1839 moveto(dev, i->tag, line->x, line->y);
1840 px = lastx = line->x;
1841 py = lasty = line->y;
1843 } if(line->type == gfx_lineTo) {
1844 lineto(dev, i->tag, line->x, line->y);
1849 } else if(line->type == gfx_splineTo) {
1851 s.x = line->sx;p.x = line->x;
1852 s.y = line->sy;p.y = line->y;
1853 splineto(dev, i->tag, s, p);
1861 msg("<trace> drawgfxline, %d lines, %d splines", lines, splines);
1865 static void drawlink(gfxdevice_t*dev, ActionTAG*actions1, ActionTAG*actions2, gfxline_t*points, char mouseover, char*type, const char*url)
1867 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1876 int buttonid = getNewID(dev);
1877 gfxbbox_t bbox = gfxline_getbbox(points);
1879 if(i->config_linknameurl) {
1887 myshapeid = getNewID(dev);
1888 i->tag = swf_InsertTag(i->tag,ST_DEFINESHAPE3);
1889 swf_ShapeNew(&i->shape);
1890 rgb.r = rgb.b = rgb.a = rgb.g = 0;
1891 fsid = swf_ShapeAddSolidFillStyle(i->shape,&rgb);
1892 swf_SetU16(i->tag, myshapeid);
1893 r.xmin = (int)(bbox.xmin*20);
1894 r.ymin = (int)(bbox.ymin*20);
1895 r.xmax = (int)(bbox.xmax*20);
1896 r.ymax = (int)(bbox.ymax*20);
1897 r = swf_ClipRect(i->pagebbox, r);
1898 swf_SetRect(i->tag,&r);
1899 swf_SetShapeStyles(i->tag,i->shape);
1900 swf_ShapeCountBits(i->shape,NULL,NULL);
1901 swf_SetShapeBits(i->tag,i->shape);
1902 swf_ShapeSetAll(i->tag,i->shape,/*x*/0,/*y*/0,0,fsid,0);
1903 i->swflastx = i->swflasty = 0;
1904 drawgfxline(dev, points, 1);
1905 swf_ShapeSetEnd(i->tag);
1906 swf_ShapeFree(i->shape);
1909 myshapeid2 = getNewID(dev);
1910 i->tag = swf_InsertTag(i->tag,ST_DEFINESHAPE3);
1911 swf_ShapeNew(&i->shape);
1913 rgb = i->config_linkcolor;
1915 fsid = swf_ShapeAddSolidFillStyle(i->shape,&rgb);
1916 swf_SetU16(i->tag, myshapeid2);
1917 r.xmin = (int)(bbox.xmin*20);
1918 r.ymin = (int)(bbox.ymin*20);
1919 r.xmax = (int)(bbox.xmax*20);
1920 r.ymax = (int)(bbox.ymax*20);
1921 r = swf_ClipRect(i->pagebbox, r);
1922 swf_SetRect(i->tag,&r);
1923 swf_SetShapeStyles(i->tag,i->shape);
1924 swf_ShapeCountBits(i->shape,NULL,NULL);
1925 swf_SetShapeBits(i->tag,i->shape);
1926 swf_ShapeSetAll(i->tag,i->shape,/*x*/0,/*y*/0,0,fsid,0);
1927 i->swflastx = i->swflasty = 0;
1928 drawgfxline(dev, points, 1);
1929 swf_ShapeSetEnd(i->tag);
1930 swf_ShapeFree(i->shape);
1934 i->tag = swf_InsertTag(i->tag,ST_DEFINEBUTTON);
1935 swf_SetU16(i->tag,buttonid); //id
1936 swf_ButtonSetFlags(i->tag, 0); //menu=no
1937 swf_ButtonSetRecord(i->tag,0x01,myshapeid,i->depth,0,0);
1938 swf_ButtonSetRecord(i->tag,0x02,myshapeid2,i->depth,0,0);
1939 swf_ButtonSetRecord(i->tag,0x04,myshapeid2,i->depth,0,0);
1940 swf_ButtonSetRecord(i->tag,0x08,myshapeid,i->depth,0,0);
1941 swf_SetU8(i->tag,0);
1942 swf_ActionSet(i->tag,actions1);
1943 swf_SetU8(i->tag,0);
1947 i->tag = swf_InsertTag(i->tag,ST_DEFINEBUTTON2);
1948 swf_SetU16(i->tag,buttonid); //id
1949 swf_ButtonSetFlags(i->tag, 0); //menu=no
1950 swf_ButtonSetRecord(i->tag,0x01,myshapeid,i->depth,0,0);
1951 swf_ButtonSetRecord(i->tag,0x02,myshapeid2,i->depth,0,0);
1952 swf_ButtonSetRecord(i->tag,0x04,myshapeid2,i->depth,0,0);
1953 swf_ButtonSetRecord(i->tag,0x08,myshapeid,i->depth,0,0);
1954 swf_SetU8(i->tag,0); // end of button records
1955 swf_ButtonSetCondition(i->tag, BC_IDLE_OVERUP);
1956 swf_ActionSet(i->tag,actions1);
1958 swf_ButtonSetCondition(i->tag, BC_OVERUP_IDLE);
1959 swf_ActionSet(i->tag,actions2);
1960 swf_SetU8(i->tag,0);
1961 swf_ButtonPostProcess(i->tag, 2);
1963 swf_SetU8(i->tag,0);
1964 swf_ButtonPostProcess(i->tag, 1);
1970 const char* name = 0;
1971 if(i->config_linknameurl) {
1972 buf2 = malloc(strlen(type)+strlen(url)+2);
1973 sprintf(buf2, "%s:%s", type, url);
1977 sprintf(buf, "button%d", buttonid);
1980 msg("<trace> Placing link ID %d", buttonid);
1981 i->tag = swf_InsertTag(i->tag,ST_PLACEOBJECT2);
1983 if(posx!=0 || posy!=0) {
1985 p.x = (int)(posx*20);
1986 p.y = (int)(posy*20);
1987 p = swf_TurnPoint(p, &i->page_matrix);
1992 swf_ObjectPlace(i->tag, buttonid, getNewDepth(dev),&m,0,(U8*)name);
1994 swf_ObjectPlace(i->tag, buttonid, getNewDepth(dev),&i->page_matrix,0,(U8*)name);
2004 for(t=0;t<picpos;t++)
2006 if(pic_xids[t] == xid &&
2007 pic_yids[t] == yid) {
2008 width = pic_width[t];
2009 height = pic_height[t];
2013 pic_ids[picpos] = swfoutput_drawimagelosslessN(&output, pic, pal, width, height, x1,y1,x2,y2,x3,y3,x4,y4, numpalette);
2014 pic_xids[picpos] = xid;
2015 pic_yids[picpos] = yid;
2016 pic_width[picpos] = width;
2017 pic_height[picpos] = height;
2020 pic[width*y+x] = buf[0];
2024 xid += pal[1].r*3 + pal[1].g*11 + pal[1].b*17;
2025 yid += pal[1].r*7 + pal[1].g*5 + pal[1].b*23;
2029 xid += x*r+x*b*3+x*g*7+x*a*11;
2030 yid += y*r*3+y*b*17+y*g*19+y*a*11;
2032 for(t=0;t<picpos;t++)
2034 if(pic_xids[t] == xid &&
2035 pic_yids[t] == yid) {
2044 int swf_setparameter(gfxdevice_t*dev, const char*name, const char*value)
2046 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
2048 msg("<trace> swfdevice: %s=%s", name, value);
2049 if(!strcmp(name, "jpegsubpixels")) {
2050 i->config_jpegsubpixels = atof(value);
2051 } else if(!strcmp(name, "ppmsubpixels")) {
2052 i->config_ppmsubpixels = atof(value);
2053 } else if(!strcmp(name, "subpixels")) {
2054 i->config_ppmsubpixels = i->config_jpegsubpixels = atof(value);
2055 } else if(!strcmp(name, "drawonlyshapes")) {
2056 i->config_drawonlyshapes = atoi(value);
2057 } else if(!strcmp(name, "ignoredraworder")) {
2058 i->config_ignoredraworder = atoi(value);
2059 } else if(!strcmp(name, "mark")) {
2060 if(!value || !value[0]) {
2061 if(i->mark) free(i->mark);
2065 i->mark = strdup("...");
2066 for(t=0;t<3;t++) if(value[t]) i->mark[t] = value[t];
2068 } else if(!strcmp(name, "filloverlap")) {
2069 i->config_filloverlap = atoi(value);
2070 } else if(!strcmp(name, "linksopennewwindow")) {
2071 i->config_opennewwindow = atoi(value);
2072 } else if(!strcmp(name, "opennewwindow")) {
2073 i->config_opennewwindow = atoi(value);
2074 } else if(!strcmp(name, "storeallcharacters")) {
2075 i->config_storeallcharacters = atoi(value);
2076 } else if(!strcmp(name, "enablezlib")) {
2077 i->config_enablezlib = atoi(value);
2078 } else if(!strcmp(name, "bboxvars")) {
2079 i->config_bboxvars = atoi(value);
2080 } else if(!strcmp(name, "dots")) {
2081 i->config_dots = atoi(value);
2082 } else if(!strcmp(name, "frameresets")) {
2083 i->config_frameresets = atoi(value);
2084 } else if(!strcmp(name, "showclipshapes")) {
2085 i->config_showclipshapes = atoi(value);
2086 } else if(!strcmp(name, "reordertags")) {
2087 i->config_reordertags = atoi(value);
2088 } else if(!strcmp(name, "internallinkfunction")) {
2089 i->config_internallinkfunction = strdup(value);
2090 } else if(!strcmp(name, "externallinkfunction")) {
2091 i->config_externallinkfunction = strdup(value);
2092 } else if(!strcmp(name, "linkfunction")) { //sets both internallinkfunction and externallinkfunction
2093 i->config_internallinkfunction = strdup(value);
2094 i->config_externallinkfunction = strdup(value);
2095 } else if(!strcmp(name, "disable_polygon_conversion")) {
2096 i->config_disable_polygon_conversion = atoi(value);
2097 } else if(!strcmp(name, "normalize_polygon_positions")) {
2098 i->config_normalize_polygon_positions = atoi(value);
2099 } else if(!strcmp(name, "wxwindowparams")) {
2100 i->config_watermark = atoi(value);
2101 } else if(!strcmp(name, "insertstop")) {
2102 i->config_insertstoptag = atoi(value);
2103 } else if(!strcmp(name, "protect")) {
2104 i->config_protect = atoi(value);
2105 if(i->config_protect && i->tag) {
2106 i->tag = swf_InsertTag(i->tag, ST_PROTECT);
2108 } else if(!strcmp(name, "flashversion")) {
2109 i->config_flashversion = atoi(value);
2111 i->swf->fileVersion = i->config_flashversion;
2113 } else if(!strcmp(name, "framerate")) {
2114 i->config_framerate = atof(value);
2116 i->swf->frameRate = i->config_framerate*0x100;
2118 } else if(!strcmp(name, "minlinewidth")) {
2119 i->config_minlinewidth = atof(value);
2120 } else if(!strcmp(name, "caplinewidth")) {
2121 i->config_caplinewidth = atof(value);
2122 } else if(!strcmp(name, "linktarget")) {
2123 i->config_linktarget = strdup(value);
2124 } else if(!strcmp(name, "invisibletexttofront")) {
2125 i->config_invisibletexttofront = atoi(value);
2126 } else if(!strcmp(name, "noclips")) {
2127 i->config_noclips = atoi(value);
2128 } else if(!strcmp(name, "dumpfonts")) {
2129 i->config_dumpfonts = atoi(value);
2130 } else if(!strcmp(name, "animate")) {
2131 i->config_animate = atoi(value);
2132 } else if(!strcmp(name, "disablelinks")) {
2133 i->config_disablelinks = atoi(value);
2134 } else if(!strcmp(name, "simpleviewer")) {
2135 i->config_simpleviewer = atoi(value);
2136 } else if(!strcmp(name, "next_bitmap_is_jpeg")) {
2138 } else if(!strcmp(name, "jpegquality")) {
2139 int val = atoi(value);
2141 if(val>101) val=101;
2142 i->config_jpegquality = val;
2143 } else if(!strcmp(name, "splinequality")) {
2144 int v = atoi(value);
2145 v = 500-(v*5); // 100% = 0.25 pixel, 0% = 25 pixel
2147 i->config_splinemaxerror = v;
2148 } else if(!strcmp(name, "fontquality")) {
2149 int v = atoi(value);
2150 v = 500-(v*5); // 100% = 0.25 pixel, 0% = 25 pixel
2152 i->config_fontsplinemaxerror = v;
2153 } else if(!strcmp(name, "linkcolor")) {
2154 if(strlen(value)!=8) {
2155 fprintf(stderr, "Unknown format for option 'linkcolor'. (%s <-> RRGGBBAA)\n", value);
2158 # define NIBBLE(s) (((s)>='0' && (s)<='9')?((s)-'0'):((s)&0x0f)+9)
2159 i->config_linkcolor.r = NIBBLE(value[0])<<4 | NIBBLE(value[1]);
2160 i->config_linkcolor.g = NIBBLE(value[2])<<4 | NIBBLE(value[3]);
2161 i->config_linkcolor.b = NIBBLE(value[4])<<4 | NIBBLE(value[5]);
2162 i->config_linkcolor.a = NIBBLE(value[6])<<4 | NIBBLE(value[7]);
2163 } else if(!strcmp(name, "help")) {
2164 printf("\nSWF layer options:\n");
2165 printf("jpegsubpixels=<pixels> resolution adjustment for jpeg images (same as jpegdpi, but in pixels)\n");
2166 printf("ppmsubpixels=<pixels resolution adjustment for lossless images (same as ppmdpi, but in pixels)\n");
2167 printf("subpixels=<pixels> shortcut for setting both jpegsubpixels and ppmsubpixels\n");
2168 printf("drawonlyshapes convert everything to shapes (currently broken)\n");
2169 printf("ignoredraworder allow to perform a few optimizations for creating smaller SWFs\n");
2170 printf("linksopennewwindow make links open a new browser window\n");
2171 printf("linktarget target window name of new links\n");
2172 printf("linkcolor=<color) color of links (format: RRGGBBAA)\n");
2173 printf("linknameurl Link buttons will be named like the URL they refer to (handy for iterating through links with actionscript)\n");
2174 printf("storeallcharacters don't reduce the fonts to used characters in the output file\n");
2175 printf("enablezlib switch on zlib compression (also done if flashversion>=6)\n");
2176 printf("bboxvars store the bounding box of the SWF file in actionscript variables\n");
2177 printf("dots Take care to handle dots correctly\n");
2178 printf("reordertags=0/1 (default: 1) perform some tag optimizations\n");
2179 printf("internallinkfunction=<name> when the user clicks a internal link (to a different page) in the converted file, this actionscript function is called\n");
2180 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");
2181 printf("disable_polygon_conversion never convert strokes to polygons (will remove capstyles and joint styles)\n");
2182 printf("caplinewidth=<width> the minimum thichness a line needs to have so that capstyles become visible (and are converted)\n");
2183 printf("insertstop put an ActionScript \"STOP\" tag in every frame\n");
2184 printf("protect add a \"protect\" tag to the file, to prevent loading in the Flash editor\n");
2185 printf("flashversion=<version> the SWF fileversion (6)\n");
2186 printf("framerate=<fps> SWF framerate\n");
2187 printf("minlinewidth=<width> convert horizontal/vertical boxes smaller than this width to lines (0.05) \n");
2188 printf("simpleviewer Add next/previous buttons to the SWF\n");
2189 printf("animate insert a showframe tag after each placeobject (animate draw order of PDF files)\n");
2190 printf("jpegquality=<quality> set compression quality of jpeg images\n");
2191 printf("splinequality=<value> Set the quality of spline convertion to value (0-100, default: 100).\n");
2192 printf("disablelinks Disable links.\n");
2199 // --------------------------------------------------------------------
2201 static CXFORM gfxcxform_to_cxform(gfxcxform_t* c)
2204 swf_GetCXForm(0, &cx, 1);
2207 if(c->rg!=0 || c->rb!=0 || c->ra!=0 ||
2208 c->gr!=0 || c->gb!=0 || c->ga!=0 ||
2209 c->br!=0 || c->bg!=0 || c->ba!=0 ||
2210 c->ar!=0 || c->ag!=0 || c->ab!=0)
2211 msg("<warning> CXForm not SWF-compatible");
2213 cx.a0 = (S16)(c->aa*256);
2214 cx.r0 = (S16)(c->rr*256);
2215 cx.g0 = (S16)(c->gg*256);
2216 cx.b0 = (S16)(c->bb*256);
2225 static int imageInCache(gfxdevice_t*dev, void*data, int width, int height)
2229 static void addImageToCache(gfxdevice_t*dev, void*data, int width, int height)
2233 static int add_image(swfoutput_internal*i, gfximage_t*img, int targetwidth, int targetheight, int* newwidth, int* newheight)
2235 gfxdevice_t*dev = i->dev;
2237 RGBA*mem = (RGBA*)img->data;
2239 int sizex = img->width;
2240 int sizey = img->height;
2241 int is_jpeg = i->jpeg;
2244 int newsizex=sizex, newsizey=sizey;
2247 if(is_jpeg && i->config_jpegsubpixels) {
2248 newsizex = (int)(targetwidth*i->config_jpegsubpixels + 0.5);
2249 newsizey = (int)(targetheight*i->config_jpegsubpixels + 0.5);
2250 } else if(!is_jpeg && i->config_ppmsubpixels) {
2251 newsizex = (int)(targetwidth*i->config_ppmsubpixels + 0.5);
2252 newsizey = (int)(targetheight*i->config_ppmsubpixels + 0.5);
2256 if(sizex<=0 || sizey<=0)
2263 /* TODO: cache images */
2265 if(newsizex<sizex || newsizey<sizey) {
2266 msg("<verbose> Scaling %dx%d image to %dx%d", sizex, sizey, newsizex, newsizey);
2267 newpic = swf_ImageScale(mem, sizex, sizey, newsizex, newsizey);
2268 *newwidth = sizex = newsizex;
2269 *newheight = sizey = newsizey;
2272 *newwidth = newsizex = sizex;
2273 *newheight = newsizey = sizey;
2276 int num_colors = swf_ImageGetNumberOfPaletteEntries(mem,sizex,sizey,0);
2277 int has_alpha = swf_ImageHasAlpha(mem,sizex,sizey);
2279 msg("<verbose> Drawing %dx%d %s%simage (id %d) at size %dx%d (%dx%d), %s%d colors",
2281 has_alpha?(has_alpha==2?"semi-transparent ":"transparent "):"",
2282 is_jpeg?"jpeg-":"", i->currentswfid+1,
2284 targetwidth, targetheight,
2285 /*newsizex, newsizey,*/
2286 num_colors>256?">":"", num_colors>256?256:num_colors);
2288 /*RGBA* pal = (RGBA*)rfx_alloc(sizeof(RGBA)*num_colors);
2289 swf_ImageGetNumberOfPaletteEntries(mem,sizex,sizey,pal);
2291 for(t=0;t<num_colors;t++) {
2292 printf("%02x%02x%02x%02x ",
2293 pal[t].r, pal[t].g, pal[t].b, pal[t].a);
2300 int cacheid = imageInCache(dev, mem, sizex, sizey);
2303 bitid = getNewID(dev);
2305 i->tag = swf_AddImage(i->tag, bitid, mem, sizex, sizey, i->config_jpegquality);
2306 addImageToCache(dev, mem, sizex, sizey);
2316 static SRECT gfxline_getSWFbbox(gfxline_t*line)
2318 gfxbbox_t bbox = gfxline_getbbox(line);
2320 r.xmin = (int)(bbox.xmin*20);
2321 r.ymin = (int)(bbox.ymin*20);
2322 r.xmax = (int)(bbox.xmax*20);
2323 r.ymax = (int)(bbox.ymax*20);
2327 int line_is_empty(gfxline_t*line)
2330 if(line->type != gfx_moveTo)
2337 static void swf_fillbitmap(gfxdevice_t*dev, gfxline_t*line, gfximage_t*img, gfxmatrix_t*matrix, gfxcxform_t*cxform)
2339 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
2341 if(line_is_empty(line))
2347 int targetx = (int)(sqrt(matrix->m00*matrix->m00 + matrix->m01*matrix->m01)*img->width);
2348 int targety = (int)(sqrt(matrix->m10*matrix->m10 + matrix->m11*matrix->m11)*img->height);
2350 int newwidth=0,newheight=0;
2351 int bitid = add_image(i, img, targetx, targety, &newwidth, &newheight);
2354 double fx = (double)img->width / (double)newwidth;
2355 double fy = (double)img->height / (double)newheight;
2358 m.sx = (int)(65536*20*matrix->m00*fx); m.r1 = (int)(65536*20*matrix->m10*fy);
2359 m.r0 = (int)(65536*20*matrix->m01*fx); m.sy = (int)(65536*20*matrix->m11*fy);
2360 m.tx = (int)(matrix->tx*20);
2361 m.ty = (int)(matrix->ty*20);
2364 int myshapeid = getNewID(dev);
2365 i->tag = swf_InsertTag(i->tag,ST_DEFINESHAPE);
2367 swf_ShapeNew(&shape);
2368 int fsid = swf_ShapeAddBitmapFillStyle(shape,&m,bitid,1);
2369 swf_SetU16(i->tag, myshapeid);
2370 SRECT r = gfxline_getSWFbbox(line);
2371 r = swf_ClipRect(i->pagebbox, r);
2372 swf_SetRect(i->tag,&r);
2373 swf_SetShapeStyles(i->tag,shape);
2374 swf_ShapeCountBits(shape,NULL,NULL);
2375 swf_SetShapeBits(i->tag,shape);
2376 swf_ShapeSetAll(i->tag,shape,UNDEFINED_COORD,UNDEFINED_COORD,0,fsid,0);
2377 i->swflastx = i->swflasty = UNDEFINED_COORD;
2378 drawgfxline(dev, line, 1);
2379 swf_ShapeSetEnd(i->tag);
2380 swf_ShapeFree(shape);
2382 msg("<trace> Placing image, shape ID %d, bitmap ID %d", myshapeid, bitid);
2383 i->tag = swf_InsertTag(i->tag,ST_PLACEOBJECT2);
2384 CXFORM cxform2 = gfxcxform_to_cxform(cxform);
2385 swf_ObjectPlace(i->tag,myshapeid,getNewDepth(dev),&i->page_matrix,&cxform2,NULL);
2388 static RGBA col_black = {255,0,0,0};
2390 static void drawoutline(gfxdevice_t*dev, gfxline_t*line)
2392 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
2394 int myshapeid = getNewID(dev);
2395 i->tag = swf_InsertTag(i->tag,ST_DEFINESHAPE3);
2398 swf_ShapeNew(&shape);
2399 int lsid = swf_ShapeAddLineStyle(shape,1,&col_black);
2401 swf_SetU16(i->tag,myshapeid);
2402 SRECT r = gfxline_getSWFbbox(line);
2403 r = swf_ClipRect(i->pagebbox, r);
2404 swf_SetRect(i->tag,&r);
2405 swf_SetShapeStyles(i->tag,shape);
2406 swf_ShapeCountBits(shape,NULL,NULL);
2407 swf_SetShapeBits(i->tag,shape);
2408 swf_ShapeSetAll(i->tag,shape,UNDEFINED_COORD,UNDEFINED_COORD,lsid,0,0);
2409 drawgfxline(dev, line, 1);
2410 swf_ShapeSetEnd(i->tag);
2411 swf_ShapeFree(shape);
2413 i->tag = swf_InsertTag(i->tag,ST_PLACEOBJECT2);
2414 swf_ObjectPlace(i->tag, myshapeid, getNewDepth(dev), 0,0,0);
2417 static void swf_startclip(gfxdevice_t*dev, gfxline_t*line)
2419 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
2420 if(i->config_noclips)
2426 if(i->clippos >= 127)
2428 msg("<warning> Too many clip levels.");
2432 if(i->config_showclipshapes)
2433 drawoutline(dev, line);
2435 int myshapeid = getNewID(dev);
2436 i->tag = swf_InsertTag(i->tag,ST_DEFINESHAPE3);
2438 memset(&col, 0, sizeof(RGBA));
2441 swf_ShapeNew(&shape);
2442 int fsid = swf_ShapeAddSolidFillStyle(shape,&col);
2444 RGBA markcol = {0,i->mark[0],i->mark[1],i->mark[2]};
2445 swf_ShapeAddSolidFillStyle(shape,&markcol);
2447 swf_SetU16(i->tag,myshapeid);
2448 SRECT r = gfxline_getSWFbbox(line);
2449 r = swf_ClipRect(i->pagebbox, r);
2450 swf_SetRect(i->tag,&r);
2451 swf_SetShapeStyles(i->tag,shape);
2452 swf_ShapeCountBits(shape,NULL,NULL);
2453 swf_SetShapeBits(i->tag,shape);
2454 swf_ShapeSetAll(i->tag,shape,UNDEFINED_COORD,UNDEFINED_COORD,0,fsid,0);
2455 i->swflastx = i->swflasty = UNDEFINED_COORD;
2456 i->shapeisempty = 1;
2457 drawgfxline(dev, line, 1);
2458 if(i->shapeisempty) {
2459 /* an empty clip shape is equivalent to a shape with no area */
2460 int x = line?line->x:0;
2461 int y = line?line->y:0;
2462 moveto(dev, i->tag, x,y);
2463 lineto(dev, i->tag, x,y);
2464 lineto(dev, i->tag, x,y);
2466 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)) {
2467 if(i->config_watermark) {
2468 gfxbbox_t r; r.xmin = r.ymin = 0;r.xmax = i->max_x;r.ymax = i->max_y;
2469 draw_watermark(dev, r, 1);
2472 swf_ShapeSetEnd(i->tag);
2473 swf_ShapeFree(shape);
2475 /* TODO: remember the bbox, and check all shapes against it */
2477 msg("<trace> Placing clip ID %d", myshapeid);
2478 i->tag = swf_InsertTag(i->tag,ST_PLACEOBJECT2);
2479 i->cliptags[i->clippos] = i->tag;
2480 i->clipshapes[i->clippos] = myshapeid;
2481 i->clipdepths[i->clippos] = getNewDepth(dev);
2485 static void swf_endclip(gfxdevice_t*dev)
2487 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
2488 if(i->config_noclips)
2496 msg("<error> Invalid end of clipping region");
2500 /*swf_ObjectPlaceClip(i->cliptags[i->clippos],i->clipshapes[i->clippos],i->clipdepths[i->clippos],&i->page_matrix,NULL,NULL,
2501 / * clip to depth: * / i->depth <= i->clipdepths[i->clippos]? i->depth : i->depth - 1);
2503 swf_ObjectPlaceClip(i->cliptags[i->clippos],i->clipshapes[i->clippos],i->clipdepths[i->clippos],&i->page_matrix,NULL,NULL,i->depth);
2505 static int gfxline_type(gfxline_t*line)
2511 int haszerosegments=0;
2514 if(line->type == gfx_moveTo) {
2517 } else if(line->type == gfx_lineTo) {
2521 } else if(line->type == gfx_splineTo) {
2523 if(tmpsplines>lines)
2531 if(lines==0 && splines==0) return 0;
2532 else if(lines==1 && splines==0) return 1;
2533 else if(lines==0 && splines==1) return 2;
2534 else if(splines==0) return 3;
2538 static int gfxline_has_dots(gfxline_t*line)
2546 if(line->type == gfx_moveTo) {
2547 /* test the length of the preceding line, and assume it is a dot if
2548 it's length is less than 1.0. But *only* if there's a noticable
2549 gap between the previous line and the next moveTo. (I've come
2550 across a PDF where thousands of "dots" were stringed together,
2552 int last_short_gap = short_gap;
2553 if((fabs(line->x - x) + fabs(line->y - y)) < 1.0) {
2558 if(isline && dist < 1 && !short_gap && !last_short_gap) {
2563 } else if(line->type == gfx_lineTo) {
2564 dist += fabs(line->x - x) + fabs(line->y - y);
2566 } else if(line->type == gfx_splineTo) {
2567 dist += fabs(line->sx - x) + fabs(line->sy - y) +
2568 fabs(line->x - line->sx) + fabs(line->y - line->sy);
2575 if(isline && dist < 1 && !short_gap) {
2581 static int gfxline_fix_short_edges(gfxline_t*line)
2585 if(line->type == gfx_lineTo) {
2586 if(fabs(line->x - x) + fabs(line->y - y) < 0.01) {
2589 } else if(line->type == gfx_splineTo) {
2590 if(fabs(line->sx - x) + fabs(line->sy - y) +
2591 fabs(line->x - line->sx) + fabs(line->y - line->sy) < 0.01) {
2602 static char is_inside_page(gfxdevice_t*dev, gfxcoord_t x, gfxcoord_t y)
2604 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
2605 if(x<i->min_x || x>i->max_x) return 0;
2606 if(y<i->min_y || y>i->max_y) return 0;
2610 gfxline_t* gfxline_move(gfxline_t*line, double x, double y)
2612 gfxline_t*l = line = gfxline_clone(line);
2624 //#define NORMALIZE_POLYGON_POSITIONS
2626 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)
2628 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
2629 if(line_is_empty(line))
2631 int type = gfxline_type(line);
2632 int has_dots = gfxline_has_dots(line);
2633 gfxbbox_t r = gfxline_getbbox(line);
2634 int is_outside_page = !is_inside_page(dev, r.xmin, r.ymin) || !is_inside_page(dev, r.xmax, r.ymax);
2636 /* TODO: * split line into segments, and perform this check for all segments */
2638 if(i->config_disable_polygon_conversion || /*type>=5 ||*/
2640 (width <= i->config_caplinewidth
2641 || (cap_style == gfx_capRound && joint_style == gfx_joinRound)
2642 || (cap_style == gfx_capRound && type<=2))))
2646 /* convert line to polygon */
2647 msg("<trace> draw as polygon, type=%d dots=%d", type, has_dots);
2649 gfxline_fix_short_edges(line);
2650 /* we need to convert the line into a polygon */
2651 gfxpoly_t* poly = gfxpoly_from_stroke(line, width, cap_style, joint_style, miterLimit, DEFAULT_GRID);
2652 gfxline_t*gfxline = gfxline_from_gfxpoly(poly);
2653 dev->fill(dev, gfxline, color);
2654 gfxline_free(gfxline);
2655 gfxpoly_destroy(poly);
2659 msg("<trace> draw as stroke, type=%d dots=%d", type, has_dots);
2662 if(i->config_normalize_polygon_positions) {
2664 double startx = 0, starty = 0;
2665 if(line && line->type == gfx_moveTo) {
2669 line = gfxline_move(line, -startx, -starty);
2670 i->shapeposx = (int)(startx*20);
2671 i->shapeposy = (int)(starty*20);
2674 swfoutput_setstrokecolor(dev, color->r, color->g, color->b, color->a);
2675 swfoutput_setlinewidth(dev, width);
2678 drawgfxline(dev, line, 0);
2680 if(i->config_normalize_polygon_positions) {
2681 free(line); //account for _move
2686 static void swf_fill(gfxdevice_t*dev, gfxline_t*line, gfxcolor_t*color)
2688 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
2689 if(line_is_empty(line))
2693 gfxbbox_t r = gfxline_getbbox(line);
2694 int is_outside_page = !is_inside_page(dev, r.xmin, r.ymin) || !is_inside_page(dev, r.xmax, r.ymax);
2698 if(!i->config_ignoredraworder)
2701 if(i->config_normalize_polygon_positions) {
2703 double startx = 0, starty = 0;
2704 if(line && line->type == gfx_moveTo) {
2708 line = gfxline_move(line, -startx, -starty);
2709 i->shapeposx = (int)(startx*20);
2710 i->shapeposy = (int)(starty*20);
2713 swfoutput_setfillcolor(dev, color->r, color->g, color->b, color->a);
2716 drawgfxline(dev, line, 1);
2718 if(i->currentswfid==2 && r.xmin==0 && r.ymin==0 && r.xmax==i->max_x && r.ymax==i->max_y) {
2719 if(i->config_watermark) {
2720 draw_watermark(dev, r, 1);
2724 msg("<trace> end of swf_fill (shapeid=%d)", i->shapeid);
2726 if(i->config_normalize_polygon_positions) {
2727 free(line); //account for _move
2731 static GRADIENT* gfxgradient_to_GRADIENT(gfxgradient_t*gradient)
2734 gfxgradient_t*g = gradient;
2739 GRADIENT* swfgradient = malloc(sizeof(GRADIENT));
2740 swfgradient->num = num;
2741 swfgradient->rgba = malloc(sizeof(swfgradient->rgba[0])*num);
2742 swfgradient->ratios = malloc(sizeof(swfgradient->ratios[0])*num);
2747 swfgradient->ratios[num] = g->pos*255;
2748 swfgradient->rgba[num] = *(RGBA*)&g->color;
2755 static void swf_fillgradient(gfxdevice_t*dev, gfxline_t*line, gfxgradient_t*gradient, gfxgradienttype_t type, gfxmatrix_t*matrix)
2757 if(line_is_empty(line))
2759 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
2761 if(line_is_empty(line))
2764 GRADIENT* swfgradient = gfxgradient_to_GRADIENT(gradient);
2771 double f = type==gfxgradient_radial?4:4;
2773 m.sx = (int)(matrix->m00*20*f); m.r1 = (int)(matrix->m10*20*f);
2774 m.r0 = (int)(matrix->m01*20*f); m.sy = (int)(matrix->m11*20*f);
2775 m.tx = (int)(matrix->tx*20);
2776 m.ty = (int)(matrix->ty*20);
2779 int myshapeid = getNewID(dev);
2780 i->tag = swf_InsertTag(i->tag, ST_DEFINESHAPE2);
2782 swf_ShapeNew(&shape);
2783 int fsid = swf_ShapeAddGradientFillStyle(shape,&m,swfgradient,type==gfxgradient_radial);
2784 swf_SetU16(i->tag, myshapeid);
2785 SRECT r = gfxline_getSWFbbox(line);
2786 r = swf_ClipRect(i->pagebbox, r);
2787 swf_SetRect(i->tag,&r);
2788 swf_SetShapeStyles(i->tag,shape);
2789 swf_ShapeCountBits(shape,NULL,NULL);
2790 swf_SetShapeBits(i->tag,shape);
2791 swf_ShapeSetAll(i->tag,shape,UNDEFINED_COORD,UNDEFINED_COORD,0,fsid,0);
2792 i->swflastx = i->swflasty = UNDEFINED_COORD;
2793 drawgfxline(dev, line, 1);
2794 swf_ShapeSetEnd(i->tag);
2795 swf_ShapeFree(shape);
2797 int depth = getNewDepth(dev);
2798 msg("<trace> Placing gradient, shape ID %d, depth %d", myshapeid, depth);
2799 i->tag = swf_InsertTag(i->tag,ST_PLACEOBJECT2);
2800 swf_ObjectPlace(i->tag,myshapeid,depth,&i->page_matrix,NULL,NULL);
2802 swf_FreeGradient(swfgradient);free(swfgradient);
2805 static SWFFONT* gfxfont_to_swffont(gfxfont_t*font, const char* id, int version)
2807 SWFFONT*swffont = (SWFFONT*)rfx_calloc(sizeof(SWFFONT));
2809 SRECT bounds = {0,0,0,0};
2811 swffont->version = version;
2812 swffont->name = (U8*)strdup(id);
2813 swffont->layout = (SWFLAYOUT*)rfx_calloc(sizeof(SWFLAYOUT));
2814 swffont->layout->ascent = 0;
2815 swffont->layout->descent = 0;
2816 swffont->layout->leading = 0;
2817 swffont->layout->bounds = (SRECT*)rfx_calloc(sizeof(SRECT)*font->num_glyphs);
2818 swffont->encoding = FONT_ENCODING_UNICODE;
2819 swffont->numchars = font->num_glyphs;
2820 swffont->maxascii = font->max_unicode;
2821 swffont->ascii2glyph = (int*)rfx_calloc(sizeof(int)*swffont->maxascii);
2822 swffont->glyph2ascii = (U16*)rfx_calloc(sizeof(U16)*swffont->numchars);
2823 swffont->glyph = (SWFGLYPH*)rfx_calloc(sizeof(SWFGLYPH)*swffont->numchars);
2824 swffont->glyphnames = (char**)rfx_calloc(sizeof(char*)*swffont->numchars);
2825 for(t=0;t<font->max_unicode;t++) {
2826 swffont->ascii2glyph[t] = font->unicode2glyph[t];
2828 SRECT max = {0,0,0,0};
2829 for(t=0;t<font->num_glyphs;t++) {
2833 swffont->glyph2ascii[t] = font->glyphs[t].unicode;
2834 if(swffont->glyph2ascii[t] == 0xffff || swffont->glyph2ascii[t] == 0x0000) {
2835 /* flash 8 flashtype requires unique unicode IDs for each character.
2836 We use the Unicode private user area to assign characters, hoping that
2837 the font doesn't contain more than 2048 glyphs */
2838 swffont->glyph2ascii[t] = 0xe000 + (t&0x1fff);
2841 if(font->glyphs[t].name) {
2842 swffont->glyphnames[t] = strdup(font->glyphs[t].name);
2844 swffont->glyphnames[t] = 0;
2846 advance = font->glyphs[t].advance;
2848 swf_Shape01DrawerInit(&draw, 0);
2849 line = font->glyphs[t].line;
2851 const double scale = GLYPH_SCALE;
2854 c.x = line->sx * scale; c.y = -line->sy * scale;
2855 //to.x = floor(line->x * scale); to.y = floor(-line->y * scale);
2856 to.x = line->x * scale; to.y = -line->y * scale;
2857 if(line->type == gfx_moveTo) {
2858 draw.moveTo(&draw, &to);
2859 } else if(line->type == gfx_lineTo) {
2860 draw.lineTo(&draw, &to);
2861 } else if(line->type == gfx_splineTo) {
2862 draw.splineTo(&draw, &c, &to);
2867 swffont->glyph[t].shape = swf_ShapeDrawerToShape(&draw);
2869 SRECT bbox = swf_ShapeDrawerGetBBox(&draw);
2870 swf_ExpandRect2(&max, &bbox);
2872 swffont->layout->bounds[t] = bbox;
2874 if(advance<32768.0/20) {
2875 swffont->glyph[t].advance = (int)(advance*20);
2877 //msg("<warning> Advance value overflow in glyph %d", t);
2878 swffont->glyph[t].advance = 32767;
2881 draw.dealloc(&draw);
2883 swf_ExpandRect2(&bounds, &swffont->layout->bounds[t]);
2885 for(t=0;t<font->num_glyphs;t++) {
2886 SRECT bbox = swffont->layout->bounds[t];
2888 /* if the glyph doesn't have a bounding box, use the
2889 combined bounding box (necessary e.g. for space characters) */
2890 if(!(bbox.xmin|bbox.ymin|bbox.xmax|bbox.ymax)) {
2891 swffont->layout->bounds[t] = bbox = max;
2894 /* check that the advance value is reasonable, by comparing it
2895 with the bounding box */
2896 if(bbox.xmax>0 && (bbox.xmax*10 < swffont->glyph[t].advance || !swffont->glyph[t].advance)) {
2897 if(swffont->glyph[t].advance)
2898 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);
2899 swffont->glyph[t].advance = bbox.xmax;
2901 //swffont->glyph[t].advance = bbox.xmax - bbox.xmin;
2905 /* Flash player will use the advance value from the char, and the ascent/descent values
2906 from the layout for text selection.
2907 ascent will extend the char into negative y direction, from the baseline, while descent
2908 will extend in positive y direction, also from the baseline.
2909 The baseline is defined as the y-position zero
2912 swffont->layout->ascent = bounds.ymin<0?-bounds.ymin:0;
2913 swffont->layout->descent = bounds.ymax>0?bounds.ymax:0;
2914 swffont->layout->leading = bounds.ymax - bounds.ymin;
2916 /* if the font has proper ascent/descent values (>0) and those define
2917 greater line spacing that what we estimated from the bounding boxes,
2918 use the font's parameters */
2919 if(font->ascent*20 > swffont->layout->ascent)
2920 swffont->layout->ascent = font->ascent*20;
2921 if(font->descent*20 > swffont->layout->descent)
2922 swffont->layout->descent = font->descent*20;
2927 static void swf_addfont(gfxdevice_t*dev, gfxfont_t*font)
2929 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
2931 if(i->swffont && i->swffont->name && !strcmp((char*)i->swffont->name,font->id))
2932 return; // the requested font is the current font
2934 fontlist_t*last=0,*l = i->fontlist;
2937 if(!strcmp((char*)l->swffont->name, font->id)) {
2938 return; // we already know this font
2942 l = (fontlist_t*)rfx_calloc(sizeof(fontlist_t));
2943 l->swffont = gfxfont_to_swffont(font, font->id, (i->config_flashversion>=8 && !NO_FONT3)?3:2);
2950 swf_FontSetID(l->swffont, getNewID(i->dev));
2952 if(getScreenLogLevel() >= LOGLEVEL_DEBUG) {
2954 // print font information
2955 msg("<debug> Font %s",font->id);
2956 msg("<debug> | ID: %d", l->swffont->id);
2957 msg("<debug> | Version: %d", l->swffont->version);
2958 msg("<debug> | Name: %s", l->swffont->name);
2959 msg("<debug> | Numchars: %d", l->swffont->numchars);
2960 msg("<debug> | Maxascii: %d", l->swffont->maxascii);
2961 msg("<debug> | Style: %d", l->swffont->style);
2962 msg("<debug> | Encoding: %d", l->swffont->encoding);
2963 for(iii=0; iii<l->swffont->numchars;iii++) {
2964 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,
2965 l->swffont->layout->bounds[iii].xmin/20.0,
2966 l->swffont->layout->bounds[iii].ymin/20.0,
2967 l->swffont->layout->bounds[iii].xmax/20.0,
2968 l->swffont->layout->bounds[iii].ymax/20.0
2971 for(t=0;t<l->swffont->maxascii;t++) {
2972 if(l->swffont->ascii2glyph[t] == iii)
2973 msg("<debug> | - maps to %d",t);
2979 static void swf_switchfont(gfxdevice_t*dev, const char*fontid)
2981 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
2983 if(i->swffont && i->swffont->name && !strcmp((char*)i->swffont->name,fontid))
2984 return; // the requested font is the current font
2986 fontlist_t*l = i->fontlist;
2988 if(!strcmp((char*)l->swffont->name, fontid)) {
2989 i->swffont = l->swffont;
2994 msg("<error> Unknown font id: %s", fontid);
2998 /* sets the matrix which is to be applied to characters drawn by swfoutput_drawchar() */
2999 static void setfontscale(gfxdevice_t*dev,double m11,double m12, double m21,double m22,double x, double y, char force)
3005 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
3006 if(i->lastfontm11 == m11 &&
3007 i->lastfontm12 == m12 &&
3008 i->lastfontm21 == m21 &&
3009 i->lastfontm22 == m22 && !force)
3014 i->lastfontm11 = m11;
3015 i->lastfontm12 = m12;
3016 i->lastfontm21 = m21;
3017 i->lastfontm22 = m22;
3019 double xsize = sqrt(m11*m11 + m12*m12);
3020 double ysize = sqrt(m21*m21 + m22*m22);
3023 if(i->config_flashversion>=8 && !NO_FONT3)
3026 i->current_font_size = (xsize>ysize?xsize:ysize)*extrazoom;
3027 if(i->current_font_size < 1)
3028 i->current_font_size = 1;
3031 swf_GetMatrix(0, &m);
3033 if(m21 || m12 || fabs(m11+m22)>0.001) {
3034 double ifs = (double)extrazoom/(i->current_font_size);
3035 m.sx = (S32)((m11*ifs)*65536); m.r1 = -(S32)((m21*ifs)*65536);
3036 m.r0 = (S32)((m12*ifs)*65536); m.sy = -(S32)((m22*ifs)*65536);
3039 /* this is the position of the first char to set a new fontmatrix-
3040 we hope that it's close enough to all other characters using the
3041 font, so we use its position as origin for the matrix */
3048 static void swf_drawchar(gfxdevice_t*dev, gfxfont_t*font, int glyph, gfxcolor_t*color, gfxmatrix_t*matrix)
3050 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
3052 msg("<error> swf_drawchar called (glyph %d) without font", glyph);
3056 if(i->config_drawonlyshapes) {
3057 gfxglyph_t*g = &font->glyphs[glyph];
3058 gfxline_t*line2 = gfxline_clone(g->line);
3059 gfxline_transform(line2, matrix);
3060 dev->fill(dev, line2, color);
3061 gfxline_free(line2);
3065 if(!i->swffont || !i->swffont->name || strcmp((char*)i->swffont->name,font->id)) // not equal to current font
3067 swf_switchfont(dev, font->id); // set the current font
3071 msg("<warning> swf_drawchar: Font is NULL");
3074 if(glyph<0 || glyph>=i->swffont->numchars) {
3075 msg("<warning> No character %d in font %s (%d chars)", glyph, FIXNULL((char*)i->swffont->name), i->swffont->numchars);
3079 setfontscale(dev, matrix->m00, matrix->m01, matrix->m10, matrix->m11, matrix->tx, matrix->ty, 0);
3081 double det = i->fontmatrix.sx/65536.0 * i->fontmatrix.sy/65536.0 -
3082 i->fontmatrix.r0/65536.0 * i->fontmatrix.r1/65536.0;
3083 if(fabs(det) < 0.0005) {
3084 /* x direction equals y direction- the text is invisible */
3085 msg("<verbose> Not drawing invisible character %d (det=%f, m=[%f %f;%f %f]\n", glyph,
3087 i->fontmatrix.sx/65536.0, i->fontmatrix.r1/65536.0,
3088 i->fontmatrix.r0/65536.0, i->fontmatrix.sy/65536.0);
3092 /*if(i->swffont->glyph[glyph].shape->bitlen <= 16) {
3093 msg("<warning> Glyph %d in current charset (%s, %d characters) is empty",
3094 glyph, FIXNULL((char*)i->swffont->name), i->swffont->numchars);
3098 /* calculate character position with respect to the current font matrix */
3099 double s = 20 * GLYPH_SCALE / det;
3100 double px = matrix->tx - i->fontmatrix.tx/20.0;
3101 double py = matrix->ty - i->fontmatrix.ty/20.0;
3102 int x = (SCOORD)(( px * i->fontmatrix.sy/65536.0 - py * i->fontmatrix.r1/65536.0)*s);
3103 int y = (SCOORD)((- px * i->fontmatrix.r0/65536.0 + py * i->fontmatrix.sx/65536.0)*s);
3104 if(x>32767 || x<-32768 || y>32767 || y<-32768) {
3105 msg("<verbose> Moving character origin to %f %f\n", matrix->tx, matrix->ty);
3107 setfontscale(dev, matrix->m00, matrix->m01, matrix->m10, matrix->m11, matrix->tx, matrix->ty, 1);
3108 /* since we just moved the char origin to the current char's position,
3109 it now has the relative position (0,0) */
3118 msg("<trace> Drawing char %d in font %d at %d,%d in color %02x%02x%02x%02x",
3119 glyph, i->swffont->id, x, y, color->r, color->g, color->b, color->a);
3121 if(color->a == 0 && i->config_invisibletexttofront) {
3122 RGBA color2 = *(RGBA*)color;
3123 if(i->config_flashversion>=8) {
3124 // use "multiply" blend mode
3125 color2.a = color2.r = color2.g = color2.b = 255;
3127 i->topchardata = charbuffer_append(i->topchardata, i->swffont, glyph, x, y, i->current_font_size, color2, &i->fontmatrix);
3129 i->chardata = charbuffer_append(i->chardata, i->swffont, glyph, x, y, i->current_font_size, *(RGBA*)color, &i->fontmatrix);
3131 swf_FontUseGlyph(i->swffont, glyph, i->current_font_size);