3 Part of the swftools package.
5 Copyright (c) 2001,2002,2003,2004,2005 Matthias Kramm <kramm@quiss.org>
7 Swftools is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 2 of the License, or
10 (at your option) any later version.
12 Swftools is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
17 You should have received a copy of the GNU General Public License
18 along with swftools; if not, write to the Free Software
19 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
24 #include "../../config.h"
40 #include "../rfxswf.h"
41 #include "../gfxdevice.h"
42 #include "../gfxtools.h"
44 #include "../gfxpoly.h"
45 #include "../gfximage.h"
47 #define CHARDATAMAX 1024
51 typedef struct _charatposition {
60 typedef struct _chararray {
61 charatposition_t chr[CHARDATAMAX+1];
63 struct _chararray *next;
66 typedef struct _charbuffer {
70 struct _charbuffer *next;
73 typedef struct _fontlist
76 struct _fontlist*next;
79 typedef long int twip;
81 typedef struct _swfmatrix {
82 double m11,m12,m21,m22,m31,m32;
85 typedef struct _swfoutput_internal
87 gfxdevice_t*dev; // the gfxdevice object where this internal struct resides
89 double config_dumpfonts;
90 double config_ppmsubpixels;
91 double config_jpegsubpixels;
93 int config_invisibletexttofront;
95 int config_simpleviewer;
96 int config_opennewwindow;
97 int config_ignoredraworder;
98 int config_drawonlyshapes;
99 int config_frameresets;
100 int config_linknameurl;
101 int config_jpegquality;
102 int config_storeallcharacters;
103 int config_enablezlib;
104 int config_insertstoptag;
105 int config_showimages;
106 int config_watermark;
108 int config_flashversion;
109 int config_reordertags;
110 int config_showclipshapes;
111 int config_splinemaxerror;
112 int config_fontsplinemaxerror;
113 int config_filloverlap;
116 int config_disable_polygon_conversion;
117 int config_normalize_polygon_positions;
118 int config_alignfonts;
119 char config_disablelinks;
120 RGBA config_linkcolor;
121 float config_minlinewidth;
122 double config_caplinewidth;
123 char* config_linktarget;
124 char*config_internallinkfunction;
125 char*config_externallinkfunction;
127 double config_framerate;
131 fontlist_t* fontlist;
170 int pic_height[1024];
177 char fillstylechanged;
179 int jpeg; //next image type
186 charbuffer_t* chardata;
187 charbuffer_t* topchardata; //chars supposed to be above everything else
194 int current_font_size;
196 double lastfontm11,lastfontm12,lastfontm21,lastfontm22;
207 } swfoutput_internal;
209 static const int NO_FONT3=0;
211 static void swf_fillbitmap(gfxdevice_t*driver, gfxline_t*line, gfximage_t*img, gfxmatrix_t*move, gfxcxform_t*cxform);
212 static int swf_setparameter(gfxdevice_t*driver, const char*key, const char*value);
213 static void swf_drawstroke(gfxdevice_t*dev, gfxline_t*line, gfxcoord_t width, gfxcolor_t*color, gfx_capType cap_style, gfx_joinType joint_style, gfxcoord_t miterLimit);
214 static void swf_startclip(gfxdevice_t*dev, gfxline_t*line);
215 static void swf_endclip(gfxdevice_t*dev);
216 static void swf_stroke(gfxdevice_t*dev, gfxline_t*line, gfxcoord_t width, gfxcolor_t*color, gfx_capType cap_style, gfx_joinType joint_style, gfxcoord_t miterLimit);
217 static void swf_fill(gfxdevice_t*dev, gfxline_t*line, gfxcolor_t*color);
218 static void swf_fillbitmap(gfxdevice_t*dev, gfxline_t*line, gfximage_t*img, gfxmatrix_t*matrix, gfxcxform_t*cxform);
219 static void swf_fillgradient(gfxdevice_t*dev, gfxline_t*line, gfxgradient_t*gradient, gfxgradienttype_t type, gfxmatrix_t*matrix);
220 static void swf_drawchar(gfxdevice_t*dev, gfxfont_t*font, int glyph, gfxcolor_t*color, gfxmatrix_t*matrix);
221 static void swf_addfont(gfxdevice_t*dev, gfxfont_t*font);
222 static void swf_drawlink(gfxdevice_t*dev, gfxline_t*line, const char*action);
223 static void swf_startframe(gfxdevice_t*dev, int width, int height);
224 static void swf_endframe(gfxdevice_t*dev);
225 static void swfoutput_namedlink(gfxdevice_t*dev, char*name, gfxline_t*points);
226 static void swfoutput_linktopage(gfxdevice_t*dev, int page, gfxline_t*points);
227 static void swfoutput_linktourl(gfxdevice_t*dev, const char*url, gfxline_t*points);
229 static gfxresult_t* swf_finish(gfxdevice_t*driver);
231 static swfoutput_internal* init_internal_struct()
233 swfoutput_internal*i = (swfoutput_internal*)malloc(sizeof(swfoutput_internal));
234 memset(i, 0, sizeof(swfoutput_internal));
258 i->fillstylechanged = 0;
265 i->config_disablelinks=0;
266 i->config_dumpfonts=0;
267 i->config_ppmsubpixels=0;
268 i->config_jpegsubpixels=0;
269 i->config_opennewwindow=1;
270 i->config_ignoredraworder=0;
271 i->config_drawonlyshapes=0;
272 i->config_jpegquality=85;
273 i->config_storeallcharacters=0;
275 i->config_enablezlib=0;
276 i->config_insertstoptag=0;
277 i->config_flashversion=6;
278 i->config_framerate=0.25;
279 i->config_splinemaxerror=1;
280 i->config_fontsplinemaxerror=1;
281 i->config_filloverlap=0;
283 i->config_bboxvars=0;
284 i->config_showclipshapes=0;
285 i->config_minlinewidth=0.05;
286 i->config_caplinewidth=1;
287 i->config_linktarget=0;
288 i->config_internallinkfunction=0;
289 i->config_externallinkfunction=0;
290 i->config_reordertags=1;
291 i->config_linknameurl=0;
293 i->config_linkcolor.r = i->config_linkcolor.g = i->config_linkcolor.b = 255;
294 i->config_linkcolor.a = 0x40;
299 static int id_error = 0;
301 static U16 getNewID(gfxdevice_t* dev)
303 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
304 if(i->currentswfid == 65535) {
306 msg("<error> ID Table overflow");
307 msg("<error> This file is too complex to render- SWF only supports 65536 shapes at once");
313 return ++i->currentswfid;
315 static U16 getNewDepth(gfxdevice_t* dev)
317 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
318 if(i->depth == 65520) {
320 msg("<error> Depth Table overflow");
321 msg("<error> This file is too complex to render- SWF only supports 65536 shapes at once");
330 static void startshape(gfxdevice_t* dev);
331 static void starttext(gfxdevice_t* dev);
332 static void endshape(gfxdevice_t* dev);
333 static void endtext(gfxdevice_t* dev);
335 typedef struct _plotxy
340 static inline int twipsnap(double f)
342 /* if(f < -0x40000000/20.0) {
343 fprintf(stderr, "Warning: Coordinate underflow (%f)\n", f);
344 f = -0x40000000/20.0;
345 } else if(f>0x3fffffff/20.0) {
346 fprintf(stderr, "Warning: Coordinate overflow (%f)\n", f);
350 /* clamp coordinates to a rectangle with the property that we
351 can represent a line from the upper left corner to the upper
352 right corner using no more than 64 strokes */
353 const double min = -(1<<(18+4))/20.0;
354 const double max = ((1<<(18+4))-1)/20.0;
356 fprintf(stderr, "Warning: Coordinate underflow (%f)\n", f);
359 fprintf(stderr, "Warning: Coordinate overflow (%f)\n", f);
366 // write a move-to command into the swf
367 static int movetoxy(gfxdevice_t*dev, TAG*tag, plotxy_t p0)
369 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
370 int rx = twipsnap(p0.x);
371 int ry = twipsnap(p0.y);
372 if(rx!=i->swflastx || ry!=i->swflasty || i->fillstylechanged) {
373 swf_ShapeSetMove (tag, i->shape, rx,ry);
374 i->fillstylechanged = 0;
381 static int moveto(gfxdevice_t*dev, TAG*tag, double x, double y)
383 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
387 return movetoxy(dev, tag, p);
389 static void addPointToBBox(gfxdevice_t*dev, int px, int py)
391 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
397 swf_ExpandRect(&i->bboxrect, p);
399 swf_ExpandRect3(&i->bboxrect, p, i->linewidth*3/2);
403 /*static void plot(gfxdevice_t*dev, int x, int y, TAG*tag)
405 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
406 int width = i->linewidth/4;
410 //swf_ShapeSetLine(tag, i->shape,-width,-width);
411 //swf_ShapeSetLine(tag, i->shape,width*2,0);
412 //swf_ShapeSetLine(tag, i->shape,0,width*2);
413 //swf_ShapeSetLine(tag, i->shape,-width*2,0);
414 //swf_ShapeSetLine(tag, i->shape,0,-width*2);
415 //swf_ShapeSetLine(tag, i->shape,width,width);
418 swf_ShapeSetLine(tag, i->shape,-width,0);
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,-width);
423 swf_ShapeSetLine(tag, i->shape,width,0);
425 addPointToBBox(dev, x-width ,y-width);
426 addPointToBBox(dev, x+width ,y+width);
429 // write a line-to command into the swf
430 static void linetoxy(gfxdevice_t*dev, TAG*tag, plotxy_t p0)
432 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
433 int px = twipsnap(p0.x);
434 int py = twipsnap(p0.y);
435 int rx = (px-i->swflastx);
436 int ry = (py-i->swflasty);
438 swf_ShapeSetLine (tag, i->shape, rx,ry);
439 addPointToBBox(dev, i->swflastx,i->swflasty);
440 addPointToBBox(dev, px,py);
441 } /* this is a nice idea, but doesn't work with current flash
442 players (the pixel will be invisible if they're not
443 precisely on a pixel boundary)
444 Besides, we should only do this if this lineto itself
445 is again followed by a "move".
446 else if(!i->fill && i->config_dots) {
447 // treat lines of length 0 as plots, making them
448 // at least 1 twip wide so Flash will display them
449 //plot(dev, i->swflastx, i->swflasty, tag);
450 swf_ShapeSetLine (tag, i->shape, rx+1,ry);
457 static void lineto(gfxdevice_t*dev, TAG*tag, double x, double y)
462 linetoxy(dev,tag, p);
465 // write a spline-to command into the swf
466 static void splineto(gfxdevice_t*dev, TAG*tag, plotxy_t control,plotxy_t end)
468 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
469 int lastlastx = i->swflastx;
470 int lastlasty = i->swflasty;
472 int cx = (twipsnap(control.x)-i->swflastx);
473 int cy = (twipsnap(control.y)-i->swflasty);
476 int ex = (twipsnap(end.x)-i->swflastx);
477 int ey = (twipsnap(end.y)-i->swflasty);
481 if((cx || cy) && (ex || ey)) {
482 swf_ShapeSetCurve(tag, i->shape, cx,cy,ex,ey);
483 addPointToBBox(dev, lastlastx ,lastlasty );
484 addPointToBBox(dev, lastlastx+cx,lastlasty+cy);
485 addPointToBBox(dev, lastlastx+cx+ex,lastlasty+cy+ey);
486 } else if(cx || cy || ex || ey) {
487 swf_ShapeSetLine(tag, i->shape, cx+ex,cy+ey);
488 addPointToBBox(dev, lastlastx ,lastlasty );
489 addPointToBBox(dev, lastlastx+cx,lastlasty+cy);
490 addPointToBBox(dev, lastlastx+cx+ex,lastlasty+cy+ey);
496 /* write a line, given two points and the transformation
498 /*static void line(gfxdevice_t*dev, TAG*tag, plotxy_t p0, plotxy_t p1)
500 moveto(dev, tag, p0);
501 lineto(dev, tag, p1);
504 void resetdrawer(gfxdevice_t*dev)
506 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
511 static void stopFill(gfxdevice_t*dev)
513 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
514 if(i->lastwasfill!=0)
516 swf_ShapeSetStyle(i->tag,i->shape,i->linestyleid,0x8000,0);
517 i->fillstylechanged = 1;
521 static void startFill(gfxdevice_t*dev)
523 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
524 if(i->lastwasfill!=1)
526 swf_ShapeSetStyle(i->tag,i->shape,0x8000,i->fillstyleid,0);
527 i->fillstylechanged = 1;
532 static inline int colorcompare(RGBA*a,RGBA*b)
544 static SRECT getcharacterbbox(chararray_t*chardata, MATRIX* m, int flashversion)
548 memset(&r, 0, sizeof(r));
551 if(debug) printf("\n");
553 double div = 1.0 / 1024.0;
554 if(flashversion>=8 && !NO_FONT3) {
559 for(t=0;t<chardata->pos;t++) {
560 charatposition_t*chr = &chardata->chr[t];
561 SRECT b = chr->font->layout->bounds[chr->charid];
562 b.xmin = floor((b.xmin*(double)chr->size) *div);
563 b.ymin = floor((b.ymin*(double)chr->size) *div);
564 b.xmax = ceil((b.xmax*(double)chr->size) *div);
565 b.ymax = ceil((b.ymax*(double)chr->size) *div);
572 /* until we solve the INTERNAL_SCALING problem (see below)
573 make sure the bounding box is big enough */
579 b = swf_TurnRect(b, m);
581 if(debug) printf("(%f,%f,%f,%f) -> (%f,%f,%f,%f) [font %d, char %d]\n",
582 chr->font->layout->bounds[chr->charid].xmin/20.0,
583 chr->font->layout->bounds[chr->charid].ymin/20.0,
584 chr->font->layout->bounds[chr->charid].xmax/20.0,
585 chr->font->layout->bounds[chr->charid].ymax/20.0,
592 swf_ExpandRect2(&r, &b);
594 chardata = chardata->next;
596 if(debug) printf("-----> (%f,%f,%f,%f)\n",
604 static chararray_t*chararray_reverse(chararray_t*buf)
606 chararray_t*prev = 0;
608 chararray_t*next = buf->next;
616 static void chararray_writetotag(chararray_t*_chardata, TAG*tag)
620 color.r = _chardata?_chardata->chr[0].color.r^255:0;
630 int charadvance[128];
633 int glyphbits=1; //TODO: can this be zero?
636 if(tag->id != ST_DEFINETEXT &&
637 tag->id != ST_DEFINETEXT2) {
638 msg("<error> internal error: charbuffer_put needs an text tag, not %d\n",tag->id);
642 msg("<warning> charbuffer_put called with zero characters");
645 for(pass = 0; pass < 2; pass++)
656 advancebits++; // add sign bit
657 swf_SetU8(tag, glyphbits);
658 swf_SetU8(tag, advancebits);
661 chararray_t*chardata = _chardata;
666 assert(!chardata->next || chardata->pos == CHARDATAMAX);
667 assert(chardata->pos);
669 int to = chardata->next?chardata->pos-1:chardata->pos;
673 char islast = t==chardata->pos;
675 charatposition_t*chr = &chardata->chr[t];
677 if(lastfont != chr->font ||
680 !colorcompare(&color, &chardata->chr[t].color) ||
682 lastsize != chardata->chr[t].size ||
685 if(charstorepos && pass==0)
688 for(s=0;s<charstorepos;s++)
690 while(charids[s]>=(1<<glyphbits))
692 while(charadvance[s]>=(1<<advancebits))
696 if(charstorepos && pass==1)
698 tag->writeBit = 0; // Q&D
699 swf_SetBits(tag, 0, 1); // GLYPH Record
700 swf_SetBits(tag, charstorepos, 7); // number of glyphs
702 for(s=0;s<charstorepos;s++)
704 swf_SetBits(tag, charids[s], glyphbits);
705 swf_SetBits(tag, charadvance[s], advancebits);
710 if(pass == 1 && !islast)
716 if(lastx != chr->x ||
726 if(!colorcompare(&color, &chr->color))
731 font.id = chr->font->id;
732 if(lastfont != chr->font || lastsize != chr->size)
735 tag->writeBit = 0; // Q&D
736 swf_TextSetInfoRecord(tag, newfont, chr->size, newcolor, newx, newy);
739 lastfont = chr->font;
742 lastsize = chr->size;
749 if(t<chardata->pos-1) nextx = chardata->chr[t+1].x;
750 if(t==chardata->pos-1 && chardata->next) nextx = chardata->next->chr[0].x;
751 int dx = nextx-chr->x;
754 if(dx>=0 && (dx<(1<<(advancebits-1)) || pass==0)) {
762 charids[charstorepos] = chr->charid;
763 charadvance[charstorepos] = advance;
764 lastchar = chr->charid;
767 chardata = chardata->next;
772 static void chararray_destroy(chararray_t*chr)
775 chararray_t*next = chr->next;
782 static inline int matrix_diff(MATRIX*m1, MATRIX*m2)
784 return memcmp(m1,m2,sizeof(MATRIX));
786 static charbuffer_t*charbuffer_append(charbuffer_t*buf, SWFFONT*font, int charid, int x,int y, int size, RGBA color, MATRIX*m)
788 if(!buf || matrix_diff(&buf->matrix,m)) {
789 charbuffer_t*n = rfx_calloc(sizeof(charbuffer_t));
794 if(!buf->last || buf->last->pos == CHARDATAMAX) {
795 chararray_t*n = rfx_calloc(sizeof(chararray_t));
797 buf->array = buf->last = n;
803 chararray_t*a = buf->last;
804 a->chr[a->pos].font = font;
805 a->chr[a->pos].charid = charid;
806 a->chr[a->pos].x = x;
807 a->chr[a->pos].y = y;
808 a->chr[a->pos].color = color;
809 a->chr[a->pos].size = size;
814 /* Notice: we can only put chars in the range -1639,1638 (-32768/20,32768/20).
815 So if we set this value to high, the char coordinates will overflow.
816 If we set it to low, however, the char positions will be inaccurate */
817 #define GLYPH_SCALE 1
819 static void chararray_writetodev(gfxdevice_t*dev, chararray_t*array, MATRIX*matrix, char invisible)
821 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
823 int textid = getNewID(dev);
824 i->tag = swf_InsertTag(i->tag,ST_DEFINETEXT2);
825 swf_SetU16(i->tag, textid);
827 r = getcharacterbbox(array, matrix, i->config_flashversion);
828 r = swf_ClipRect(i->pagebbox, r);
829 swf_SetRect(i->tag,&r);
830 swf_SetMatrix(i->tag, matrix);
831 msg("<trace> Placing text as ID %d", textid);
832 chararray_writetotag(array, i->tag);
837 if(i->swf->fileVersion >= 8) {
838 i->tag = swf_InsertTag(i->tag, ST_CSMTEXTSETTINGS);
839 swf_SetU16(i->tag, textid);
841 //swf_SetU8(i->tag, /*subpixel grid*/(2<<3)|/*flashtype*/0x40);
842 swf_SetU8(i->tag, /*grid*/(1<<3)|/*flashtype*/0x40);
843 //swf_SetU8(i->tag, /*no grid*/(0<<3)|/*flashtype*/0x40);
845 swf_SetU32(i->tag, 0);//thickness
846 swf_SetU32(i->tag, 0);//sharpness
847 //swf_SetU32(i->tag, 0x20000);//thickness
848 //swf_SetU32(i->tag, 0x800000);//sharpness
849 swf_SetU8(i->tag, 0);//reserved
851 if(invisible && i->config_flashversion>=8) {
852 i->tag = swf_InsertTag(i->tag,ST_PLACEOBJECT3);
853 swf_ObjectPlaceBlend(i->tag,textid,getNewDepth(dev),&i->page_matrix,NULL,NULL,BLENDMODE_MULTIPLY);
855 i->tag = swf_InsertTag(i->tag,ST_PLACEOBJECT2);
856 swf_ObjectPlace(i->tag,textid,getNewDepth(dev),&i->page_matrix,NULL,NULL);
860 static void charbuffer_writetodevandfree(gfxdevice_t*dev, charbuffer_t*buf, char invisible)
863 charbuffer_t*next = buf->next;buf->next = 0;
864 chararray_writetodev(dev, buf->array, &buf->matrix, invisible);
865 chararray_destroy(buf->array);
871 static void endtext(gfxdevice_t*dev)
873 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
876 charbuffer_writetodevandfree(dev, i->chardata, 0);i->chardata = 0;
880 static int watermark2_width=47;
881 static int watermark2_height=11;
882 static int watermark2[47] = {95,1989,71,0,2015,337,1678,0,2015,5,1921,320,1938,25,2006,1024,
883 1042,21,13,960,1039,976,8,2000,1359,1088,31,1989,321,1728,0,1152,
884 1344,832,0,1984,0,896,1088,1088,896,0,1984,128,256,512,1984};
886 static void draw_watermark(gfxdevice_t*dev, gfxbbox_t r, char drawall)
888 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
889 double wx = r.xmax / 5.0;
890 double tx = r.xmax*4.0 / 5.0;
891 double ty = r.ymax-wx*watermark2_height/watermark2_width;
892 double sx = (r.xmax - tx) / watermark2_width;
893 double sy = (r.ymax - ty) / watermark2_height;
896 if(ty > 0 && px > 1.0 && py > 1.0) {
898 for(y=0;y<watermark2_height;y++)
899 for(x=0;x<watermark2_width;x++) {
900 if(((watermark2[x]>>y)&1)) {
901 if(!drawall && rand()%5)
903 unsigned int b = rand();
904 moveto(dev, i->tag, x*sx+tx+((b>>1)&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+ty+((b>>3)&1)/20.0);
906 lineto(dev, i->tag, x*sx+px+tx+((b>>2)&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+py+ty+((b>>4)&1)/20.0);
908 lineto(dev, i->tag, x*sx+tx+((b>>1)&1)/20.0, y*sy+ty+((b>>3)&1)/20.0);
914 static void swfoutput_setfillcolor(gfxdevice_t* dev, U8 r, U8 g, U8 b, U8 a)
916 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
917 if(i->fillrgb.r == r &&
920 i->fillrgb.a == a) return;
929 static void insert_watermark(gfxdevice_t*dev, char drawall)
931 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
932 if(!drawall && i->watermarks>20)
938 swfoutput_setfillcolor(dev, 0,0,255,192);
940 swfoutput_setfillcolor(dev, rand(),rand(),rand(),(rand()&127)+128);
945 gfxbbox_t r; r.xmin = r.ymin = 0;
948 draw_watermark(dev, r, drawall);
954 static void endpage(gfxdevice_t*dev)
956 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
964 charbuffer_writetodevandfree(dev, i->topchardata, 1);
971 if(i->config_watermark) {
972 insert_watermark(dev, 1);
978 static void addViewer(gfxdevice_t* dev)
980 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
983 RGBA button_colors[3]= {{0xbf,0x00,0x00,0x80},{0xbf,0x20,0x20,0xc0}, {0xbf,0xc0,0xc0,0xff}};
985 int button_sizex = 20;
986 int button_sizey = 20;
988 RGBA black = {255,0,0,0};
990 i->tag = swf_InsertTag(i->tag,ST_DEFINESHAPE3);
992 int ls1 = swf_ShapeAddLineStyle(s,40,&black);
993 int fs1 = swf_ShapeAddSolidFillStyle(s,&button_colors[t/2]);
994 int shapeid = ids[t] = getNewID(dev);
995 swf_SetU16(i->tag,shapeid);
997 r.xmin = -20*button_sizex;
998 r.xmax = 20*button_sizex;
1000 r.ymax = 40*button_sizey;
1001 swf_SetRect(i->tag,&r); // set shape bounds
1002 swf_SetShapeHeader(i->tag,s); // write all styles to tag
1003 swf_ShapeSetAll(i->tag,s,0*button_sizex,0,ls1,fs1,0);
1004 swf_ShapeSetLine(i->tag,s,(1-(t&1)*2)*20*button_sizex,20*button_sizey);
1005 swf_ShapeSetLine(i->tag,s,-(1-(t&1)*2)*20*button_sizex,20*button_sizey);
1006 swf_ShapeSetLine(i->tag,s,0,-40*button_sizey);
1007 swf_ShapeSetEnd(i->tag); // finish drawing
1008 swf_ShapeFree(s); // clean shape structure (which isn't needed anymore after writing the tag)
1010 ActionTAG*a1=0,*a2=0,*a3=0;
1011 a1 = action_NextFrame(a1);
1012 a1 = action_Stop(a1);
1013 a1 = action_End(a1);
1015 a2 = action_PreviousFrame(a2);
1016 a2 = action_Stop(a2);
1017 a2 = action_End(a2);
1019 a3 = action_Stop(a3);
1020 a3 = action_End(a3);
1022 i->tag = swf_InsertTag(i->tag, ST_DOACTION);
1023 swf_ActionSet(i->tag,a3);
1025 i->tag = swf_InsertTag(i->tag,ST_DEFINEBUTTON);
1026 int buttonid1 = getNewID(dev);
1027 swf_SetU16(i->tag, buttonid1);
1028 swf_ButtonSetRecord(i->tag,BS_UP|BS_HIT,ids[0],1,NULL,NULL);
1029 swf_ButtonSetRecord(i->tag,BS_OVER,ids[2],1,NULL,NULL);
1030 swf_ButtonSetRecord(i->tag,BS_DOWN,ids[4],1,NULL,NULL);
1031 swf_SetU8(i->tag,0); // end of button records
1032 swf_ActionSet(i->tag,a1);
1034 i->tag = swf_InsertTag(i->tag,ST_DEFINEBUTTON);
1035 int buttonid2 = getNewID(dev);
1036 swf_SetU16(i->tag, buttonid2);
1037 swf_ButtonSetRecord(i->tag,BS_UP|BS_HIT,ids[1],1,NULL,NULL);
1038 swf_ButtonSetRecord(i->tag,BS_OVER,ids[3],1,NULL,NULL);
1039 swf_ButtonSetRecord(i->tag,BS_DOWN,ids[5],1,NULL,NULL);
1040 swf_SetU8(i->tag,0); // end of button records
1041 swf_ActionSet(i->tag,a2);
1043 i->tag = swf_InsertTag(i->tag,ST_PLACEOBJECT2);
1045 swf_GetMatrix(0, &m);
1046 m.tx = button_sizex*20+200;
1047 swf_ObjectPlace(i->tag, buttonid2, 65534,&m,0,0);
1048 i->tag = swf_InsertTag(i->tag,ST_PLACEOBJECT2);
1049 m.tx = button_sizex*20+200+200;
1050 swf_ObjectPlace(i->tag, buttonid1, 65535,&m,0,0);
1054 void swf_startframe(gfxdevice_t*dev, int width, int height)
1056 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1058 if(i->config_protect) {
1059 i->tag = swf_InsertTag(i->tag, ST_PROTECT);
1060 i->config_protect = 0;
1062 if(i->config_simpleviewer) {
1067 if(!i->firstpage && !i->pagefinished)
1070 msg("<verbose> Starting new SWF page of size %dx%d", width, height);
1072 swf_GetMatrix(0, &i->page_matrix);
1073 i->page_matrix.tx = 0;
1074 i->page_matrix.ty = 0;
1081 /* create a bbox structure with the page size. This is used
1082 for clipping shape and text bounding boxes. As we don't want to
1083 generate bounding boxes which extend beyond the movie size (in
1084 order to not confuse Flash), we clip everything against i->pagebbox */
1085 i->pagebbox.xmin = 0;
1086 i->pagebbox.ymin = 0;
1087 i->pagebbox.xmax = width*20;
1088 i->pagebbox.ymax = height*20;
1090 /* increase SWF's bounding box */
1091 swf_ExpandRect2(&i->swf->movieSize, &i->pagebbox);
1093 i->lastframeno = i->frameno;
1095 i->pagefinished = 0;
1099 void swf_endframe(gfxdevice_t*dev)
1101 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1103 if(!i->pagefinished)
1106 if( (i->swf->fileVersion <= 8) && (i->config_insertstoptag) ) {
1108 atag = action_Stop(atag);
1109 atag = action_End(atag);
1110 i->tag = swf_InsertTag(i->tag,ST_DOACTION);
1111 swf_ActionSet(i->tag,atag);
1113 i->tag = swf_InsertTag(i->tag,ST_SHOWFRAME);
1116 for(i->depth;i->depth>i->startdepth;i->depth--) {
1117 i->tag = swf_InsertTag(i->tag,ST_REMOVEOBJECT2);
1118 swf_SetU16(i->tag,i->depth);
1120 i->depth = i->startdepth;
1122 if(i->config_frameresets) {
1123 for(i->currentswfid;i->currentswfid>i->startids;i->currentswfid--) {
1124 i->tag = swf_InsertTag(i->tag,ST_FREECHARACTER);
1125 swf_SetU16(i->tag,i->currentswfid);
1127 i->currentswfid = i->startids;
1131 static void setBackground(gfxdevice_t*dev, int x1, int y1, int x2, int y2)
1133 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1135 rgb.a = rgb.r = rgb.g = rgb.b = 0xff;
1139 int shapeid = getNewID(dev);
1144 i->tag = swf_InsertTag(i->tag, ST_DEFINESHAPE);
1146 fs1 = swf_ShapeAddSolidFillStyle(s, &rgb);
1147 swf_SetU16(i->tag,shapeid);
1148 swf_SetRect(i->tag,&r);
1149 swf_SetShapeHeader(i->tag,s);
1150 swf_ShapeSetAll(i->tag,s,x1,y1,ls1,fs1,0);
1151 swf_ShapeSetLine(i->tag,s,(x2-x1),0);
1152 swf_ShapeSetLine(i->tag,s,0,(y2-y1));
1153 swf_ShapeSetLine(i->tag,s,(x1-x2),0);
1154 swf_ShapeSetLine(i->tag,s,0,(y1-y2));
1155 swf_ShapeSetEnd(i->tag);
1157 i->tag = swf_InsertTag(i->tag, ST_PLACEOBJECT2);
1158 swf_ObjectPlace(i->tag,shapeid,getNewDepth(dev),0,0,0);
1159 i->tag = swf_InsertTag(i->tag, ST_PLACEOBJECT2);
1160 swf_ObjectPlaceClip(i->tag,shapeid,getNewDepth(dev),0,0,0,65535);
1163 /* initialize the swf writer */
1164 void gfxdevice_swf_init(gfxdevice_t* dev)
1166 memset(dev, 0, sizeof(gfxdevice_t));
1170 dev->internal = init_internal_struct(); // set config to default values
1172 dev->startpage = swf_startframe;
1173 dev->endpage = swf_endframe;
1174 dev->finish = swf_finish;
1175 dev->fillbitmap = swf_fillbitmap;
1176 dev->setparameter = swf_setparameter;
1177 dev->stroke = swf_stroke;
1178 dev->startclip = swf_startclip;
1179 dev->endclip = swf_endclip;
1180 dev->fill = swf_fill;
1181 dev->fillbitmap = swf_fillbitmap;
1182 dev->fillgradient = swf_fillgradient;
1183 dev->addfont = swf_addfont;
1184 dev->drawchar = swf_drawchar;
1185 dev->drawlink = swf_drawlink;
1187 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1190 msg("<verbose> initializing swf output\n", i->max_x,i->max_y);
1194 i->swf = (SWF*)rfx_calloc(sizeof(SWF));
1195 i->swf->fileVersion = 0;
1196 i->swf->frameRate = 0x80;
1197 i->swf->movieSize.xmin = 0;
1198 i->swf->movieSize.ymin = 0;
1199 i->swf->movieSize.xmax = 0;
1200 i->swf->movieSize.ymax = 0;
1201 i->swf->fileAttributes = 9; // as3, local-with-network
1203 i->swf->firstTag = swf_InsertTag(NULL,ST_SETBACKGROUNDCOLOR);
1204 i->tag = i->swf->firstTag;
1206 rgb.a = rgb.r = rgb.g = rgb.b = 0xff;
1208 swf_SetRGB(i->tag,&rgb);
1210 i->startdepth = i->depth = 0;
1211 i->startids = i->currentswfid = 0;
1214 static void startshape(gfxdevice_t*dev)
1216 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1221 //if(i->chardatapos && i->chardata[i->chardatapos-1].color.a)
1224 i->tag = swf_InsertTag(i->tag,ST_DEFINESHAPE3);
1226 swf_ShapeNew(&i->shape);
1227 i->linestyleid = swf_ShapeAddLineStyle(i->shape,i->linewidth,&i->strokergb);
1228 i->fillstyleid = swf_ShapeAddSolidFillStyle(i->shape,&i->fillrgb);
1230 RGBA markcol = {0,i->mark[0],i->mark[1],i->mark[2]};
1231 swf_ShapeAddSolidFillStyle(i->shape,&markcol);
1234 i->shapeid = getNewID(dev);
1236 msg("<debug> Using shape id %d", i->shapeid);
1238 swf_SetU16(i->tag,i->shapeid); // ID
1240 i->bboxrectpos = i->tag->len;
1242 swf_SetRect(i->tag,&i->pagebbox);
1244 memset(&i->bboxrect, 0, sizeof(i->bboxrect));
1246 swf_SetShapeStyles(i->tag,i->shape);
1247 swf_ShapeCountBits(i->shape,NULL,NULL);
1248 swf_SetShapeBits(i->tag,i->shape);
1250 /* TODO: do we really need this? */
1251 //swf_ShapeSetAll(i->tag,i->shape,/*x*/0,/*y*/0,i->linestyleid,0,0);
1252 //swf_ShapeSetAll(i->tag,i->shape,/*x*/UNDEFINED_COORD,/*y*/UNDEFINED_COORD,i->linestyleid,0,0);
1253 i->swflastx=i->swflasty=UNDEFINED_COORD;
1254 i->lastwasfill = -1;
1255 i->shapeisempty = 1;
1258 static void starttext(gfxdevice_t*dev)
1260 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1264 if(i->config_watermark) {
1265 insert_watermark(dev, 0);
1268 i->swflastx=i->swflasty=0;
1272 /* TODO: move to ../lib/rfxswf */
1273 void changeRect(gfxdevice_t*dev, TAG*tag, int pos, SRECT*newrect)
1275 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1276 /* determine length of old rect */
1280 swf_GetRect(tag, &old);
1281 swf_ResetReadBits(tag);
1282 int pos_end = tag->pos;
1284 int len = tag->len - pos_end;
1285 U8*data = (U8*)malloc(len);
1286 memcpy(data, &tag->data[pos_end], len);
1289 swf_SetRect(tag, newrect);
1290 swf_SetBlock(tag, data, len);
1292 tag->pos = tag->readBit = 0;
1295 void cancelshape(gfxdevice_t*dev)
1297 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1298 /* delete old shape tag */
1300 i->tag = i->tag->prev;
1301 swf_DeleteTag(0, todel);
1302 if(i->shape) {swf_ShapeFree(i->shape);i->shape=0;}
1304 i->bboxrectpos = -1;
1306 // i->currentswfid--; // doesn't work, for some reason
1309 void fixAreas(gfxdevice_t*dev)
1311 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1312 if(!i->shapeisempty && i->fill &&
1313 (i->bboxrect.xmin == i->bboxrect.xmax ||
1314 i->bboxrect.ymin == i->bboxrect.ymax) &&
1315 i->config_minlinewidth >= 0.001
1317 msg("<debug> Shape has size 0: width=%.2f height=%.2f",
1318 (i->bboxrect.xmax-i->bboxrect.xmin)/20.0,
1319 (i->bboxrect.ymax-i->bboxrect.ymin)/20.0
1322 SRECT r = i->bboxrect;
1324 if(r.xmin == r.xmax && r.ymin == r.ymax) {
1325 /* this thing comes down to a single dot- nothing to fix here */
1331 RGBA save_col = i->strokergb;
1332 int save_width = i->linewidth;
1334 i->strokergb = i->fillrgb;
1335 i->linewidth = (int)(i->config_minlinewidth*20);
1336 if(i->linewidth==0) i->linewidth = 1;
1341 moveto(dev, i->tag, r.xmin/20.0,r.ymin/20.0);
1342 lineto(dev, i->tag, r.xmax/20.0,r.ymax/20.0);
1344 i->strokergb = save_col;
1345 i->linewidth = save_width;
1350 static void endshape_noput(gfxdevice_t*dev)
1352 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1355 //changeRect(dev, i->tag, i->bboxrectpos, &i->bboxrect);
1358 swf_ShapeFree(i->shape);
1366 static void endshape(gfxdevice_t*dev)
1368 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1374 if(i->shapeisempty ||
1376 (i->bboxrect.xmin == i->bboxrect.xmax &&
1377 i->bboxrect.ymin == i->bboxrect.ymax))
1379 // delete the shape again, we didn't do anything
1380 msg("<debug> cancelling shape: bbox is (%f,%f,%f,%f)",
1381 i->bboxrect.xmin /20.0,
1382 i->bboxrect.ymin /20.0,
1383 i->bboxrect.xmax /20.0,
1384 i->bboxrect.ymax /20.0
1390 swf_ShapeSetEnd(i->tag);
1392 SRECT r = swf_ClipRect(i->pagebbox, i->bboxrect);
1393 changeRect(dev, i->tag, i->bboxrectpos, &r);
1395 msg("<trace> Placing shape ID %d", i->shapeid);
1397 i->tag = swf_InsertTag(i->tag,ST_PLACEOBJECT2);
1398 MATRIX m = i->page_matrix;
1399 m.tx += i->shapeposx;
1400 m.ty += i->shapeposy;
1401 swf_ObjectPlace(i->tag,i->shapeid,getNewDepth(dev),&m,NULL,NULL);
1403 if(i->config_animate) {
1404 i->tag = swf_InsertTag(i->tag,ST_SHOWFRAME);
1407 swf_ShapeFree(i->shape);
1410 i->bboxrectpos = -1;
1417 void wipeSWF(SWF*swf)
1419 TAG*tag = swf->firstTag;
1421 TAG*next = tag->next;
1422 if(tag->id != ST_SETBACKGROUNDCOLOR &&
1423 tag->id != ST_END &&
1424 tag->id != ST_DOACTION &&
1425 tag->id != ST_SHOWFRAME) {
1426 swf_DeleteTag(swf, tag);
1432 void swfoutput_finalize(gfxdevice_t*dev)
1434 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1436 if(i->tag && i->tag->id == ST_END)
1437 return; //already done
1439 i->swf->fileVersion = i->config_flashversion;
1440 i->swf->frameRate = i->config_framerate*0x100;
1442 if(i->config_bboxvars) {
1443 TAG* tag = swf_InsertTag(i->swf->firstTag, ST_DOACTION);
1445 a = action_PushString(a, "xmin");
1446 a = action_PushFloat(a, i->swf->movieSize.xmin / 20.0);
1447 a = action_SetVariable(a);
1448 a = action_PushString(a, "ymin");
1449 a = action_PushFloat(a, i->swf->movieSize.ymin / 20.0);
1450 a = action_SetVariable(a);
1451 a = action_PushString(a, "xmax");
1452 a = action_PushFloat(a, i->swf->movieSize.xmax / 20.0);
1453 a = action_SetVariable(a);
1454 a = action_PushString(a, "ymax");
1455 a = action_PushFloat(a, i->swf->movieSize.ymax / 20.0);
1456 a = action_SetVariable(a);
1457 a = action_PushString(a, "width");
1458 a = action_PushFloat(a, (i->swf->movieSize.xmax - i->swf->movieSize.xmin) / 20.0);
1459 a = action_SetVariable(a);
1460 a = action_PushString(a, "height");
1461 a = action_PushFloat(a, (i->swf->movieSize.ymax - i->swf->movieSize.ymin) / 20.0);
1462 a = action_SetVariable(a);
1464 swf_ActionSet(tag, a);
1469 free(i->mark);i->mark = 0;
1473 fontlist_t *iterator = i->fontlist;
1474 char use_font3 = i->config_flashversion>=8 && !NO_FONT3;
1477 TAG*mtag = i->swf->firstTag;
1478 if(iterator->swffont) {
1479 if(!i->config_storeallcharacters) {
1480 msg("<debug> Reducing font %s", iterator->swffont->name);
1481 swf_FontReduce(iterator->swffont);
1483 int used = iterator->swffont->use && iterator->swffont->use->used_glyphs;
1486 mtag = swf_InsertTag(mtag, ST_DEFINEFONT2);
1487 swf_FontSetDefine2(mtag, iterator->swffont);
1489 mtag = swf_InsertTag(mtag, ST_DEFINEFONT3);
1490 swf_FontSetDefine2(mtag, iterator->swffont);
1495 iterator = iterator->next;
1498 i->tag = swf_InsertTag(i->tag,ST_END);
1499 TAG* tag = i->tag->prev;
1501 if(use_font3 && i->config_storeallcharacters && i->config_alignfonts) {
1502 swf_FontPostprocess(i->swf); // generate alignment information
1505 /* remove the removeobject2 tags between the last ST_SHOWFRAME
1506 and the ST_END- they confuse the flash player */
1507 while(tag->id == ST_REMOVEOBJECT2) {
1508 TAG* prev = tag->prev;
1509 swf_DeleteTag(i->swf, tag);
1516 if(i->config_enablezlib || i->config_flashversion>=6) {
1517 i->swf->compressed = 1;
1520 /* Add AVM2 actionscript */
1521 if(i->config_flashversion>=9 &&
1522 (i->config_insertstoptag || i->hasbuttons) && !i->config_linknameurl) {
1523 swf_AddButtonLinks(i->swf, i->config_insertstoptag,
1524 i->config_internallinkfunction||i->config_externallinkfunction);
1526 // if(i->config_reordertags)
1527 // swf_Optimize(i->swf);
1530 int swfresult_save(gfxresult_t*gfx, const char*filename)
1532 SWF*swf = (SWF*)gfx->internal;
1535 fi = open(filename, O_BINARY|O_CREAT|O_TRUNC|O_WRONLY, 0777);
1540 msg("<fatal> Could not create \"%s\". ", FIXNULL(filename));
1544 if FAILED(swf_WriteSWF(fi,swf))
1545 msg("<error> WriteSWF() failed.\n");
1551 void* swfresult_get(gfxresult_t*gfx, const char*name)
1553 SWF*swf = (SWF*)gfx->internal;
1554 if(!strcmp(name, "swf")) {
1555 return (void*)swf_CopySWF(swf);
1556 } else if(!strcmp(name, "xmin")) {
1557 return (void*)(ptroff_t)(swf->movieSize.xmin/20);
1558 } else if(!strcmp(name, "ymin")) {
1559 return (void*)(ptroff_t)(swf->movieSize.ymin/20);
1560 } else if(!strcmp(name, "xmax")) {
1561 return (void*)(ptroff_t)(swf->movieSize.xmax/20);
1562 } else if(!strcmp(name, "ymax")) {
1563 return (void*)(ptroff_t)(swf->movieSize.ymax/20);
1564 } else if(!strcmp(name, "width")) {
1565 return (void*)(ptroff_t)((swf->movieSize.xmax - swf->movieSize.xmin)/20);
1566 } else if(!strcmp(name, "height")) {
1567 return (void*)(ptroff_t)((swf->movieSize.ymax - swf->movieSize.ymin)/20);
1571 void swfresult_destroy(gfxresult_t*gfx)
1574 swf_FreeTags((SWF*)gfx->internal);
1575 free(gfx->internal);
1578 memset(gfx, 0, sizeof(gfxresult_t));
1582 static void swfoutput_destroy(gfxdevice_t* dev);
1584 gfxresult_t* swf_finish(gfxdevice_t* dev)
1586 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1589 if(i->config_linktarget) {
1590 free(i->config_linktarget);
1591 i->config_linktarget = 0;
1594 swfoutput_finalize(dev);
1595 SWF* swf = i->swf;i->swf = 0;
1596 swfoutput_destroy(dev);
1598 result = (gfxresult_t*)rfx_calloc(sizeof(gfxresult_t));
1599 result->internal = swf;
1600 result->save = swfresult_save;
1602 result->get = swfresult_get;
1603 result->destroy = swfresult_destroy;
1607 /* Perform cleaning up */
1608 static void swfoutput_destroy(gfxdevice_t* dev)
1610 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1612 /* not initialized yet- nothing to destroy */
1616 fontlist_t *tmp,*iterator = i->fontlist;
1618 if(iterator->swffont) {
1619 swf_FontFree(iterator->swffont);iterator->swffont=0;
1622 iterator = iterator->next;
1625 if(i->swf) {swf_FreeTags(i->swf);free(i->swf);i->swf = 0;}
1628 memset(dev, 0, sizeof(gfxdevice_t));
1631 static void swfoutput_setstrokecolor(gfxdevice_t* dev, U8 r, U8 g, U8 b, U8 a)
1633 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1634 if(i->strokergb.r == r &&
1635 i->strokergb.g == g &&
1636 i->strokergb.b == b &&
1637 i->strokergb.a == a) return;
1647 //#define ROUND_UP 19
1648 //#define ROUND_UP 10
1650 static void swfoutput_setlinewidth(gfxdevice_t*dev, double _linewidth)
1652 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1653 if(i->linewidth == (U16)(_linewidth*20+19.0/20.0))
1657 i->linewidth = (U16)(_linewidth*20+19.0/20.0);
1661 static void drawlink(gfxdevice_t*dev, ActionTAG*,ActionTAG*, gfxline_t*points, char mouseover, char*type, const char*url);
1662 static void swfoutput_namedlink(gfxdevice_t*dev, char*name, gfxline_t*points);
1663 static void swfoutput_linktopage(gfxdevice_t*dev, int page, gfxline_t*points);
1664 static void swfoutput_linktourl(gfxdevice_t*dev, const char*url, gfxline_t*points);
1666 /*void swfoutput_drawlink(gfxdevice_t*dev, char*url, gfxline_t*points)
1668 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1669 dev->drawlink(dev, points, url);
1672 void swf_drawlink(gfxdevice_t*dev, gfxline_t*points, const char*url)
1674 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1676 if(i->config_disablelinks)
1679 if(!strncmp("http://pdf2swf:", url, 15)) {
1680 char*tmp = strdup(url);
1681 int l = strlen(tmp);
1684 swfoutput_namedlink(dev, tmp+15, points);
1687 } else if(!strncmp("page", url, 4)) {
1690 if(url[t]<'0' || url[t]>'9')
1693 int page = atoi(&url[4]);
1694 if(page<0) page = 0;
1695 swfoutput_linktopage(dev, page, points);
1698 swfoutput_linktourl(dev, url, points);
1701 void swfoutput_linktourl(gfxdevice_t*dev, const char*url, gfxline_t*points)
1703 ActionTAG* actions = 0;
1704 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1710 /* TODO: escape special characters in url */
1712 if(i->config_externallinkfunction && i->config_flashversion<=8) {
1713 actions = action_PushString(actions, url); //parameter
1714 actions = action_PushInt(actions, 1); //number of parameters (1)
1715 actions = action_PushString(actions, i->config_externallinkfunction); //function name
1716 actions = action_CallFunction(actions);
1717 } else if(!i->config_linktarget) {
1718 if(!i->config_opennewwindow)
1719 actions = action_GetUrl(actions, url, "_parent");
1721 actions = action_GetUrl(actions, url, "_this");
1723 actions = action_GetUrl(actions, url, i->config_linktarget);
1725 actions = action_End(actions);
1727 drawlink(dev, actions, 0, points, 0, "url", url);
1729 swf_ActionFree(actions);
1731 void swfoutput_linktopage(gfxdevice_t*dev, int page, gfxline_t*points)
1733 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1734 ActionTAG* actions = 0;
1741 if(!i->config_internallinkfunction || i->config_flashversion>=9) {
1742 actions = action_GotoFrame(actions, page-1);
1743 actions = action_End(actions);
1745 actions = action_PushInt(actions, page); //parameter
1746 actions = action_PushInt(actions, 1); //number of parameters (1)
1747 actions = action_PushString(actions, i->config_internallinkfunction); //function name
1748 actions = action_CallFunction(actions);
1749 actions = action_End(actions);
1753 sprintf(name, "page%d", page);
1755 drawlink(dev, actions, 0, points, 0, "page", name);
1757 swf_ActionFree(actions);
1760 /* Named Links (a.k.a. Acrobatmenu) are used to implement various gadgets
1761 of the viewer objects, like subtitles, index elements etc.
1763 void swfoutput_namedlink(gfxdevice_t*dev, char*name, gfxline_t*points)
1765 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1766 ActionTAG *actions1,*actions2;
1767 char*tmp = strdup(name);
1776 if(!strncmp(tmp, "call:", 5))
1778 char*x = strchr(&tmp[5], ':');
1780 actions1 = action_PushInt(0, 0); //number of parameters (0)
1781 actions1 = action_PushString(actions1, &tmp[5]); //function name
1782 actions1 = action_CallFunction(actions1);
1783 actions1 = action_End(actions1);
1786 actions1 = action_PushString(0, x+1); //parameter
1787 actions1 = action_PushInt(actions1, 1); //number of parameters (1)
1788 actions1 = action_PushString(actions1, &tmp[5]); //function name
1789 actions1 = action_CallFunction(actions1);
1790 actions1 = action_End(actions1);
1792 actions2 = action_End(0);
1798 actions1 = action_PushString(0, "/:subtitle");
1799 actions1 = action_PushString(actions1, name);
1800 actions1 = action_SetVariable(actions1);
1801 actions1 = action_End(actions1);
1803 actions2 = action_PushString(0, "/:subtitle");
1804 actions2 = action_PushString(actions2, "");
1805 actions2 = action_SetVariable(actions2);
1806 actions2 = action_End(actions2);
1810 drawlink(dev, actions1, actions2, points, mouseover, type, name);
1812 swf_ActionFree(actions1);
1813 swf_ActionFree(actions2);
1817 static void drawgfxline(gfxdevice_t*dev, gfxline_t*line, int fill)
1819 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1820 gfxcoord_t lastx=0,lasty=0,px=0,py=0;
1822 int lines= 0, splines=0;
1829 /* check whether the next segment is zero */
1830 if(line->type == gfx_moveTo) {
1831 moveto(dev, i->tag, line->x, line->y);
1832 px = lastx = line->x;
1833 py = lasty = line->y;
1835 } if(line->type == gfx_lineTo) {
1836 lineto(dev, i->tag, line->x, line->y);
1841 } else if(line->type == gfx_splineTo) {
1843 s.x = line->sx;p.x = line->x;
1844 s.y = line->sy;p.y = line->y;
1845 splineto(dev, i->tag, s, p);
1853 msg("<trace> drawgfxline, %d lines, %d splines", lines, splines);
1857 static void drawlink(gfxdevice_t*dev, ActionTAG*actions1, ActionTAG*actions2, gfxline_t*points, char mouseover, char*type, const char*url)
1859 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1868 int buttonid = getNewID(dev);
1869 gfxbbox_t bbox = gfxline_getbbox(points);
1871 if(i->config_linknameurl) {
1879 myshapeid = getNewID(dev);
1880 i->tag = swf_InsertTag(i->tag,ST_DEFINESHAPE3);
1881 swf_ShapeNew(&i->shape);
1882 rgb.r = rgb.b = rgb.a = rgb.g = 0;
1883 fsid = swf_ShapeAddSolidFillStyle(i->shape,&rgb);
1884 swf_SetU16(i->tag, myshapeid);
1885 r.xmin = (int)(bbox.xmin*20);
1886 r.ymin = (int)(bbox.ymin*20);
1887 r.xmax = (int)(bbox.xmax*20);
1888 r.ymax = (int)(bbox.ymax*20);
1889 r = swf_ClipRect(i->pagebbox, r);
1890 swf_SetRect(i->tag,&r);
1891 swf_SetShapeStyles(i->tag,i->shape);
1892 swf_ShapeCountBits(i->shape,NULL,NULL);
1893 swf_SetShapeBits(i->tag,i->shape);
1894 swf_ShapeSetAll(i->tag,i->shape,/*x*/0,/*y*/0,0,fsid,0);
1895 i->swflastx = i->swflasty = 0;
1896 drawgfxline(dev, points, 1);
1897 swf_ShapeSetEnd(i->tag);
1898 swf_ShapeFree(i->shape);
1901 myshapeid2 = getNewID(dev);
1902 i->tag = swf_InsertTag(i->tag,ST_DEFINESHAPE3);
1903 swf_ShapeNew(&i->shape);
1905 rgb = i->config_linkcolor;
1907 fsid = swf_ShapeAddSolidFillStyle(i->shape,&rgb);
1908 swf_SetU16(i->tag, myshapeid2);
1909 r.xmin = (int)(bbox.xmin*20);
1910 r.ymin = (int)(bbox.ymin*20);
1911 r.xmax = (int)(bbox.xmax*20);
1912 r.ymax = (int)(bbox.ymax*20);
1913 r = swf_ClipRect(i->pagebbox, r);
1914 swf_SetRect(i->tag,&r);
1915 swf_SetShapeStyles(i->tag,i->shape);
1916 swf_ShapeCountBits(i->shape,NULL,NULL);
1917 swf_SetShapeBits(i->tag,i->shape);
1918 swf_ShapeSetAll(i->tag,i->shape,/*x*/0,/*y*/0,0,fsid,0);
1919 i->swflastx = i->swflasty = 0;
1920 drawgfxline(dev, points, 1);
1921 swf_ShapeSetEnd(i->tag);
1922 swf_ShapeFree(i->shape);
1926 i->tag = swf_InsertTag(i->tag,ST_DEFINEBUTTON);
1927 swf_SetU16(i->tag,buttonid); //id
1928 swf_ButtonSetFlags(i->tag, 0); //menu=no
1929 swf_ButtonSetRecord(i->tag,0x01,myshapeid,i->depth,0,0);
1930 swf_ButtonSetRecord(i->tag,0x02,myshapeid2,i->depth,0,0);
1931 swf_ButtonSetRecord(i->tag,0x04,myshapeid2,i->depth,0,0);
1932 swf_ButtonSetRecord(i->tag,0x08,myshapeid,i->depth,0,0);
1933 swf_SetU8(i->tag,0);
1934 swf_ActionSet(i->tag,actions1);
1935 swf_SetU8(i->tag,0);
1939 i->tag = swf_InsertTag(i->tag,ST_DEFINEBUTTON2);
1940 swf_SetU16(i->tag,buttonid); //id
1941 swf_ButtonSetFlags(i->tag, 0); //menu=no
1942 swf_ButtonSetRecord(i->tag,0x01,myshapeid,i->depth,0,0);
1943 swf_ButtonSetRecord(i->tag,0x02,myshapeid2,i->depth,0,0);
1944 swf_ButtonSetRecord(i->tag,0x04,myshapeid2,i->depth,0,0);
1945 swf_ButtonSetRecord(i->tag,0x08,myshapeid,i->depth,0,0);
1946 swf_SetU8(i->tag,0); // end of button records
1947 swf_ButtonSetCondition(i->tag, BC_IDLE_OVERUP);
1948 swf_ActionSet(i->tag,actions1);
1950 swf_ButtonSetCondition(i->tag, BC_OVERUP_IDLE);
1951 swf_ActionSet(i->tag,actions2);
1952 swf_SetU8(i->tag,0);
1953 swf_ButtonPostProcess(i->tag, 2);
1955 swf_SetU8(i->tag,0);
1956 swf_ButtonPostProcess(i->tag, 1);
1962 const char* name = 0;
1963 if(i->config_linknameurl) {
1964 buf2 = malloc(strlen(type)+strlen(url)+2);
1965 sprintf(buf2, "%s:%s", type, url);
1969 sprintf(buf, "button%d", buttonid);
1972 msg("<trace> Placing link ID %d", buttonid);
1973 i->tag = swf_InsertTag(i->tag,ST_PLACEOBJECT2);
1975 if(posx!=0 || posy!=0) {
1977 p.x = (int)(posx*20);
1978 p.y = (int)(posy*20);
1979 p = swf_TurnPoint(p, &i->page_matrix);
1984 swf_ObjectPlace(i->tag, buttonid, getNewDepth(dev),&m,0,(U8*)name);
1986 swf_ObjectPlace(i->tag, buttonid, getNewDepth(dev),&i->page_matrix,0,(U8*)name);
1996 for(t=0;t<picpos;t++)
1998 if(pic_xids[t] == xid &&
1999 pic_yids[t] == yid) {
2000 width = pic_width[t];
2001 height = pic_height[t];
2005 pic_ids[picpos] = swfoutput_drawimagelosslessN(&output, pic, pal, width, height, x1,y1,x2,y2,x3,y3,x4,y4, numpalette);
2006 pic_xids[picpos] = xid;
2007 pic_yids[picpos] = yid;
2008 pic_width[picpos] = width;
2009 pic_height[picpos] = height;
2012 pic[width*y+x] = buf[0];
2016 xid += pal[1].r*3 + pal[1].g*11 + pal[1].b*17;
2017 yid += pal[1].r*7 + pal[1].g*5 + pal[1].b*23;
2021 xid += x*r+x*b*3+x*g*7+x*a*11;
2022 yid += y*r*3+y*b*17+y*g*19+y*a*11;
2024 for(t=0;t<picpos;t++)
2026 if(pic_xids[t] == xid &&
2027 pic_yids[t] == yid) {
2036 int swf_setparameter(gfxdevice_t*dev, const char*name, const char*value)
2038 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
2040 msg("<trace> swfdevice: %s=%s", name, value);
2041 if(!strcmp(name, "jpegsubpixels")) {
2042 i->config_jpegsubpixels = atof(value);
2043 } else if(!strcmp(name, "ppmsubpixels")) {
2044 i->config_ppmsubpixels = atof(value);
2045 } else if(!strcmp(name, "subpixels")) {
2046 i->config_ppmsubpixels = i->config_jpegsubpixels = atof(value);
2047 } else if(!strcmp(name, "drawonlyshapes")) {
2048 i->config_drawonlyshapes = atoi(value);
2049 } else if(!strcmp(name, "ignoredraworder")) {
2050 i->config_ignoredraworder = atoi(value);
2051 } else if(!strcmp(name, "mark")) {
2052 if(!value || !value[0]) {
2053 if(i->mark) free(i->mark);
2057 i->mark = strdup("...");
2058 for(t=0;t<3;t++) if(value[t]) i->mark[t] = value[t];
2060 } else if(!strcmp(name, "filloverlap")) {
2061 i->config_filloverlap = atoi(value);
2062 } else if(!strcmp(name, "linksopennewwindow")) {
2063 i->config_opennewwindow = atoi(value);
2064 } else if(!strcmp(name, "opennewwindow")) {
2065 i->config_opennewwindow = atoi(value);
2066 } else if(!strcmp(name, "storeallcharacters")) {
2067 i->config_storeallcharacters = atoi(value);
2068 } else if(!strcmp(name, "enablezlib")) {
2069 i->config_enablezlib = atoi(value);
2070 } else if(!strcmp(name, "bboxvars")) {
2071 i->config_bboxvars = atoi(value);
2072 } else if(!strcmp(name, "dots")) {
2073 i->config_dots = atoi(value);
2074 } else if(!strcmp(name, "frameresets")) {
2075 i->config_frameresets = atoi(value);
2076 } else if(!strcmp(name, "showclipshapes")) {
2077 i->config_showclipshapes = atoi(value);
2078 } else if(!strcmp(name, "reordertags")) {
2079 i->config_reordertags = atoi(value);
2080 } else if(!strcmp(name, "internallinkfunction")) {
2081 i->config_internallinkfunction = strdup(value);
2082 } else if(!strcmp(name, "externallinkfunction")) {
2083 i->config_externallinkfunction = strdup(value);
2084 } else if(!strcmp(name, "linkfunction")) { //sets both internallinkfunction and externallinkfunction
2085 i->config_internallinkfunction = strdup(value);
2086 i->config_externallinkfunction = strdup(value);
2087 } else if(!strcmp(name, "disable_polygon_conversion")) {
2088 i->config_disable_polygon_conversion = atoi(value);
2089 } else if(!strcmp(name, "normalize_polygon_positions")) {
2090 i->config_normalize_polygon_positions = atoi(value);
2091 } else if(!strcmp(name, "wxwindowparams")) {
2092 i->config_watermark = atoi(value);
2093 } else if(!strcmp(name, "insertstop")) {
2094 i->config_insertstoptag = atoi(value);
2095 } else if(!strcmp(name, "protect")) {
2096 i->config_protect = atoi(value);
2097 if(i->config_protect && i->tag) {
2098 i->tag = swf_InsertTag(i->tag, ST_PROTECT);
2100 } else if(!strcmp(name, "flashversion")) {
2101 i->config_flashversion = atoi(value);
2103 i->swf->fileVersion = i->config_flashversion;
2105 } else if(!strcmp(name, "framerate")) {
2106 i->config_framerate = atof(value);
2108 i->swf->frameRate = i->config_framerate*0x100;
2110 } else if(!strcmp(name, "minlinewidth")) {
2111 i->config_minlinewidth = atof(value);
2112 } else if(!strcmp(name, "caplinewidth")) {
2113 i->config_caplinewidth = atof(value);
2114 } else if(!strcmp(name, "linktarget")) {
2115 i->config_linktarget = strdup(value);
2116 } else if(!strcmp(name, "invisibletexttofront")) {
2117 i->config_invisibletexttofront = atoi(value);
2118 } else if(!strcmp(name, "noclips")) {
2119 i->config_noclips = atoi(value);
2120 } else if(!strcmp(name, "dumpfonts")) {
2121 i->config_dumpfonts = atoi(value);
2122 } else if(!strcmp(name, "animate")) {
2123 i->config_animate = atoi(value);
2124 } else if(!strcmp(name, "linknameurl")) {
2125 i->config_linknameurl = atoi(value);
2126 } else if(!strcmp(name, "showimages")) {
2127 i->config_showimages = atoi(value);
2128 } else if(!strcmp(name, "disablelinks")) {
2129 i->config_disablelinks = atoi(value);
2130 } else if(!strcmp(name, "simpleviewer")) {
2131 i->config_simpleviewer = atoi(value);
2132 } else if(!strcmp(name, "next_bitmap_is_jpeg")) {
2134 } else if(!strcmp(name, "jpegquality")) {
2135 int val = atoi(value);
2137 if(val>101) val=101;
2138 i->config_jpegquality = val;
2139 } else if(!strcmp(name, "splinequality")) {
2140 int v = atoi(value);
2141 v = 500-(v*5); // 100% = 0.25 pixel, 0% = 25 pixel
2143 i->config_splinemaxerror = v;
2144 } else if(!strcmp(name, "fontquality")) {
2145 int v = atoi(value);
2146 v = 500-(v*5); // 100% = 0.25 pixel, 0% = 25 pixel
2148 i->config_fontsplinemaxerror = v;
2149 } else if(!strcmp(name, "linkcolor")) {
2150 if(strlen(value)!=8) {
2151 fprintf(stderr, "Unknown format for option 'linkcolor'. (%s <-> RRGGBBAA)\n", value);
2154 # define NIBBLE(s) (((s)>='0' && (s)<='9')?((s)-'0'):((s)&0x0f)+9)
2155 i->config_linkcolor.r = NIBBLE(value[0])<<4 | NIBBLE(value[1]);
2156 i->config_linkcolor.g = NIBBLE(value[2])<<4 | NIBBLE(value[3]);
2157 i->config_linkcolor.b = NIBBLE(value[4])<<4 | NIBBLE(value[5]);
2158 i->config_linkcolor.a = NIBBLE(value[6])<<4 | NIBBLE(value[7]);
2159 } else if(!strcmp(name, "help")) {
2160 printf("\nSWF layer options:\n");
2161 printf("jpegsubpixels=<pixels> resolution adjustment for jpeg images (same as jpegdpi, but in pixels)\n");
2162 printf("ppmsubpixels=<pixels resolution adjustment for lossless images (same as ppmdpi, but in pixels)\n");
2163 printf("subpixels=<pixels> shortcut for setting both jpegsubpixels and ppmsubpixels\n");
2164 printf("drawonlyshapes convert everything to shapes (currently broken)\n");
2165 printf("ignoredraworder allow to perform a few optimizations for creating smaller SWFs\n");
2166 printf("linksopennewwindow make links open a new browser window\n");
2167 printf("linktarget target window name of new links\n");
2168 printf("linkcolor=<color) color of links (format: RRGGBBAA)\n");
2169 printf("linknameurl Link buttons will be named like the URL they refer to (handy for iterating through links with actionscript)\n");
2170 printf("storeallcharacters don't reduce the fonts to used characters in the output file\n");
2171 printf("enablezlib switch on zlib compression (also done if flashversion>=6)\n");
2172 printf("bboxvars store the bounding box of the SWF file in actionscript variables\n");
2173 printf("dots Take care to handle dots correctly\n");
2174 printf("reordertags=0/1 (default: 1) perform some tag optimizations\n");
2175 printf("internallinkfunction=<name> when the user clicks a internal link (to a different page) in the converted file, this actionscript function is called\n");
2176 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");
2177 printf("disable_polygon_conversion never convert strokes to polygons (will remove capstyles and joint styles)\n");
2178 printf("caplinewidth=<width> the minimum thichness a line needs to have so that capstyles become visible (and are converted)\n");
2179 printf("insertstop put an ActionScript \"STOP\" tag in every frame\n");
2180 printf("protect add a \"protect\" tag to the file, to prevent loading in the Flash editor\n");
2181 printf("flashversion=<version> the SWF fileversion (6)\n");
2182 printf("framerate=<fps> SWF framerate\n");
2183 printf("minlinewidth=<width> convert horizontal/vertical boxes smaller than this width to lines (0.05) \n");
2184 printf("simpleviewer Add next/previous buttons to the SWF\n");
2185 printf("animate insert a showframe tag after each placeobject (animate draw order of PDF files)\n");
2186 printf("jpegquality=<quality> set compression quality of jpeg images\n");
2187 printf("splinequality=<value> Set the quality of spline convertion to value (0-100, default: 100).\n");
2188 printf("disablelinks Disable links.\n");
2195 // --------------------------------------------------------------------
2197 static CXFORM gfxcxform_to_cxform(gfxcxform_t* c)
2200 swf_GetCXForm(0, &cx, 1);
2203 if(c->rg!=0 || c->rb!=0 || c->ra!=0 ||
2204 c->gr!=0 || c->gb!=0 || c->ga!=0 ||
2205 c->br!=0 || c->bg!=0 || c->ba!=0 ||
2206 c->ar!=0 || c->ag!=0 || c->ab!=0)
2207 msg("<warning> CXForm not SWF-compatible");
2209 cx.a0 = (S16)(c->aa*256);
2210 cx.r0 = (S16)(c->rr*256);
2211 cx.g0 = (S16)(c->gg*256);
2212 cx.b0 = (S16)(c->bb*256);
2221 static int imageInCache(gfxdevice_t*dev, void*data, int width, int height)
2225 static void addImageToCache(gfxdevice_t*dev, void*data, int width, int height)
2229 static int add_image(swfoutput_internal*i, gfximage_t*img, int targetwidth, int targetheight, int* newwidth, int* newheight)
2231 gfxdevice_t*dev = i->dev;
2233 RGBA*mem = (RGBA*)img->data;
2235 int sizex = img->width;
2236 int sizey = img->height;
2237 int is_jpeg = i->jpeg;
2240 int newsizex=sizex, newsizey=sizey;
2243 if(is_jpeg && i->config_jpegsubpixels) {
2244 newsizex = (int)(targetwidth*i->config_jpegsubpixels + 0.5);
2245 newsizey = (int)(targetheight*i->config_jpegsubpixels + 0.5);
2246 } else if(!is_jpeg && i->config_ppmsubpixels) {
2247 newsizex = (int)(targetwidth*i->config_ppmsubpixels + 0.5);
2248 newsizey = (int)(targetheight*i->config_ppmsubpixels + 0.5);
2252 if(sizex<=0 || sizey<=0)
2259 /* TODO: cache images */
2261 if(newsizex<sizex || newsizey<sizey) {
2262 msg("<verbose> Scaling %dx%d image to %dx%d", sizex, sizey, newsizex, newsizey);
2263 gfximage_t*ni = gfximage_rescale(img, newsizex, newsizey);
2264 newpic = (RGBA*)ni->data;
2266 *newwidth = sizex = newsizex;
2267 *newheight = sizey = newsizey;
2270 *newwidth = newsizex = sizex;
2271 *newheight = newsizey = sizey;
2274 int num_colors = swf_ImageGetNumberOfPaletteEntries(mem,sizex,sizey,0);
2275 int has_alpha = swf_ImageHasAlpha(mem,sizex,sizey);
2277 msg("<verbose> Drawing %dx%d %s%simage (id %d) at size %dx%d (%dx%d), %s%d colors",
2279 has_alpha?(has_alpha==2?"semi-transparent ":"transparent "):"",
2280 is_jpeg?"jpeg-":"", i->currentswfid+1,
2282 targetwidth, targetheight,
2283 /*newsizex, newsizey,*/
2284 num_colors>256?">":"", num_colors>256?256:num_colors);
2286 /*RGBA* pal = (RGBA*)rfx_alloc(sizeof(RGBA)*num_colors);
2287 swf_ImageGetNumberOfPaletteEntries(mem,sizex,sizey,pal);
2289 for(t=0;t<num_colors;t++) {
2290 printf("%02x%02x%02x%02x ",
2291 pal[t].r, pal[t].g, pal[t].b, pal[t].a);
2298 int cacheid = imageInCache(dev, mem, sizex, sizey);
2301 bitid = getNewID(dev);
2303 i->tag = swf_AddImage(i->tag, bitid, mem, sizex, sizey, i->config_jpegquality);
2304 addImageToCache(dev, mem, sizex, sizey);
2314 static SRECT gfxline_getSWFbbox(gfxline_t*line)
2316 gfxbbox_t bbox = gfxline_getbbox(line);
2318 r.xmin = (int)(bbox.xmin*20);
2319 r.ymin = (int)(bbox.ymin*20);
2320 r.xmax = (int)(bbox.xmax*20);
2321 r.ymax = (int)(bbox.ymax*20);
2325 int line_is_empty(gfxline_t*line)
2328 if(line->type != gfx_moveTo)
2335 static void swf_fillbitmap(gfxdevice_t*dev, gfxline_t*line, gfximage_t*img, gfxmatrix_t*matrix, gfxcxform_t*cxform)
2337 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
2339 if(line_is_empty(line))
2345 int targetx = (int)(sqrt(matrix->m00*matrix->m00 + matrix->m01*matrix->m01)*img->width);
2346 int targety = (int)(sqrt(matrix->m10*matrix->m10 + matrix->m11*matrix->m11)*img->height);
2348 int newwidth=0,newheight=0;
2349 int bitid = add_image(i, img, targetx, targety, &newwidth, &newheight);
2352 double fx = (double)img->width / (double)newwidth;
2353 double fy = (double)img->height / (double)newheight;
2356 m.sx = (int)(65536*20*matrix->m00*fx); m.r1 = (int)(65536*20*matrix->m10*fy);
2357 m.r0 = (int)(65536*20*matrix->m01*fx); m.sy = (int)(65536*20*matrix->m11*fy);
2358 m.tx = (int)(matrix->tx*20);
2359 m.ty = (int)(matrix->ty*20);
2362 int myshapeid = getNewID(dev);
2363 i->tag = swf_InsertTag(i->tag,ST_DEFINESHAPE);
2365 swf_ShapeNew(&shape);
2366 int fsid = swf_ShapeAddBitmapFillStyle(shape,&m,bitid,1);
2368 if(i->config_showimages) {
2369 RGBA pink = {255,255,0,255};
2370 lsid = swf_ShapeAddLineStyle(shape, 20, &pink);
2372 swf_SetU16(i->tag, myshapeid);
2373 SRECT r = gfxline_getSWFbbox(line);
2374 r = swf_ClipRect(i->pagebbox, r);
2375 swf_SetRect(i->tag,&r);
2376 swf_SetShapeStyles(i->tag,shape);
2377 swf_ShapeCountBits(shape,NULL,NULL);
2378 swf_SetShapeBits(i->tag,shape);
2379 swf_ShapeSetAll(i->tag,shape,UNDEFINED_COORD,UNDEFINED_COORD,lsid,fsid,0);
2380 i->swflastx = i->swflasty = UNDEFINED_COORD;
2381 drawgfxline(dev, line, 1);
2382 swf_ShapeSetEnd(i->tag);
2383 swf_ShapeFree(shape);
2385 msg("<trace> Placing image, shape ID %d, bitmap ID %d", myshapeid, bitid);
2386 i->tag = swf_InsertTag(i->tag,ST_PLACEOBJECT2);
2387 CXFORM cxform2 = gfxcxform_to_cxform(cxform);
2388 swf_ObjectPlace(i->tag,myshapeid,getNewDepth(dev),&i->page_matrix,&cxform2,NULL);
2391 static RGBA col_black = {255,0,0,0};
2393 static void drawoutline(gfxdevice_t*dev, gfxline_t*line)
2395 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
2397 int myshapeid = getNewID(dev);
2398 i->tag = swf_InsertTag(i->tag,ST_DEFINESHAPE3);
2401 swf_ShapeNew(&shape);
2402 int lsid = swf_ShapeAddLineStyle(shape,1,&col_black);
2404 swf_SetU16(i->tag,myshapeid);
2405 SRECT r = gfxline_getSWFbbox(line);
2406 r = swf_ClipRect(i->pagebbox, r);
2407 swf_SetRect(i->tag,&r);
2408 swf_SetShapeStyles(i->tag,shape);
2409 swf_ShapeCountBits(shape,NULL,NULL);
2410 swf_SetShapeBits(i->tag,shape);
2411 swf_ShapeSetAll(i->tag,shape,UNDEFINED_COORD,UNDEFINED_COORD,lsid,0,0);
2412 drawgfxline(dev, line, 1);
2413 swf_ShapeSetEnd(i->tag);
2414 swf_ShapeFree(shape);
2416 i->tag = swf_InsertTag(i->tag,ST_PLACEOBJECT2);
2417 swf_ObjectPlace(i->tag, myshapeid, getNewDepth(dev), 0,0,0);
2420 static void swf_startclip(gfxdevice_t*dev, gfxline_t*line)
2422 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
2423 if(i->config_noclips)
2429 if(i->clippos >= 127)
2431 msg("<warning> Too many clip levels.");
2435 if(i->config_showclipshapes)
2436 drawoutline(dev, line);
2438 int myshapeid = getNewID(dev);
2439 i->tag = swf_InsertTag(i->tag,ST_DEFINESHAPE3);
2441 memset(&col, 0, sizeof(RGBA));
2444 swf_ShapeNew(&shape);
2445 int fsid = swf_ShapeAddSolidFillStyle(shape,&col);
2447 RGBA markcol = {0,i->mark[0],i->mark[1],i->mark[2]};
2448 swf_ShapeAddSolidFillStyle(shape,&markcol);
2450 swf_SetU16(i->tag,myshapeid);
2451 SRECT r = gfxline_getSWFbbox(line);
2452 r = swf_ClipRect(i->pagebbox, r);
2453 swf_SetRect(i->tag,&r);
2454 swf_SetShapeStyles(i->tag,shape);
2455 swf_ShapeCountBits(shape,NULL,NULL);
2456 swf_SetShapeBits(i->tag,shape);
2457 swf_ShapeSetAll(i->tag,shape,UNDEFINED_COORD,UNDEFINED_COORD,0,fsid,0);
2458 i->swflastx = i->swflasty = UNDEFINED_COORD;
2459 i->shapeisempty = 1;
2460 drawgfxline(dev, line, 1);
2461 if(i->shapeisempty) {
2462 /* an empty clip shape is equivalent to a shape with no area */
2463 int x = line?line->x:0;
2464 int y = line?line->y:0;
2465 moveto(dev, i->tag, x,y);
2466 lineto(dev, i->tag, x,y);
2467 lineto(dev, i->tag, x,y);
2469 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)) {
2470 if(i->config_watermark) {
2471 gfxbbox_t r; r.xmin = r.ymin = 0;r.xmax = i->max_x;r.ymax = i->max_y;
2472 draw_watermark(dev, r, 1);
2475 swf_ShapeSetEnd(i->tag);
2476 swf_ShapeFree(shape);
2478 /* TODO: remember the bbox, and check all shapes against it */
2480 msg("<trace> Placing clip ID %d", myshapeid);
2481 i->tag = swf_InsertTag(i->tag,ST_PLACEOBJECT2);
2482 i->cliptags[i->clippos] = i->tag;
2483 i->clipshapes[i->clippos] = myshapeid;
2484 i->clipdepths[i->clippos] = getNewDepth(dev);
2488 static void swf_endclip(gfxdevice_t*dev)
2490 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
2491 if(i->config_noclips)
2499 msg("<error> Invalid end of clipping region");
2503 /*swf_ObjectPlaceClip(i->cliptags[i->clippos],i->clipshapes[i->clippos],i->clipdepths[i->clippos],&i->page_matrix,NULL,NULL,
2504 / * clip to depth: * / i->depth <= i->clipdepths[i->clippos]? i->depth : i->depth - 1);
2506 swf_ObjectPlaceClip(i->cliptags[i->clippos],i->clipshapes[i->clippos],i->clipdepths[i->clippos],&i->page_matrix,NULL,NULL,i->depth);
2508 static int gfxline_type(gfxline_t*line)
2514 int haszerosegments=0;
2517 if(line->type == gfx_moveTo) {
2520 } else if(line->type == gfx_lineTo) {
2524 } else if(line->type == gfx_splineTo) {
2526 if(tmpsplines>lines)
2534 if(lines==0 && splines==0) return 0;
2535 else if(lines==1 && splines==0) return 1;
2536 else if(lines==0 && splines==1) return 2;
2537 else if(splines==0) return 3;
2541 static int gfxline_has_dots(gfxline_t*line)
2549 if(line->type == gfx_moveTo) {
2550 /* test the length of the preceding line, and assume it is a dot if
2551 it's length is less than 1.0. But *only* if there's a noticable
2552 gap between the previous line and the next moveTo. (I've come
2553 across a PDF where thousands of "dots" were stringed together,
2555 int last_short_gap = short_gap;
2556 if((fabs(line->x - x) + fabs(line->y - y)) < 1.0) {
2561 if(isline && dist < 1 && !short_gap && !last_short_gap) {
2566 } else if(line->type == gfx_lineTo) {
2567 dist += fabs(line->x - x) + fabs(line->y - y);
2569 } else if(line->type == gfx_splineTo) {
2570 dist += fabs(line->sx - x) + fabs(line->sy - y) +
2571 fabs(line->x - line->sx) + fabs(line->y - line->sy);
2578 if(isline && dist < 1 && !short_gap) {
2584 static int gfxline_fix_short_edges(gfxline_t*line)
2588 if(line->type == gfx_lineTo) {
2589 if(fabs(line->x - x) + fabs(line->y - y) < 0.01) {
2592 } else if(line->type == gfx_splineTo) {
2593 if(fabs(line->sx - x) + fabs(line->sy - y) +
2594 fabs(line->x - line->sx) + fabs(line->y - line->sy) < 0.01) {
2605 static char is_inside_page(gfxdevice_t*dev, gfxcoord_t x, gfxcoord_t y)
2607 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
2608 if(x<i->min_x || x>i->max_x) return 0;
2609 if(y<i->min_y || y>i->max_y) return 0;
2613 gfxline_t* gfxline_move(gfxline_t*line, double x, double y)
2615 gfxline_t*l = line = gfxline_clone(line);
2627 //#define NORMALIZE_POLYGON_POSITIONS
2629 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)
2631 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
2632 if(line_is_empty(line))
2634 int type = gfxline_type(line);
2635 int has_dots = gfxline_has_dots(line);
2636 gfxbbox_t r = gfxline_getbbox(line);
2637 int is_outside_page = !is_inside_page(dev, r.xmin, r.ymin) || !is_inside_page(dev, r.xmax, r.ymax);
2639 /* TODO: * split line into segments, and perform this check for all segments */
2641 if(i->config_disable_polygon_conversion || /*type>=5 ||*/
2643 (width <= i->config_caplinewidth
2644 || (cap_style == gfx_capRound && joint_style == gfx_joinRound)
2645 || (cap_style == gfx_capRound && type<=2))))
2649 /* convert line to polygon */
2650 msg("<trace> draw as polygon, type=%d dots=%d", type, has_dots);
2652 gfxline_fix_short_edges(line);
2653 /* we need to convert the line into a polygon */
2654 gfxpoly_t* poly = gfxpoly_from_stroke(line, width, cap_style, joint_style, miterLimit, DEFAULT_GRID);
2655 gfxline_t*gfxline = gfxline_from_gfxpoly(poly);
2656 dev->fill(dev, gfxline, color);
2657 gfxline_free(gfxline);
2658 gfxpoly_destroy(poly);
2662 msg("<trace> draw as stroke, type=%d dots=%d", type, has_dots);
2665 if(i->config_normalize_polygon_positions) {
2667 double startx = 0, starty = 0;
2668 if(line && line->type == gfx_moveTo) {
2672 line = gfxline_move(line, -startx, -starty);
2673 i->shapeposx = (int)(startx*20);
2674 i->shapeposy = (int)(starty*20);
2677 swfoutput_setstrokecolor(dev, color->r, color->g, color->b, color->a);
2678 swfoutput_setlinewidth(dev, width);
2681 drawgfxline(dev, line, 0);
2683 if(i->config_normalize_polygon_positions) {
2684 free(line); //account for _move
2689 static void swf_fill(gfxdevice_t*dev, gfxline_t*line, gfxcolor_t*color)
2691 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
2692 if(line_is_empty(line))
2696 gfxbbox_t r = gfxline_getbbox(line);
2697 int is_outside_page = !is_inside_page(dev, r.xmin, r.ymin) || !is_inside_page(dev, r.xmax, r.ymax);
2701 if(!i->config_ignoredraworder)
2704 if(i->config_normalize_polygon_positions) {
2706 double startx = 0, starty = 0;
2707 if(line && line->type == gfx_moveTo) {
2711 line = gfxline_move(line, -startx, -starty);
2712 i->shapeposx = (int)(startx*20);
2713 i->shapeposy = (int)(starty*20);
2716 swfoutput_setfillcolor(dev, color->r, color->g, color->b, color->a);
2719 drawgfxline(dev, line, 1);
2721 if(i->currentswfid==2 && r.xmin==0 && r.ymin==0 && r.xmax==i->max_x && r.ymax==i->max_y) {
2722 if(i->config_watermark) {
2723 draw_watermark(dev, r, 1);
2727 msg("<trace> end of swf_fill (shapeid=%d)", i->shapeid);
2729 if(i->config_normalize_polygon_positions) {
2730 free(line); //account for _move
2734 static GRADIENT* gfxgradient_to_GRADIENT(gfxgradient_t*gradient)
2737 gfxgradient_t*g = gradient;
2742 GRADIENT* swfgradient = malloc(sizeof(GRADIENT));
2743 swfgradient->num = num;
2744 swfgradient->rgba = malloc(sizeof(swfgradient->rgba[0])*num);
2745 swfgradient->ratios = malloc(sizeof(swfgradient->ratios[0])*num);
2750 swfgradient->ratios[num] = g->pos*255;
2751 swfgradient->rgba[num] = *(RGBA*)&g->color;
2758 static void swf_fillgradient(gfxdevice_t*dev, gfxline_t*line, gfxgradient_t*gradient, gfxgradienttype_t type, gfxmatrix_t*matrix)
2760 if(line_is_empty(line))
2762 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
2764 if(line_is_empty(line))
2767 GRADIENT* swfgradient = gfxgradient_to_GRADIENT(gradient);
2774 double f = type==gfxgradient_radial?4:4;
2776 m.sx = (int)(matrix->m00*20*f); m.r1 = (int)(matrix->m10*20*f);
2777 m.r0 = (int)(matrix->m01*20*f); m.sy = (int)(matrix->m11*20*f);
2778 m.tx = (int)(matrix->tx*20);
2779 m.ty = (int)(matrix->ty*20);
2782 int myshapeid = getNewID(dev);
2783 i->tag = swf_InsertTag(i->tag, ST_DEFINESHAPE2);
2785 swf_ShapeNew(&shape);
2786 int fsid = swf_ShapeAddGradientFillStyle(shape,&m,swfgradient,type==gfxgradient_radial);
2787 swf_SetU16(i->tag, myshapeid);
2788 SRECT r = gfxline_getSWFbbox(line);
2789 r = swf_ClipRect(i->pagebbox, r);
2790 swf_SetRect(i->tag,&r);
2791 swf_SetShapeStyles(i->tag,shape);
2792 swf_ShapeCountBits(shape,NULL,NULL);
2793 swf_SetShapeBits(i->tag,shape);
2794 swf_ShapeSetAll(i->tag,shape,UNDEFINED_COORD,UNDEFINED_COORD,0,fsid,0);
2795 i->swflastx = i->swflasty = UNDEFINED_COORD;
2796 drawgfxline(dev, line, 1);
2797 swf_ShapeSetEnd(i->tag);
2798 swf_ShapeFree(shape);
2800 int depth = getNewDepth(dev);
2801 msg("<trace> Placing gradient, shape ID %d, depth %d", myshapeid, depth);
2802 i->tag = swf_InsertTag(i->tag,ST_PLACEOBJECT2);
2803 swf_ObjectPlace(i->tag,myshapeid,depth,&i->page_matrix,NULL,NULL);
2805 swf_FreeGradient(swfgradient);free(swfgradient);
2808 static SWFFONT* gfxfont_to_swffont(gfxfont_t*font, const char* id, int version)
2810 SWFFONT*swffont = (SWFFONT*)rfx_calloc(sizeof(SWFFONT));
2812 SRECT bounds = {0,0,0,0};
2814 swffont->version = version;
2815 swffont->name = (U8*)strdup(id);
2816 swffont->layout = (SWFLAYOUT*)rfx_calloc(sizeof(SWFLAYOUT));
2817 swffont->layout->ascent = 0;
2818 swffont->layout->descent = 0;
2819 swffont->layout->leading = 0;
2820 swffont->layout->bounds = (SRECT*)rfx_calloc(sizeof(SRECT)*font->num_glyphs);
2821 swffont->encoding = FONT_ENCODING_UNICODE;
2822 swffont->numchars = font->num_glyphs;
2823 swffont->maxascii = font->max_unicode;
2824 swffont->ascii2glyph = (int*)rfx_calloc(sizeof(int)*swffont->maxascii);
2825 swffont->glyph2ascii = (U16*)rfx_calloc(sizeof(U16)*swffont->numchars);
2826 swffont->glyph = (SWFGLYPH*)rfx_calloc(sizeof(SWFGLYPH)*swffont->numchars);
2827 swffont->glyphnames = (char**)rfx_calloc(sizeof(char*)*swffont->numchars);
2829 SRECT max = {0,0,0,0};
2830 for(t=0;t<font->num_glyphs;t++) {
2834 int u = font->glyphs[t].unicode;
2837 for(s=0;s<font->num_glyphs;s++) {
2838 if(swffont->glyph2ascii[s]==u)
2841 if(u >= 0xe000 || u == 0x0000 || twice) {
2842 /* flash 8 flashtype requires unique unicode IDs for each character.
2843 We use the Unicode private user area to assign characters, hoping that
2844 the font doesn't contain more than 2048 glyphs */
2845 u = 0xe000 + (t&0x1fff);
2847 swffont->glyph2ascii[t] = u;
2849 if(font->glyphs[t].name) {
2850 swffont->glyphnames[t] = strdup(font->glyphs[t].name);
2852 swffont->glyphnames[t] = 0;
2854 advance = font->glyphs[t].advance;
2856 swf_Shape01DrawerInit(&draw, 0);
2857 line = font->glyphs[t].line;
2859 const double scale = GLYPH_SCALE;
2862 c.x = line->sx * scale; c.y = -line->sy * scale;
2863 //to.x = floor(line->x * scale); to.y = floor(-line->y * scale);
2864 to.x = line->x * scale; to.y = -line->y * scale;
2866 /*if(strstr(swffont->name, "BIRNU") && t==90) {
2870 if(line->type == gfx_moveTo) {
2871 draw.moveTo(&draw, &to);
2872 } else if(line->type == gfx_lineTo) {
2873 draw.lineTo(&draw, &to);
2874 } else if(line->type == gfx_splineTo) {
2875 draw.splineTo(&draw, &c, &to);
2880 swffont->glyph[t].shape = swf_ShapeDrawerToShape(&draw);
2882 SRECT bbox = swf_ShapeDrawerGetBBox(&draw);
2883 swf_ExpandRect2(&max, &bbox);
2885 swffont->layout->bounds[t] = bbox;
2887 if(advance<32768.0/20) {
2888 swffont->glyph[t].advance = (int)(advance*20);
2890 //msg("<warning> Advance value overflow in glyph %d", t);
2891 swffont->glyph[t].advance = 32767;
2894 draw.dealloc(&draw);
2896 swf_ExpandRect2(&bounds, &swffont->layout->bounds[t]);
2899 for(t=0;t<font->num_glyphs;t++) {
2900 SRECT bbox = swffont->layout->bounds[t];
2902 /* if the glyph doesn't have a bounding box, use the
2903 combined bounding box (necessary e.g. for space characters) */
2904 if(!(bbox.xmin|bbox.ymin|bbox.xmax|bbox.ymax)) {
2905 swffont->layout->bounds[t] = bbox = max;
2908 /* check that the advance value is reasonable, by comparing it
2909 with the bounding box */
2910 if(bbox.xmax>0 && (bbox.xmax*10 < swffont->glyph[t].advance || !swffont->glyph[t].advance)) {
2911 if(swffont->glyph[t].advance)
2912 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);
2913 swffont->glyph[t].advance = bbox.xmax;
2915 //swffont->glyph[t].advance = bbox.xmax - bbox.xmin;
2919 /* Flash player will use the advance value from the char, and the ascent/descent values
2920 from the layout for text selection.
2921 ascent will extend the char into negative y direction, from the baseline, while descent
2922 will extend in positive y direction, also from the baseline.
2923 The baseline is defined as the y-position zero
2926 swffont->layout->ascent = bounds.ymin<0?-bounds.ymin:0;
2927 swffont->layout->descent = bounds.ymax>0?bounds.ymax:0;
2928 swffont->layout->leading = bounds.ymax - bounds.ymin;
2930 /* if the font has proper ascent/descent values (>0) and those define
2931 greater line spacing that what we estimated from the bounding boxes,
2932 use the font's parameters */
2933 if(font->ascent*20 > swffont->layout->ascent)
2934 swffont->layout->ascent = font->ascent*20;
2935 if(font->descent*20 > swffont->layout->descent)
2936 swffont->layout->descent = font->descent*20;
2938 swf_FontSort(swffont);
2942 static void swf_addfont(gfxdevice_t*dev, gfxfont_t*font)
2944 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
2946 if(i->swffont && i->swffont->name && !strcmp((char*)i->swffont->name,font->id))
2947 return; // the requested font is the current font
2949 fontlist_t*last=0,*l = i->fontlist;
2952 if(!strcmp((char*)l->swffont->name, font->id)) {
2953 return; // we already know this font
2957 l = (fontlist_t*)rfx_calloc(sizeof(fontlist_t));
2958 l->swffont = gfxfont_to_swffont(font, font->id, (i->config_flashversion>=8 && !NO_FONT3)?3:2);
2965 swf_FontSetID(l->swffont, getNewID(i->dev));
2967 if(getScreenLogLevel() >= LOGLEVEL_DEBUG) {
2969 // print font information
2970 msg("<debug> Font %s",font->id);
2971 msg("<debug> | ID: %d", l->swffont->id);
2972 msg("<debug> | Version: %d", l->swffont->version);
2973 msg("<debug> | Name: %s", l->swffont->name);
2974 msg("<debug> | Numchars: %d", l->swffont->numchars);
2975 msg("<debug> | Maxascii: %d", l->swffont->maxascii);
2976 msg("<debug> | Style: %d", l->swffont->style);
2977 msg("<debug> | Encoding: %d", l->swffont->encoding);
2978 for(iii=0; iii<l->swffont->numchars;iii++) {
2979 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,
2980 l->swffont->layout->bounds[iii].xmin/20.0,
2981 l->swffont->layout->bounds[iii].ymin/20.0,
2982 l->swffont->layout->bounds[iii].xmax/20.0,
2983 l->swffont->layout->bounds[iii].ymax/20.0
2989 static void swf_switchfont(gfxdevice_t*dev, const char*fontid)
2991 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
2993 if(i->swffont && i->swffont->name && !strcmp((char*)i->swffont->name,fontid))
2994 return; // the requested font is the current font
2996 fontlist_t*l = i->fontlist;
2998 if(!strcmp((char*)l->swffont->name, fontid)) {
2999 i->swffont = l->swffont;
3004 msg("<error> Unknown font id: %s", fontid);
3008 /* sets the matrix which is to be applied to characters drawn by swfoutput_drawchar() */
3009 static void setfontscale(gfxdevice_t*dev,double m11,double m12, double m21,double m22,double x, double y, char force)
3015 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
3016 if(i->lastfontm11 == m11 &&
3017 i->lastfontm12 == m12 &&
3018 i->lastfontm21 == m21 &&
3019 i->lastfontm22 == m22 && !force)
3024 i->lastfontm11 = m11;
3025 i->lastfontm12 = m12;
3026 i->lastfontm21 = m21;
3027 i->lastfontm22 = m22;
3029 double xsize = sqrt(m11*m11 + m12*m12);
3030 double ysize = sqrt(m21*m21 + m22*m22);
3033 if(i->config_flashversion>=8 && !NO_FONT3)
3036 i->current_font_size = (xsize>ysize?xsize:ysize)*extrazoom;
3037 if(i->current_font_size < 1)
3038 i->current_font_size = 1;
3041 swf_GetMatrix(0, &m);
3043 if(m21 || m12 || fabs(m11+m22)>0.001) {
3044 double ifs = (double)extrazoom/(i->current_font_size);
3045 m.sx = (S32)((m11*ifs)*65536); m.r1 = -(S32)((m21*ifs)*65536);
3046 m.r0 = (S32)((m12*ifs)*65536); m.sy = -(S32)((m22*ifs)*65536);
3049 /* this is the position of the first char to set a new fontmatrix-
3050 we hope that it's close enough to all other characters using the
3051 font, so we use its position as origin for the matrix */
3058 static void swf_drawchar(gfxdevice_t*dev, gfxfont_t*font, int glyph, gfxcolor_t*color, gfxmatrix_t*matrix)
3060 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
3062 msg("<error> swf_drawchar called (glyph %d) without font", glyph);
3066 if(i->config_drawonlyshapes) {
3067 gfxglyph_t*g = &font->glyphs[glyph];
3068 gfxline_t*line2 = gfxline_clone(g->line);
3069 gfxline_transform(line2, matrix);
3070 dev->fill(dev, line2, color);
3071 gfxline_free(line2);
3075 if(!i->swffont || !i->swffont->name || strcmp((char*)i->swffont->name,font->id)) // not equal to current font
3077 swf_switchfont(dev, font->id); // set the current font
3081 msg("<warning> swf_drawchar: Font is NULL");
3084 if(glyph<0 || glyph>=i->swffont->numchars) {
3085 msg("<warning> No character %d in font %s (%d chars)", glyph, FIXNULL((char*)i->swffont->name), i->swffont->numchars);
3088 glyph = i->swffont->glyph2glyph[glyph];
3090 setfontscale(dev, matrix->m00, matrix->m01, matrix->m10, matrix->m11, matrix->tx, matrix->ty, 0);
3092 double det = i->fontmatrix.sx/65536.0 * i->fontmatrix.sy/65536.0 -
3093 i->fontmatrix.r0/65536.0 * i->fontmatrix.r1/65536.0;
3094 if(fabs(det) < 0.0005) {
3095 /* x direction equals y direction- the text is invisible */
3096 msg("<verbose> Not drawing invisible character %d (det=%f, m=[%f %f;%f %f]\n", glyph,
3098 i->fontmatrix.sx/65536.0, i->fontmatrix.r1/65536.0,
3099 i->fontmatrix.r0/65536.0, i->fontmatrix.sy/65536.0);
3103 /*if(i->swffont->glyph[glyph].shape->bitlen <= 16) {
3104 msg("<warning> Glyph %d in current charset (%s, %d characters) is empty",
3105 glyph, FIXNULL((char*)i->swffont->name), i->swffont->numchars);
3109 /* calculate character position with respect to the current font matrix */
3110 double s = 20 * GLYPH_SCALE / det;
3111 double px = matrix->tx - i->fontmatrix.tx/20.0;
3112 double py = matrix->ty - i->fontmatrix.ty/20.0;
3113 int x = (SCOORD)(( px * i->fontmatrix.sy/65536.0 - py * i->fontmatrix.r1/65536.0)*s);
3114 int y = (SCOORD)((- px * i->fontmatrix.r0/65536.0 + py * i->fontmatrix.sx/65536.0)*s);
3115 if(x>32767 || x<-32768 || y>32767 || y<-32768) {
3116 msg("<verbose> Moving character origin to %f %f\n", matrix->tx, matrix->ty);
3118 setfontscale(dev, matrix->m00, matrix->m01, matrix->m10, matrix->m11, matrix->tx, matrix->ty, 1);
3119 /* since we just moved the char origin to the current char's position,
3120 it now has the relative position (0,0) */
3129 msg("<trace> Drawing char %d in font %d at %d,%d in color %02x%02x%02x%02x",
3130 glyph, i->swffont->id, x, y, color->r, color->g, color->b, color->a);
3132 if(color->a == 0 && i->config_invisibletexttofront) {
3133 RGBA color2 = *(RGBA*)color;
3134 if(i->config_flashversion>=8) {
3135 // use "multiply" blend mode
3136 color2.a = color2.r = color2.g = color2.b = 255;
3138 i->topchardata = charbuffer_append(i->topchardata, i->swffont, glyph, x, y, i->current_font_size, color2, &i->fontmatrix);
3140 i->chardata = charbuffer_append(i->chardata, i->swffont, glyph, x, y, i->current_font_size, *(RGBA*)color, &i->fontmatrix);
3142 swf_FontUseGlyph(i->swffont, glyph, i->current_font_size);