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;
965 charbuffer_writetodevandfree(dev, i->topchardata, 1);
972 if(i->config_watermark) {
973 insert_watermark(dev, 1);
979 static void addViewer(gfxdevice_t* dev)
981 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
984 RGBA button_colors[3]= {{0xbf,0x00,0x00,0x80},{0xbf,0x20,0x20,0xc0}, {0xbf,0xc0,0xc0,0xff}};
986 int button_sizex = 20;
987 int button_sizey = 20;
989 RGBA black = {255,0,0,0};
991 i->tag = swf_InsertTag(i->tag,ST_DEFINESHAPE3);
993 int ls1 = swf_ShapeAddLineStyle(s,40,&black);
994 int fs1 = swf_ShapeAddSolidFillStyle(s,&button_colors[t/2]);
995 int shapeid = ids[t] = getNewID(dev);
996 swf_SetU16(i->tag,shapeid);
998 r.xmin = -20*button_sizex;
999 r.xmax = 20*button_sizex;
1001 r.ymax = 40*button_sizey;
1002 swf_SetRect(i->tag,&r); // set shape bounds
1003 swf_SetShapeHeader(i->tag,s); // write all styles to tag
1004 swf_ShapeSetAll(i->tag,s,0*button_sizex,0,ls1,fs1,0);
1005 swf_ShapeSetLine(i->tag,s,(1-(t&1)*2)*20*button_sizex,20*button_sizey);
1006 swf_ShapeSetLine(i->tag,s,-(1-(t&1)*2)*20*button_sizex,20*button_sizey);
1007 swf_ShapeSetLine(i->tag,s,0,-40*button_sizey);
1008 swf_ShapeSetEnd(i->tag); // finish drawing
1009 swf_ShapeFree(s); // clean shape structure (which isn't needed anymore after writing the tag)
1011 ActionTAG*a1=0,*a2=0,*a3=0;
1012 a1 = action_NextFrame(a1);
1013 a1 = action_Stop(a1);
1014 a1 = action_End(a1);
1016 a2 = action_PreviousFrame(a2);
1017 a2 = action_Stop(a2);
1018 a2 = action_End(a2);
1020 a3 = action_Stop(a3);
1021 a3 = action_End(a3);
1023 i->tag = swf_InsertTag(i->tag, ST_DOACTION);
1024 swf_ActionSet(i->tag,a3);
1026 i->tag = swf_InsertTag(i->tag,ST_DEFINEBUTTON);
1027 int buttonid1 = getNewID(dev);
1028 swf_SetU16(i->tag, buttonid1);
1029 swf_ButtonSetRecord(i->tag,BS_UP|BS_HIT,ids[0],1,NULL,NULL);
1030 swf_ButtonSetRecord(i->tag,BS_OVER,ids[2],1,NULL,NULL);
1031 swf_ButtonSetRecord(i->tag,BS_DOWN,ids[4],1,NULL,NULL);
1032 swf_SetU8(i->tag,0); // end of button records
1033 swf_ActionSet(i->tag,a1);
1035 i->tag = swf_InsertTag(i->tag,ST_DEFINEBUTTON);
1036 int buttonid2 = getNewID(dev);
1037 swf_SetU16(i->tag, buttonid2);
1038 swf_ButtonSetRecord(i->tag,BS_UP|BS_HIT,ids[1],1,NULL,NULL);
1039 swf_ButtonSetRecord(i->tag,BS_OVER,ids[3],1,NULL,NULL);
1040 swf_ButtonSetRecord(i->tag,BS_DOWN,ids[5],1,NULL,NULL);
1041 swf_SetU8(i->tag,0); // end of button records
1042 swf_ActionSet(i->tag,a2);
1044 i->tag = swf_InsertTag(i->tag,ST_PLACEOBJECT2);
1046 swf_GetMatrix(0, &m);
1047 m.tx = button_sizex*20+200;
1048 swf_ObjectPlace(i->tag, buttonid2, 65534,&m,0,0);
1049 i->tag = swf_InsertTag(i->tag,ST_PLACEOBJECT2);
1050 m.tx = button_sizex*20+200+200;
1051 swf_ObjectPlace(i->tag, buttonid1, 65535,&m,0,0);
1055 void swf_startframe(gfxdevice_t*dev, int width, int height)
1057 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1059 if(i->config_protect) {
1060 i->tag = swf_InsertTag(i->tag, ST_PROTECT);
1061 i->config_protect = 0;
1063 if(i->config_simpleviewer) {
1068 if(!i->firstpage && !i->pagefinished)
1071 msg("<verbose> Starting new SWF page of size %dx%d", width, height);
1073 swf_GetMatrix(0, &i->page_matrix);
1074 i->page_matrix.tx = 0;
1075 i->page_matrix.ty = 0;
1082 /* create a bbox structure with the page size. This is used
1083 for clipping shape and text bounding boxes. As we don't want to
1084 generate bounding boxes which extend beyond the movie size (in
1085 order to not confuse Flash), we clip everything against i->pagebbox */
1086 i->pagebbox.xmin = 0;
1087 i->pagebbox.ymin = 0;
1088 i->pagebbox.xmax = width*20;
1089 i->pagebbox.ymax = height*20;
1091 /* increase SWF's bounding box */
1092 swf_ExpandRect2(&i->swf->movieSize, &i->pagebbox);
1094 i->lastframeno = i->frameno;
1096 i->pagefinished = 0;
1100 void swf_endframe(gfxdevice_t*dev)
1102 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1104 if(!i->pagefinished)
1107 if( (i->swf->fileVersion <= 8) && (i->config_insertstoptag) ) {
1109 atag = action_Stop(atag);
1110 atag = action_End(atag);
1111 i->tag = swf_InsertTag(i->tag,ST_DOACTION);
1112 swf_ActionSet(i->tag,atag);
1114 i->tag = swf_InsertTag(i->tag,ST_SHOWFRAME);
1117 for(i->depth;i->depth>i->startdepth;i->depth--) {
1118 i->tag = swf_InsertTag(i->tag,ST_REMOVEOBJECT2);
1119 swf_SetU16(i->tag,i->depth);
1121 i->depth = i->startdepth;
1123 if(i->config_frameresets) {
1124 for(i->currentswfid;i->currentswfid>i->startids;i->currentswfid--) {
1125 i->tag = swf_InsertTag(i->tag,ST_FREECHARACTER);
1126 swf_SetU16(i->tag,i->currentswfid);
1128 i->currentswfid = i->startids;
1132 static void setBackground(gfxdevice_t*dev, int x1, int y1, int x2, int y2)
1134 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1136 rgb.a = rgb.r = rgb.g = rgb.b = 0xff;
1140 int shapeid = getNewID(dev);
1145 i->tag = swf_InsertTag(i->tag, ST_DEFINESHAPE);
1147 fs1 = swf_ShapeAddSolidFillStyle(s, &rgb);
1148 swf_SetU16(i->tag,shapeid);
1149 swf_SetRect(i->tag,&r);
1150 swf_SetShapeHeader(i->tag,s);
1151 swf_ShapeSetAll(i->tag,s,x1,y1,ls1,fs1,0);
1152 swf_ShapeSetLine(i->tag,s,(x2-x1),0);
1153 swf_ShapeSetLine(i->tag,s,0,(y2-y1));
1154 swf_ShapeSetLine(i->tag,s,(x1-x2),0);
1155 swf_ShapeSetLine(i->tag,s,0,(y1-y2));
1156 swf_ShapeSetEnd(i->tag);
1158 i->tag = swf_InsertTag(i->tag, ST_PLACEOBJECT2);
1159 swf_ObjectPlace(i->tag,shapeid,getNewDepth(dev),0,0,0);
1160 i->tag = swf_InsertTag(i->tag, ST_PLACEOBJECT2);
1161 swf_ObjectPlaceClip(i->tag,shapeid,getNewDepth(dev),0,0,0,65535);
1164 /* initialize the swf writer */
1165 void gfxdevice_swf_init(gfxdevice_t* dev)
1167 memset(dev, 0, sizeof(gfxdevice_t));
1171 dev->internal = init_internal_struct(); // set config to default values
1173 dev->startpage = swf_startframe;
1174 dev->endpage = swf_endframe;
1175 dev->finish = swf_finish;
1176 dev->fillbitmap = swf_fillbitmap;
1177 dev->setparameter = swf_setparameter;
1178 dev->stroke = swf_stroke;
1179 dev->startclip = swf_startclip;
1180 dev->endclip = swf_endclip;
1181 dev->fill = swf_fill;
1182 dev->fillbitmap = swf_fillbitmap;
1183 dev->fillgradient = swf_fillgradient;
1184 dev->addfont = swf_addfont;
1185 dev->drawchar = swf_drawchar;
1186 dev->drawlink = swf_drawlink;
1188 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1191 msg("<verbose> initializing swf output\n", i->max_x,i->max_y);
1195 i->swf = (SWF*)rfx_calloc(sizeof(SWF));
1196 i->swf->fileVersion = 0;
1197 i->swf->frameRate = 0x80;
1198 i->swf->movieSize.xmin = 0;
1199 i->swf->movieSize.ymin = 0;
1200 i->swf->movieSize.xmax = 0;
1201 i->swf->movieSize.ymax = 0;
1202 i->swf->fileAttributes = 9; // as3, local-with-network
1204 i->swf->firstTag = swf_InsertTag(NULL,ST_SETBACKGROUNDCOLOR);
1205 i->tag = i->swf->firstTag;
1207 rgb.a = rgb.r = rgb.g = rgb.b = 0xff;
1209 swf_SetRGB(i->tag,&rgb);
1211 i->startdepth = i->depth = 0;
1212 i->startids = i->currentswfid = 0;
1215 static void startshape(gfxdevice_t*dev)
1217 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1222 //if(i->chardatapos && i->chardata[i->chardatapos-1].color.a)
1225 i->tag = swf_InsertTag(i->tag,ST_DEFINESHAPE3);
1227 swf_ShapeNew(&i->shape);
1228 i->linestyleid = swf_ShapeAddLineStyle(i->shape,i->linewidth,&i->strokergb);
1229 i->fillstyleid = swf_ShapeAddSolidFillStyle(i->shape,&i->fillrgb);
1231 RGBA markcol = {0,i->mark[0],i->mark[1],i->mark[2]};
1232 swf_ShapeAddSolidFillStyle(i->shape,&markcol);
1235 i->shapeid = getNewID(dev);
1237 msg("<debug> Using shape id %d", i->shapeid);
1239 swf_SetU16(i->tag,i->shapeid); // ID
1241 i->bboxrectpos = i->tag->len;
1243 swf_SetRect(i->tag,&i->pagebbox);
1245 memset(&i->bboxrect, 0, sizeof(i->bboxrect));
1247 swf_SetShapeStyles(i->tag,i->shape);
1248 swf_ShapeCountBits(i->shape,NULL,NULL);
1249 swf_SetShapeBits(i->tag,i->shape);
1251 /* TODO: do we really need this? */
1252 //swf_ShapeSetAll(i->tag,i->shape,/*x*/0,/*y*/0,i->linestyleid,0,0);
1253 //swf_ShapeSetAll(i->tag,i->shape,/*x*/UNDEFINED_COORD,/*y*/UNDEFINED_COORD,i->linestyleid,0,0);
1254 i->swflastx=i->swflasty=UNDEFINED_COORD;
1255 i->lastwasfill = -1;
1256 i->shapeisempty = 1;
1259 static void starttext(gfxdevice_t*dev)
1261 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1265 if(i->config_watermark) {
1266 insert_watermark(dev, 0);
1269 i->swflastx=i->swflasty=0;
1273 /* TODO: move to ../lib/rfxswf */
1274 void changeRect(gfxdevice_t*dev, TAG*tag, int pos, SRECT*newrect)
1276 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1277 /* determine length of old rect */
1281 swf_GetRect(tag, &old);
1282 swf_ResetReadBits(tag);
1283 int pos_end = tag->pos;
1285 int len = tag->len - pos_end;
1286 U8*data = (U8*)malloc(len);
1287 memcpy(data, &tag->data[pos_end], len);
1290 swf_SetRect(tag, newrect);
1291 swf_SetBlock(tag, data, len);
1293 tag->pos = tag->readBit = 0;
1296 void cancelshape(gfxdevice_t*dev)
1298 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1299 /* delete old shape tag */
1301 i->tag = i->tag->prev;
1302 swf_DeleteTag(0, todel);
1303 if(i->shape) {swf_ShapeFree(i->shape);i->shape=0;}
1305 i->bboxrectpos = -1;
1307 // i->currentswfid--; // doesn't work, for some reason
1310 void fixAreas(gfxdevice_t*dev)
1312 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1313 if(!i->shapeisempty && i->fill &&
1314 (i->bboxrect.xmin == i->bboxrect.xmax ||
1315 i->bboxrect.ymin == i->bboxrect.ymax) &&
1316 i->config_minlinewidth >= 0.001
1318 msg("<debug> Shape has size 0: width=%.2f height=%.2f",
1319 (i->bboxrect.xmax-i->bboxrect.xmin)/20.0,
1320 (i->bboxrect.ymax-i->bboxrect.ymin)/20.0
1323 SRECT r = i->bboxrect;
1325 if(r.xmin == r.xmax && r.ymin == r.ymax) {
1326 /* this thing comes down to a single dot- nothing to fix here */
1332 RGBA save_col = i->strokergb;
1333 int save_width = i->linewidth;
1335 i->strokergb = i->fillrgb;
1336 i->linewidth = (int)(i->config_minlinewidth*20);
1337 if(i->linewidth==0) i->linewidth = 1;
1342 moveto(dev, i->tag, r.xmin/20.0,r.ymin/20.0);
1343 lineto(dev, i->tag, r.xmax/20.0,r.ymax/20.0);
1345 i->strokergb = save_col;
1346 i->linewidth = save_width;
1351 static void endshape_noput(gfxdevice_t*dev)
1353 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1356 //changeRect(dev, i->tag, i->bboxrectpos, &i->bboxrect);
1359 swf_ShapeFree(i->shape);
1367 static void endshape(gfxdevice_t*dev)
1369 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1375 if(i->shapeisempty ||
1377 (i->bboxrect.xmin == i->bboxrect.xmax &&
1378 i->bboxrect.ymin == i->bboxrect.ymax))
1380 // delete the shape again, we didn't do anything
1381 msg("<debug> cancelling shape: bbox is (%f,%f,%f,%f)",
1382 i->bboxrect.xmin /20.0,
1383 i->bboxrect.ymin /20.0,
1384 i->bboxrect.xmax /20.0,
1385 i->bboxrect.ymax /20.0
1391 swf_ShapeSetEnd(i->tag);
1393 SRECT r = swf_ClipRect(i->pagebbox, i->bboxrect);
1394 changeRect(dev, i->tag, i->bboxrectpos, &r);
1396 msg("<trace> Placing shape ID %d", i->shapeid);
1398 i->tag = swf_InsertTag(i->tag,ST_PLACEOBJECT2);
1399 MATRIX m = i->page_matrix;
1400 m.tx += i->shapeposx;
1401 m.ty += i->shapeposy;
1402 swf_ObjectPlace(i->tag,i->shapeid,getNewDepth(dev),&m,NULL,NULL);
1404 if(i->config_animate) {
1405 i->tag = swf_InsertTag(i->tag,ST_SHOWFRAME);
1408 swf_ShapeFree(i->shape);
1411 i->bboxrectpos = -1;
1418 void wipeSWF(SWF*swf)
1420 TAG*tag = swf->firstTag;
1422 TAG*next = tag->next;
1423 if(tag->id != ST_SETBACKGROUNDCOLOR &&
1424 tag->id != ST_END &&
1425 tag->id != ST_DOACTION &&
1426 tag->id != ST_SHOWFRAME) {
1427 swf_DeleteTag(swf, tag);
1433 void swfoutput_finalize(gfxdevice_t*dev)
1435 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1437 if(i->tag && i->tag->id == ST_END)
1438 return; //already done
1440 i->swf->fileVersion = i->config_flashversion;
1441 i->swf->frameRate = i->config_framerate*0x100;
1443 if(i->config_bboxvars) {
1444 TAG* tag = swf_InsertTag(i->swf->firstTag, ST_DOACTION);
1446 a = action_PushString(a, "xmin");
1447 a = action_PushFloat(a, i->swf->movieSize.xmin / 20.0);
1448 a = action_SetVariable(a);
1449 a = action_PushString(a, "ymin");
1450 a = action_PushFloat(a, i->swf->movieSize.ymin / 20.0);
1451 a = action_SetVariable(a);
1452 a = action_PushString(a, "xmax");
1453 a = action_PushFloat(a, i->swf->movieSize.xmax / 20.0);
1454 a = action_SetVariable(a);
1455 a = action_PushString(a, "ymax");
1456 a = action_PushFloat(a, i->swf->movieSize.ymax / 20.0);
1457 a = action_SetVariable(a);
1458 a = action_PushString(a, "width");
1459 a = action_PushFloat(a, (i->swf->movieSize.xmax - i->swf->movieSize.xmin) / 20.0);
1460 a = action_SetVariable(a);
1461 a = action_PushString(a, "height");
1462 a = action_PushFloat(a, (i->swf->movieSize.ymax - i->swf->movieSize.ymin) / 20.0);
1463 a = action_SetVariable(a);
1465 swf_ActionSet(tag, a);
1470 free(i->mark);i->mark = 0;
1474 fontlist_t *iterator = i->fontlist;
1475 char use_font3 = i->config_flashversion>=8 && !NO_FONT3;
1478 TAG*mtag = i->swf->firstTag;
1479 if(iterator->swffont) {
1480 if(!i->config_storeallcharacters) {
1481 msg("<debug> Reducing font %s", iterator->swffont->name);
1482 swf_FontReduce(iterator->swffont);
1484 int used = iterator->swffont->use && iterator->swffont->use->used_glyphs;
1487 mtag = swf_InsertTag(mtag, ST_DEFINEFONT2);
1488 swf_FontSetDefine2(mtag, iterator->swffont);
1490 mtag = swf_InsertTag(mtag, ST_DEFINEFONT3);
1491 swf_FontSetDefine2(mtag, iterator->swffont);
1496 iterator = iterator->next;
1499 i->tag = swf_InsertTag(i->tag,ST_END);
1500 TAG* tag = i->tag->prev;
1502 if(use_font3 && i->config_storeallcharacters && i->config_alignfonts) {
1503 swf_FontPostprocess(i->swf); // generate alignment information
1506 /* remove the removeobject2 tags between the last ST_SHOWFRAME
1507 and the ST_END- they confuse the flash player */
1508 while(tag->id == ST_REMOVEOBJECT2) {
1509 TAG* prev = tag->prev;
1510 swf_DeleteTag(i->swf, tag);
1517 if(i->config_enablezlib || i->config_flashversion>=6) {
1518 i->swf->compressed = 1;
1521 /* Add AVM2 actionscript */
1522 if(i->config_flashversion>=9 &&
1523 (i->config_insertstoptag || i->hasbuttons) && !i->config_linknameurl) {
1524 swf_AddButtonLinks(i->swf, i->config_insertstoptag,
1525 i->config_internallinkfunction||i->config_externallinkfunction);
1527 // if(i->config_reordertags)
1528 // swf_Optimize(i->swf);
1531 int swfresult_save(gfxresult_t*gfx, const char*filename)
1533 SWF*swf = (SWF*)gfx->internal;
1536 fi = open(filename, O_BINARY|O_CREAT|O_TRUNC|O_WRONLY, 0777);
1541 msg("<fatal> Could not create \"%s\". ", FIXNULL(filename));
1545 if FAILED(swf_WriteSWF(fi,swf))
1546 msg("<error> WriteSWF() failed.\n");
1552 void* swfresult_get(gfxresult_t*gfx, const char*name)
1554 SWF*swf = (SWF*)gfx->internal;
1555 if(!strcmp(name, "swf")) {
1556 return (void*)swf_CopySWF(swf);
1557 } else if(!strcmp(name, "xmin")) {
1558 return (void*)(ptroff_t)(swf->movieSize.xmin/20);
1559 } else if(!strcmp(name, "ymin")) {
1560 return (void*)(ptroff_t)(swf->movieSize.ymin/20);
1561 } else if(!strcmp(name, "xmax")) {
1562 return (void*)(ptroff_t)(swf->movieSize.xmax/20);
1563 } else if(!strcmp(name, "ymax")) {
1564 return (void*)(ptroff_t)(swf->movieSize.ymax/20);
1565 } else if(!strcmp(name, "width")) {
1566 return (void*)(ptroff_t)((swf->movieSize.xmax - swf->movieSize.xmin)/20);
1567 } else if(!strcmp(name, "height")) {
1568 return (void*)(ptroff_t)((swf->movieSize.ymax - swf->movieSize.ymin)/20);
1572 void swfresult_destroy(gfxresult_t*gfx)
1575 swf_FreeTags((SWF*)gfx->internal);
1576 free(gfx->internal);
1579 memset(gfx, 0, sizeof(gfxresult_t));
1583 static void swfoutput_destroy(gfxdevice_t* dev);
1585 gfxresult_t* swf_finish(gfxdevice_t* dev)
1587 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1590 if(i->config_linktarget) {
1591 free(i->config_linktarget);
1592 i->config_linktarget = 0;
1595 swfoutput_finalize(dev);
1596 SWF* swf = i->swf;i->swf = 0;
1597 swfoutput_destroy(dev);
1599 result = (gfxresult_t*)rfx_calloc(sizeof(gfxresult_t));
1600 result->internal = swf;
1601 result->save = swfresult_save;
1603 result->get = swfresult_get;
1604 result->destroy = swfresult_destroy;
1608 /* Perform cleaning up */
1609 static void swfoutput_destroy(gfxdevice_t* dev)
1611 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1613 /* not initialized yet- nothing to destroy */
1617 fontlist_t *tmp,*iterator = i->fontlist;
1619 if(iterator->swffont) {
1620 swf_FontFree(iterator->swffont);iterator->swffont=0;
1623 iterator = iterator->next;
1626 if(i->swf) {swf_FreeTags(i->swf);free(i->swf);i->swf = 0;}
1629 memset(dev, 0, sizeof(gfxdevice_t));
1632 static void swfoutput_setstrokecolor(gfxdevice_t* dev, U8 r, U8 g, U8 b, U8 a)
1634 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1635 if(i->strokergb.r == r &&
1636 i->strokergb.g == g &&
1637 i->strokergb.b == b &&
1638 i->strokergb.a == a) return;
1648 //#define ROUND_UP 19
1649 //#define ROUND_UP 10
1651 static void swfoutput_setlinewidth(gfxdevice_t*dev, double _linewidth)
1653 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1654 if(i->linewidth == (U16)(_linewidth*20+19.0/20.0))
1658 i->linewidth = (U16)(_linewidth*20+19.0/20.0);
1662 static void drawlink(gfxdevice_t*dev, ActionTAG*,ActionTAG*, gfxline_t*points, char mouseover, char*type, const char*url);
1663 static void swfoutput_namedlink(gfxdevice_t*dev, char*name, gfxline_t*points);
1664 static void swfoutput_linktopage(gfxdevice_t*dev, int page, gfxline_t*points);
1665 static void swfoutput_linktourl(gfxdevice_t*dev, const char*url, gfxline_t*points);
1667 /*void swfoutput_drawlink(gfxdevice_t*dev, char*url, gfxline_t*points)
1669 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1670 dev->drawlink(dev, points, url);
1673 void swf_drawlink(gfxdevice_t*dev, gfxline_t*points, const char*url)
1675 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1677 if(i->config_disablelinks)
1680 if(!strncmp("http://pdf2swf:", url, 15)) {
1681 char*tmp = strdup(url);
1682 int l = strlen(tmp);
1685 swfoutput_namedlink(dev, tmp+15, points);
1688 } else if(!strncmp("page", url, 4)) {
1691 if(url[t]<'0' || url[t]>'9')
1694 int page = atoi(&url[4]);
1695 if(page<0) page = 0;
1696 swfoutput_linktopage(dev, page, points);
1699 swfoutput_linktourl(dev, url, points);
1702 void swfoutput_linktourl(gfxdevice_t*dev, const char*url, gfxline_t*points)
1704 ActionTAG* actions = 0;
1705 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1711 /* TODO: escape special characters in url */
1713 if(i->config_externallinkfunction && i->config_flashversion<=8) {
1714 actions = action_PushString(actions, url); //parameter
1715 actions = action_PushInt(actions, 1); //number of parameters (1)
1716 actions = action_PushString(actions, i->config_externallinkfunction); //function name
1717 actions = action_CallFunction(actions);
1718 } else if(!i->config_linktarget) {
1719 if(!i->config_opennewwindow)
1720 actions = action_GetUrl(actions, url, "_parent");
1722 actions = action_GetUrl(actions, url, "_this");
1724 actions = action_GetUrl(actions, url, i->config_linktarget);
1726 actions = action_End(actions);
1728 drawlink(dev, actions, 0, points, 0, "url", url);
1730 swf_ActionFree(actions);
1732 void swfoutput_linktopage(gfxdevice_t*dev, int page, gfxline_t*points)
1734 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1735 ActionTAG* actions = 0;
1742 if(!i->config_internallinkfunction || i->config_flashversion>=9) {
1743 actions = action_GotoFrame(actions, page-1);
1744 actions = action_End(actions);
1746 actions = action_PushInt(actions, page); //parameter
1747 actions = action_PushInt(actions, 1); //number of parameters (1)
1748 actions = action_PushString(actions, i->config_internallinkfunction); //function name
1749 actions = action_CallFunction(actions);
1750 actions = action_End(actions);
1754 sprintf(name, "page%d", page);
1756 drawlink(dev, actions, 0, points, 0, "page", name);
1758 swf_ActionFree(actions);
1761 /* Named Links (a.k.a. Acrobatmenu) are used to implement various gadgets
1762 of the viewer objects, like subtitles, index elements etc.
1764 void swfoutput_namedlink(gfxdevice_t*dev, char*name, gfxline_t*points)
1766 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1767 ActionTAG *actions1,*actions2;
1768 char*tmp = strdup(name);
1777 if(!strncmp(tmp, "call:", 5))
1779 char*x = strchr(&tmp[5], ':');
1781 actions1 = action_PushInt(0, 0); //number of parameters (0)
1782 actions1 = action_PushString(actions1, &tmp[5]); //function name
1783 actions1 = action_CallFunction(actions1);
1784 actions1 = action_End(actions1);
1787 actions1 = action_PushString(0, x+1); //parameter
1788 actions1 = action_PushInt(actions1, 1); //number of parameters (1)
1789 actions1 = action_PushString(actions1, &tmp[5]); //function name
1790 actions1 = action_CallFunction(actions1);
1791 actions1 = action_End(actions1);
1793 actions2 = action_End(0);
1799 actions1 = action_PushString(0, "/:subtitle");
1800 actions1 = action_PushString(actions1, name);
1801 actions1 = action_SetVariable(actions1);
1802 actions1 = action_End(actions1);
1804 actions2 = action_PushString(0, "/:subtitle");
1805 actions2 = action_PushString(actions2, "");
1806 actions2 = action_SetVariable(actions2);
1807 actions2 = action_End(actions2);
1811 drawlink(dev, actions1, actions2, points, mouseover, type, name);
1813 swf_ActionFree(actions1);
1814 swf_ActionFree(actions2);
1818 static void drawgfxline(gfxdevice_t*dev, gfxline_t*line, int fill)
1820 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1821 gfxcoord_t lastx=0,lasty=0,px=0,py=0;
1823 int lines= 0, splines=0;
1830 /* check whether the next segment is zero */
1831 if(line->type == gfx_moveTo) {
1832 moveto(dev, i->tag, line->x, line->y);
1833 px = lastx = line->x;
1834 py = lasty = line->y;
1836 } if(line->type == gfx_lineTo) {
1837 lineto(dev, i->tag, line->x, line->y);
1842 } else if(line->type == gfx_splineTo) {
1844 s.x = line->sx;p.x = line->x;
1845 s.y = line->sy;p.y = line->y;
1846 splineto(dev, i->tag, s, p);
1854 msg("<trace> drawgfxline, %d lines, %d splines", lines, splines);
1858 static void drawlink(gfxdevice_t*dev, ActionTAG*actions1, ActionTAG*actions2, gfxline_t*points, char mouseover, char*type, const char*url)
1860 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1869 int buttonid = getNewID(dev);
1870 gfxbbox_t bbox = gfxline_getbbox(points);
1872 if(i->config_linknameurl) {
1880 myshapeid = getNewID(dev);
1881 i->tag = swf_InsertTag(i->tag,ST_DEFINESHAPE3);
1882 swf_ShapeNew(&i->shape);
1883 rgb.r = rgb.b = rgb.a = rgb.g = 0;
1884 fsid = swf_ShapeAddSolidFillStyle(i->shape,&rgb);
1885 swf_SetU16(i->tag, myshapeid);
1886 r.xmin = (int)(bbox.xmin*20);
1887 r.ymin = (int)(bbox.ymin*20);
1888 r.xmax = (int)(bbox.xmax*20);
1889 r.ymax = (int)(bbox.ymax*20);
1890 r = swf_ClipRect(i->pagebbox, r);
1891 swf_SetRect(i->tag,&r);
1892 swf_SetShapeStyles(i->tag,i->shape);
1893 swf_ShapeCountBits(i->shape,NULL,NULL);
1894 swf_SetShapeBits(i->tag,i->shape);
1895 swf_ShapeSetAll(i->tag,i->shape,/*x*/0,/*y*/0,0,fsid,0);
1896 i->swflastx = i->swflasty = 0;
1897 drawgfxline(dev, points, 1);
1898 swf_ShapeSetEnd(i->tag);
1899 swf_ShapeFree(i->shape);
1902 myshapeid2 = getNewID(dev);
1903 i->tag = swf_InsertTag(i->tag,ST_DEFINESHAPE3);
1904 swf_ShapeNew(&i->shape);
1906 rgb = i->config_linkcolor;
1908 fsid = swf_ShapeAddSolidFillStyle(i->shape,&rgb);
1909 swf_SetU16(i->tag, myshapeid2);
1910 r.xmin = (int)(bbox.xmin*20);
1911 r.ymin = (int)(bbox.ymin*20);
1912 r.xmax = (int)(bbox.xmax*20);
1913 r.ymax = (int)(bbox.ymax*20);
1914 r = swf_ClipRect(i->pagebbox, r);
1915 swf_SetRect(i->tag,&r);
1916 swf_SetShapeStyles(i->tag,i->shape);
1917 swf_ShapeCountBits(i->shape,NULL,NULL);
1918 swf_SetShapeBits(i->tag,i->shape);
1919 swf_ShapeSetAll(i->tag,i->shape,/*x*/0,/*y*/0,0,fsid,0);
1920 i->swflastx = i->swflasty = 0;
1921 drawgfxline(dev, points, 1);
1922 swf_ShapeSetEnd(i->tag);
1923 swf_ShapeFree(i->shape);
1927 i->tag = swf_InsertTag(i->tag,ST_DEFINEBUTTON);
1928 swf_SetU16(i->tag,buttonid); //id
1929 swf_ButtonSetFlags(i->tag, 0); //menu=no
1930 swf_ButtonSetRecord(i->tag,0x01,myshapeid,i->depth,0,0);
1931 swf_ButtonSetRecord(i->tag,0x02,myshapeid2,i->depth,0,0);
1932 swf_ButtonSetRecord(i->tag,0x04,myshapeid2,i->depth,0,0);
1933 swf_ButtonSetRecord(i->tag,0x08,myshapeid,i->depth,0,0);
1934 swf_SetU8(i->tag,0);
1935 swf_ActionSet(i->tag,actions1);
1936 swf_SetU8(i->tag,0);
1940 i->tag = swf_InsertTag(i->tag,ST_DEFINEBUTTON2);
1941 swf_SetU16(i->tag,buttonid); //id
1942 swf_ButtonSetFlags(i->tag, 0); //menu=no
1943 swf_ButtonSetRecord(i->tag,0x01,myshapeid,i->depth,0,0);
1944 swf_ButtonSetRecord(i->tag,0x02,myshapeid2,i->depth,0,0);
1945 swf_ButtonSetRecord(i->tag,0x04,myshapeid2,i->depth,0,0);
1946 swf_ButtonSetRecord(i->tag,0x08,myshapeid,i->depth,0,0);
1947 swf_SetU8(i->tag,0); // end of button records
1948 swf_ButtonSetCondition(i->tag, BC_IDLE_OVERUP);
1949 swf_ActionSet(i->tag,actions1);
1951 swf_ButtonSetCondition(i->tag, BC_OVERUP_IDLE);
1952 swf_ActionSet(i->tag,actions2);
1953 swf_SetU8(i->tag,0);
1954 swf_ButtonPostProcess(i->tag, 2);
1956 swf_SetU8(i->tag,0);
1957 swf_ButtonPostProcess(i->tag, 1);
1963 const char* name = 0;
1964 if(i->config_linknameurl) {
1965 buf2 = malloc(strlen(type)+strlen(url)+2);
1966 sprintf(buf2, "%s:%s", type, url);
1970 sprintf(buf, "button%d", buttonid);
1973 msg("<trace> Placing link ID %d", buttonid);
1974 i->tag = swf_InsertTag(i->tag,ST_PLACEOBJECT2);
1976 if(posx!=0 || posy!=0) {
1978 p.x = (int)(posx*20);
1979 p.y = (int)(posy*20);
1980 p = swf_TurnPoint(p, &i->page_matrix);
1985 swf_ObjectPlace(i->tag, buttonid, getNewDepth(dev),&m,0,(U8*)name);
1987 swf_ObjectPlace(i->tag, buttonid, getNewDepth(dev),&i->page_matrix,0,(U8*)name);
1997 for(t=0;t<picpos;t++)
1999 if(pic_xids[t] == xid &&
2000 pic_yids[t] == yid) {
2001 width = pic_width[t];
2002 height = pic_height[t];
2006 pic_ids[picpos] = swfoutput_drawimagelosslessN(&output, pic, pal, width, height, x1,y1,x2,y2,x3,y3,x4,y4, numpalette);
2007 pic_xids[picpos] = xid;
2008 pic_yids[picpos] = yid;
2009 pic_width[picpos] = width;
2010 pic_height[picpos] = height;
2013 pic[width*y+x] = buf[0];
2017 xid += pal[1].r*3 + pal[1].g*11 + pal[1].b*17;
2018 yid += pal[1].r*7 + pal[1].g*5 + pal[1].b*23;
2022 xid += x*r+x*b*3+x*g*7+x*a*11;
2023 yid += y*r*3+y*b*17+y*g*19+y*a*11;
2025 for(t=0;t<picpos;t++)
2027 if(pic_xids[t] == xid &&
2028 pic_yids[t] == yid) {
2037 int swf_setparameter(gfxdevice_t*dev, const char*name, const char*value)
2039 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
2041 msg("<trace> swfdevice: %s=%s", name, value);
2042 if(!strcmp(name, "jpegsubpixels")) {
2043 i->config_jpegsubpixels = atof(value);
2044 } else if(!strcmp(name, "ppmsubpixels")) {
2045 i->config_ppmsubpixels = atof(value);
2046 } else if(!strcmp(name, "subpixels")) {
2047 i->config_ppmsubpixels = i->config_jpegsubpixels = atof(value);
2048 } else if(!strcmp(name, "drawonlyshapes")) {
2049 i->config_drawonlyshapes = atoi(value);
2050 } else if(!strcmp(name, "ignoredraworder")) {
2051 i->config_ignoredraworder = atoi(value);
2052 } else if(!strcmp(name, "mark")) {
2053 if(!value || !value[0]) {
2054 if(i->mark) free(i->mark);
2058 i->mark = strdup("...");
2059 for(t=0;t<3;t++) if(value[t]) i->mark[t] = value[t];
2061 } else if(!strcmp(name, "filloverlap")) {
2062 i->config_filloverlap = atoi(value);
2063 } else if(!strcmp(name, "linksopennewwindow")) {
2064 i->config_opennewwindow = atoi(value);
2065 } else if(!strcmp(name, "opennewwindow")) {
2066 i->config_opennewwindow = atoi(value);
2067 } else if(!strcmp(name, "storeallcharacters")) {
2068 i->config_storeallcharacters = atoi(value);
2069 } else if(!strcmp(name, "enablezlib")) {
2070 i->config_enablezlib = atoi(value);
2071 } else if(!strcmp(name, "bboxvars")) {
2072 i->config_bboxvars = atoi(value);
2073 } else if(!strcmp(name, "dots")) {
2074 i->config_dots = atoi(value);
2075 } else if(!strcmp(name, "frameresets")) {
2076 i->config_frameresets = atoi(value);
2077 } else if(!strcmp(name, "showclipshapes")) {
2078 i->config_showclipshapes = atoi(value);
2079 } else if(!strcmp(name, "reordertags")) {
2080 i->config_reordertags = atoi(value);
2081 } else if(!strcmp(name, "internallinkfunction")) {
2082 i->config_internallinkfunction = strdup(value);
2083 } else if(!strcmp(name, "externallinkfunction")) {
2084 i->config_externallinkfunction = strdup(value);
2085 } else if(!strcmp(name, "linkfunction")) { //sets both internallinkfunction and externallinkfunction
2086 i->config_internallinkfunction = strdup(value);
2087 i->config_externallinkfunction = strdup(value);
2088 } else if(!strcmp(name, "disable_polygon_conversion")) {
2089 i->config_disable_polygon_conversion = atoi(value);
2090 } else if(!strcmp(name, "normalize_polygon_positions")) {
2091 i->config_normalize_polygon_positions = atoi(value);
2092 } else if(!strcmp(name, "wxwindowparams")) {
2093 i->config_watermark = atoi(value);
2094 } else if(!strcmp(name, "insertstop")) {
2095 i->config_insertstoptag = atoi(value);
2096 } else if(!strcmp(name, "protect")) {
2097 i->config_protect = atoi(value);
2098 if(i->config_protect && i->tag) {
2099 i->tag = swf_InsertTag(i->tag, ST_PROTECT);
2101 } else if(!strcmp(name, "flashversion")) {
2102 i->config_flashversion = atoi(value);
2104 i->swf->fileVersion = i->config_flashversion;
2106 } else if(!strcmp(name, "framerate")) {
2107 i->config_framerate = atof(value);
2109 i->swf->frameRate = i->config_framerate*0x100;
2111 } else if(!strcmp(name, "minlinewidth")) {
2112 i->config_minlinewidth = atof(value);
2113 } else if(!strcmp(name, "caplinewidth")) {
2114 i->config_caplinewidth = atof(value);
2115 } else if(!strcmp(name, "linktarget")) {
2116 i->config_linktarget = strdup(value);
2117 } else if(!strcmp(name, "invisibletexttofront")) {
2118 i->config_invisibletexttofront = atoi(value);
2119 } else if(!strcmp(name, "noclips")) {
2120 i->config_noclips = atoi(value);
2121 } else if(!strcmp(name, "dumpfonts")) {
2122 i->config_dumpfonts = atoi(value);
2123 } else if(!strcmp(name, "animate")) {
2124 i->config_animate = atoi(value);
2125 } else if(!strcmp(name, "linknameurl")) {
2126 i->config_linknameurl = atoi(value);
2127 } else if(!strcmp(name, "showimages")) {
2128 i->config_showimages = atoi(value);
2129 } else if(!strcmp(name, "disablelinks")) {
2130 i->config_disablelinks = atoi(value);
2131 } else if(!strcmp(name, "simpleviewer")) {
2132 i->config_simpleviewer = atoi(value);
2133 } else if(!strcmp(name, "next_bitmap_is_jpeg")) {
2135 } else if(!strcmp(name, "jpegquality")) {
2136 int val = atoi(value);
2138 if(val>101) val=101;
2139 i->config_jpegquality = val;
2140 } else if(!strcmp(name, "splinequality")) {
2141 int v = atoi(value);
2142 v = 500-(v*5); // 100% = 0.25 pixel, 0% = 25 pixel
2144 i->config_splinemaxerror = v;
2145 } else if(!strcmp(name, "fontquality")) {
2146 int v = atoi(value);
2147 v = 500-(v*5); // 100% = 0.25 pixel, 0% = 25 pixel
2149 i->config_fontsplinemaxerror = v;
2150 } else if(!strcmp(name, "linkcolor")) {
2151 if(strlen(value)!=8) {
2152 fprintf(stderr, "Unknown format for option 'linkcolor'. (%s <-> RRGGBBAA)\n", value);
2155 # define NIBBLE(s) (((s)>='0' && (s)<='9')?((s)-'0'):((s)&0x0f)+9)
2156 i->config_linkcolor.r = NIBBLE(value[0])<<4 | NIBBLE(value[1]);
2157 i->config_linkcolor.g = NIBBLE(value[2])<<4 | NIBBLE(value[3]);
2158 i->config_linkcolor.b = NIBBLE(value[4])<<4 | NIBBLE(value[5]);
2159 i->config_linkcolor.a = NIBBLE(value[6])<<4 | NIBBLE(value[7]);
2160 } else if(!strcmp(name, "help")) {
2161 printf("\nSWF layer options:\n");
2162 printf("jpegsubpixels=<pixels> resolution adjustment for jpeg images (same as jpegdpi, but in pixels)\n");
2163 printf("ppmsubpixels=<pixels resolution adjustment for lossless images (same as ppmdpi, but in pixels)\n");
2164 printf("subpixels=<pixels> shortcut for setting both jpegsubpixels and ppmsubpixels\n");
2165 printf("drawonlyshapes convert everything to shapes (currently broken)\n");
2166 printf("ignoredraworder allow to perform a few optimizations for creating smaller SWFs\n");
2167 printf("linksopennewwindow make links open a new browser window\n");
2168 printf("linktarget target window name of new links\n");
2169 printf("linkcolor=<color) color of links (format: RRGGBBAA)\n");
2170 printf("linknameurl Link buttons will be named like the URL they refer to (handy for iterating through links with actionscript)\n");
2171 printf("storeallcharacters don't reduce the fonts to used characters in the output file\n");
2172 printf("enablezlib switch on zlib compression (also done if flashversion>=6)\n");
2173 printf("bboxvars store the bounding box of the SWF file in actionscript variables\n");
2174 printf("dots Take care to handle dots correctly\n");
2175 printf("reordertags=0/1 (default: 1) perform some tag optimizations\n");
2176 printf("internallinkfunction=<name> when the user clicks a internal link (to a different page) in the converted file, this actionscript function is called\n");
2177 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");
2178 printf("disable_polygon_conversion never convert strokes to polygons (will remove capstyles and joint styles)\n");
2179 printf("caplinewidth=<width> the minimum thichness a line needs to have so that capstyles become visible (and are converted)\n");
2180 printf("insertstop put an ActionScript \"STOP\" tag in every frame\n");
2181 printf("protect add a \"protect\" tag to the file, to prevent loading in the Flash editor\n");
2182 printf("flashversion=<version> the SWF fileversion (6)\n");
2183 printf("framerate=<fps> SWF framerate\n");
2184 printf("minlinewidth=<width> convert horizontal/vertical boxes smaller than this width to lines (0.05) \n");
2185 printf("simpleviewer Add next/previous buttons to the SWF\n");
2186 printf("animate insert a showframe tag after each placeobject (animate draw order of PDF files)\n");
2187 printf("jpegquality=<quality> set compression quality of jpeg images\n");
2188 printf("splinequality=<value> Set the quality of spline convertion to value (0-100, default: 100).\n");
2189 printf("disablelinks Disable links.\n");
2196 // --------------------------------------------------------------------
2198 static CXFORM gfxcxform_to_cxform(gfxcxform_t* c)
2201 swf_GetCXForm(0, &cx, 1);
2204 if(c->rg!=0 || c->rb!=0 || c->ra!=0 ||
2205 c->gr!=0 || c->gb!=0 || c->ga!=0 ||
2206 c->br!=0 || c->bg!=0 || c->ba!=0 ||
2207 c->ar!=0 || c->ag!=0 || c->ab!=0)
2208 msg("<warning> CXForm not SWF-compatible");
2210 cx.a0 = (S16)(c->aa*256);
2211 cx.r0 = (S16)(c->rr*256);
2212 cx.g0 = (S16)(c->gg*256);
2213 cx.b0 = (S16)(c->bb*256);
2222 static int imageInCache(gfxdevice_t*dev, void*data, int width, int height)
2226 static void addImageToCache(gfxdevice_t*dev, void*data, int width, int height)
2230 static int add_image(swfoutput_internal*i, gfximage_t*img, int targetwidth, int targetheight, int* newwidth, int* newheight)
2232 gfxdevice_t*dev = i->dev;
2234 RGBA*mem = (RGBA*)img->data;
2236 int sizex = img->width;
2237 int sizey = img->height;
2238 int is_jpeg = i->jpeg;
2241 int newsizex=sizex, newsizey=sizey;
2244 if(is_jpeg && i->config_jpegsubpixels) {
2245 newsizex = (int)(targetwidth*i->config_jpegsubpixels + 0.5);
2246 newsizey = (int)(targetheight*i->config_jpegsubpixels + 0.5);
2247 } else if(!is_jpeg && i->config_ppmsubpixels) {
2248 newsizex = (int)(targetwidth*i->config_ppmsubpixels + 0.5);
2249 newsizey = (int)(targetheight*i->config_ppmsubpixels + 0.5);
2253 if(sizex<=0 || sizey<=0)
2260 /* TODO: cache images */
2262 if(newsizex<sizex || newsizey<sizey) {
2263 msg("<verbose> Scaling %dx%d image to %dx%d", sizex, sizey, newsizex, newsizey);
2264 gfximage_t*ni = gfximage_rescale(img, newsizex, newsizey);
2265 newpic = (RGBA*)ni->data;
2267 *newwidth = sizex = newsizex;
2268 *newheight = sizey = newsizey;
2271 *newwidth = newsizex = sizex;
2272 *newheight = newsizey = sizey;
2275 int num_colors = swf_ImageGetNumberOfPaletteEntries(mem,sizex,sizey,0);
2276 int has_alpha = swf_ImageHasAlpha(mem,sizex,sizey);
2278 msg("<verbose> Drawing %dx%d %s%simage (id %d) at size %dx%d (%dx%d), %s%d colors",
2280 has_alpha?(has_alpha==2?"semi-transparent ":"transparent "):"",
2281 is_jpeg?"jpeg-":"", i->currentswfid+1,
2283 targetwidth, targetheight,
2284 /*newsizex, newsizey,*/
2285 num_colors>256?">":"", num_colors>256?256:num_colors);
2287 /*RGBA* pal = (RGBA*)rfx_alloc(sizeof(RGBA)*num_colors);
2288 swf_ImageGetNumberOfPaletteEntries(mem,sizex,sizey,pal);
2290 for(t=0;t<num_colors;t++) {
2291 printf("%02x%02x%02x%02x ",
2292 pal[t].r, pal[t].g, pal[t].b, pal[t].a);
2299 int cacheid = imageInCache(dev, mem, sizex, sizey);
2302 bitid = getNewID(dev);
2304 i->tag = swf_AddImage(i->tag, bitid, mem, sizex, sizey, i->config_jpegquality);
2305 addImageToCache(dev, mem, sizex, sizey);
2315 static SRECT gfxline_getSWFbbox(gfxline_t*line)
2317 gfxbbox_t bbox = gfxline_getbbox(line);
2319 r.xmin = (int)(bbox.xmin*20);
2320 r.ymin = (int)(bbox.ymin*20);
2321 r.xmax = (int)(bbox.xmax*20);
2322 r.ymax = (int)(bbox.ymax*20);
2326 int line_is_empty(gfxline_t*line)
2329 if(line->type != gfx_moveTo)
2336 static void swf_fillbitmap(gfxdevice_t*dev, gfxline_t*line, gfximage_t*img, gfxmatrix_t*matrix, gfxcxform_t*cxform)
2338 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
2340 if(line_is_empty(line))
2346 int targetx = (int)(sqrt(matrix->m00*matrix->m00 + matrix->m01*matrix->m01)*img->width);
2347 int targety = (int)(sqrt(matrix->m10*matrix->m10 + matrix->m11*matrix->m11)*img->height);
2349 int newwidth=0,newheight=0;
2350 int bitid = add_image(i, img, targetx, targety, &newwidth, &newheight);
2353 double fx = (double)img->width / (double)newwidth;
2354 double fy = (double)img->height / (double)newheight;
2357 m.sx = (int)(65536*20*matrix->m00*fx); m.r1 = (int)(65536*20*matrix->m10*fy);
2358 m.r0 = (int)(65536*20*matrix->m01*fx); m.sy = (int)(65536*20*matrix->m11*fy);
2359 m.tx = (int)(matrix->tx*20);
2360 m.ty = (int)(matrix->ty*20);
2363 int myshapeid = getNewID(dev);
2364 i->tag = swf_InsertTag(i->tag,ST_DEFINESHAPE);
2366 swf_ShapeNew(&shape);
2367 int fsid = swf_ShapeAddBitmapFillStyle(shape,&m,bitid,1);
2369 if(i->config_showimages) {
2370 RGBA pink = {255,255,0,255};
2371 lsid = swf_ShapeAddLineStyle(shape, 20, &pink);
2373 swf_SetU16(i->tag, myshapeid);
2374 SRECT r = gfxline_getSWFbbox(line);
2375 r = swf_ClipRect(i->pagebbox, r);
2376 swf_SetRect(i->tag,&r);
2377 swf_SetShapeStyles(i->tag,shape);
2378 swf_ShapeCountBits(shape,NULL,NULL);
2379 swf_SetShapeBits(i->tag,shape);
2380 swf_ShapeSetAll(i->tag,shape,UNDEFINED_COORD,UNDEFINED_COORD,lsid,fsid,0);
2381 i->swflastx = i->swflasty = UNDEFINED_COORD;
2382 drawgfxline(dev, line, 1);
2383 swf_ShapeSetEnd(i->tag);
2384 swf_ShapeFree(shape);
2386 msg("<trace> Placing image, shape ID %d, bitmap ID %d", myshapeid, bitid);
2387 i->tag = swf_InsertTag(i->tag,ST_PLACEOBJECT2);
2388 CXFORM cxform2 = gfxcxform_to_cxform(cxform);
2389 swf_ObjectPlace(i->tag,myshapeid,getNewDepth(dev),&i->page_matrix,&cxform2,NULL);
2392 static RGBA col_black = {255,0,0,0};
2394 static void drawoutline(gfxdevice_t*dev, gfxline_t*line)
2396 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
2398 int myshapeid = getNewID(dev);
2399 i->tag = swf_InsertTag(i->tag,ST_DEFINESHAPE3);
2402 swf_ShapeNew(&shape);
2403 int lsid = swf_ShapeAddLineStyle(shape,1,&col_black);
2405 swf_SetU16(i->tag,myshapeid);
2406 SRECT r = gfxline_getSWFbbox(line);
2407 r = swf_ClipRect(i->pagebbox, r);
2408 swf_SetRect(i->tag,&r);
2409 swf_SetShapeStyles(i->tag,shape);
2410 swf_ShapeCountBits(shape,NULL,NULL);
2411 swf_SetShapeBits(i->tag,shape);
2412 swf_ShapeSetAll(i->tag,shape,UNDEFINED_COORD,UNDEFINED_COORD,lsid,0,0);
2413 drawgfxline(dev, line, 1);
2414 swf_ShapeSetEnd(i->tag);
2415 swf_ShapeFree(shape);
2417 i->tag = swf_InsertTag(i->tag,ST_PLACEOBJECT2);
2418 swf_ObjectPlace(i->tag, myshapeid, getNewDepth(dev), 0,0,0);
2421 static void swf_startclip(gfxdevice_t*dev, gfxline_t*line)
2423 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
2424 if(i->config_noclips)
2430 if(i->clippos >= 127)
2432 msg("<warning> Too many clip levels.");
2436 if(i->config_showclipshapes)
2437 drawoutline(dev, line);
2439 int myshapeid = getNewID(dev);
2440 i->tag = swf_InsertTag(i->tag,ST_DEFINESHAPE3);
2442 memset(&col, 0, sizeof(RGBA));
2445 swf_ShapeNew(&shape);
2446 int fsid = swf_ShapeAddSolidFillStyle(shape,&col);
2448 RGBA markcol = {0,i->mark[0],i->mark[1],i->mark[2]};
2449 swf_ShapeAddSolidFillStyle(shape,&markcol);
2451 swf_SetU16(i->tag,myshapeid);
2452 SRECT r = gfxline_getSWFbbox(line);
2453 r = swf_ClipRect(i->pagebbox, r);
2454 swf_SetRect(i->tag,&r);
2455 swf_SetShapeStyles(i->tag,shape);
2456 swf_ShapeCountBits(shape,NULL,NULL);
2457 swf_SetShapeBits(i->tag,shape);
2458 swf_ShapeSetAll(i->tag,shape,UNDEFINED_COORD,UNDEFINED_COORD,0,fsid,0);
2459 i->swflastx = i->swflasty = UNDEFINED_COORD;
2460 i->shapeisempty = 1;
2461 drawgfxline(dev, line, 1);
2462 if(i->shapeisempty) {
2463 /* an empty clip shape is equivalent to a shape with no area */
2464 int x = line?line->x:0;
2465 int y = line?line->y:0;
2466 moveto(dev, i->tag, x,y);
2467 lineto(dev, i->tag, x,y);
2468 lineto(dev, i->tag, x,y);
2470 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)) {
2471 if(i->config_watermark) {
2472 gfxbbox_t r; r.xmin = r.ymin = 0;r.xmax = i->max_x;r.ymax = i->max_y;
2473 draw_watermark(dev, r, 1);
2476 swf_ShapeSetEnd(i->tag);
2477 swf_ShapeFree(shape);
2479 /* TODO: remember the bbox, and check all shapes against it */
2481 msg("<trace> Placing clip ID %d", myshapeid);
2482 i->tag = swf_InsertTag(i->tag,ST_PLACEOBJECT2);
2483 i->cliptags[i->clippos] = i->tag;
2484 i->clipshapes[i->clippos] = myshapeid;
2485 i->clipdepths[i->clippos] = getNewDepth(dev);
2489 static void swf_endclip(gfxdevice_t*dev)
2491 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
2492 if(i->config_noclips)
2500 msg("<error> Invalid end of clipping region");
2504 /*swf_ObjectPlaceClip(i->cliptags[i->clippos],i->clipshapes[i->clippos],i->clipdepths[i->clippos],&i->page_matrix,NULL,NULL,
2505 / * clip to depth: * / i->depth <= i->clipdepths[i->clippos]? i->depth : i->depth - 1);
2507 swf_ObjectPlaceClip(i->cliptags[i->clippos],i->clipshapes[i->clippos],i->clipdepths[i->clippos],&i->page_matrix,NULL,NULL,i->depth);
2509 static int gfxline_type(gfxline_t*line)
2515 int haszerosegments=0;
2518 if(line->type == gfx_moveTo) {
2521 } else if(line->type == gfx_lineTo) {
2525 } else if(line->type == gfx_splineTo) {
2527 if(tmpsplines>lines)
2535 if(lines==0 && splines==0) return 0;
2536 else if(lines==1 && splines==0) return 1;
2537 else if(lines==0 && splines==1) return 2;
2538 else if(splines==0) return 3;
2542 static int gfxline_has_dots(gfxline_t*line)
2550 if(line->type == gfx_moveTo) {
2551 /* test the length of the preceding line, and assume it is a dot if
2552 it's length is less than 1.0. But *only* if there's a noticable
2553 gap between the previous line and the next moveTo. (I've come
2554 across a PDF where thousands of "dots" were stringed together,
2556 int last_short_gap = short_gap;
2557 if((fabs(line->x - x) + fabs(line->y - y)) < 1.0) {
2562 if(isline && dist < 1 && !short_gap && !last_short_gap) {
2567 } else if(line->type == gfx_lineTo) {
2568 dist += fabs(line->x - x) + fabs(line->y - y);
2570 } else if(line->type == gfx_splineTo) {
2571 dist += fabs(line->sx - x) + fabs(line->sy - y) +
2572 fabs(line->x - line->sx) + fabs(line->y - line->sy);
2579 if(isline && dist < 1 && !short_gap) {
2585 static int gfxline_fix_short_edges(gfxline_t*line)
2589 if(line->type == gfx_lineTo) {
2590 if(fabs(line->x - x) + fabs(line->y - y) < 0.01) {
2593 } else if(line->type == gfx_splineTo) {
2594 if(fabs(line->sx - x) + fabs(line->sy - y) +
2595 fabs(line->x - line->sx) + fabs(line->y - line->sy) < 0.01) {
2606 static char is_inside_page(gfxdevice_t*dev, gfxcoord_t x, gfxcoord_t y)
2608 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
2609 if(x<i->min_x || x>i->max_x) return 0;
2610 if(y<i->min_y || y>i->max_y) return 0;
2614 gfxline_t* gfxline_move(gfxline_t*line, double x, double y)
2616 gfxline_t*l = line = gfxline_clone(line);
2628 //#define NORMALIZE_POLYGON_POSITIONS
2630 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)
2632 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
2633 if(line_is_empty(line))
2635 int type = gfxline_type(line);
2636 int has_dots = gfxline_has_dots(line);
2637 gfxbbox_t r = gfxline_getbbox(line);
2638 int is_outside_page = !is_inside_page(dev, r.xmin, r.ymin) || !is_inside_page(dev, r.xmax, r.ymax);
2640 /* TODO: * split line into segments, and perform this check for all segments */
2642 if(i->config_disable_polygon_conversion || /*type>=5 ||*/
2644 (width <= i->config_caplinewidth
2645 || (cap_style == gfx_capRound && joint_style == gfx_joinRound)
2646 || (cap_style == gfx_capRound && type<=2))))
2650 /* convert line to polygon */
2651 msg("<trace> draw as polygon, type=%d dots=%d", type, has_dots);
2653 gfxline_fix_short_edges(line);
2654 /* we need to convert the line into a polygon */
2655 gfxpoly_t* poly = gfxpoly_from_stroke(line, width, cap_style, joint_style, miterLimit, DEFAULT_GRID);
2656 gfxline_t*gfxline = gfxline_from_gfxpoly(poly);
2657 dev->fill(dev, gfxline, color);
2658 gfxline_free(gfxline);
2659 gfxpoly_destroy(poly);
2663 msg("<trace> draw as stroke, type=%d dots=%d", type, has_dots);
2666 if(i->config_normalize_polygon_positions) {
2668 double startx = 0, starty = 0;
2669 if(line && line->type == gfx_moveTo) {
2673 line = gfxline_move(line, -startx, -starty);
2674 i->shapeposx = (int)(startx*20);
2675 i->shapeposy = (int)(starty*20);
2678 swfoutput_setstrokecolor(dev, color->r, color->g, color->b, color->a);
2679 swfoutput_setlinewidth(dev, width);
2682 drawgfxline(dev, line, 0);
2684 if(i->config_normalize_polygon_positions) {
2685 free(line); //account for _move
2690 static void swf_fill(gfxdevice_t*dev, gfxline_t*line, gfxcolor_t*color)
2692 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
2693 if(line_is_empty(line))
2697 gfxbbox_t r = gfxline_getbbox(line);
2698 int is_outside_page = !is_inside_page(dev, r.xmin, r.ymin) || !is_inside_page(dev, r.xmax, r.ymax);
2702 if(!i->config_ignoredraworder)
2705 if(i->config_normalize_polygon_positions) {
2707 double startx = 0, starty = 0;
2708 if(line && line->type == gfx_moveTo) {
2712 line = gfxline_move(line, -startx, -starty);
2713 i->shapeposx = (int)(startx*20);
2714 i->shapeposy = (int)(starty*20);
2717 swfoutput_setfillcolor(dev, color->r, color->g, color->b, color->a);
2720 drawgfxline(dev, line, 1);
2722 if(i->currentswfid==2 && r.xmin==0 && r.ymin==0 && r.xmax==i->max_x && r.ymax==i->max_y) {
2723 if(i->config_watermark) {
2724 draw_watermark(dev, r, 1);
2728 msg("<trace> end of swf_fill (shapeid=%d)", i->shapeid);
2730 if(i->config_normalize_polygon_positions) {
2731 free(line); //account for _move
2735 static GRADIENT* gfxgradient_to_GRADIENT(gfxgradient_t*gradient)
2738 gfxgradient_t*g = gradient;
2743 GRADIENT* swfgradient = malloc(sizeof(GRADIENT));
2744 swfgradient->num = num;
2745 swfgradient->rgba = malloc(sizeof(swfgradient->rgba[0])*num);
2746 swfgradient->ratios = malloc(sizeof(swfgradient->ratios[0])*num);
2751 swfgradient->ratios[num] = g->pos*255;
2752 swfgradient->rgba[num] = *(RGBA*)&g->color;
2759 static void swf_fillgradient(gfxdevice_t*dev, gfxline_t*line, gfxgradient_t*gradient, gfxgradienttype_t type, gfxmatrix_t*matrix)
2761 if(line_is_empty(line))
2763 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
2765 if(line_is_empty(line))
2768 GRADIENT* swfgradient = gfxgradient_to_GRADIENT(gradient);
2775 double f = type==gfxgradient_radial?4:4;
2777 m.sx = (int)(matrix->m00*20*f); m.r1 = (int)(matrix->m10*20*f);
2778 m.r0 = (int)(matrix->m01*20*f); m.sy = (int)(matrix->m11*20*f);
2779 m.tx = (int)(matrix->tx*20);
2780 m.ty = (int)(matrix->ty*20);
2783 int myshapeid = getNewID(dev);
2784 i->tag = swf_InsertTag(i->tag, ST_DEFINESHAPE2);
2786 swf_ShapeNew(&shape);
2787 int fsid = swf_ShapeAddGradientFillStyle(shape,&m,swfgradient,type==gfxgradient_radial);
2788 swf_SetU16(i->tag, myshapeid);
2789 SRECT r = gfxline_getSWFbbox(line);
2790 r = swf_ClipRect(i->pagebbox, r);
2791 swf_SetRect(i->tag,&r);
2792 swf_SetShapeStyles(i->tag,shape);
2793 swf_ShapeCountBits(shape,NULL,NULL);
2794 swf_SetShapeBits(i->tag,shape);
2795 swf_ShapeSetAll(i->tag,shape,UNDEFINED_COORD,UNDEFINED_COORD,0,fsid,0);
2796 i->swflastx = i->swflasty = UNDEFINED_COORD;
2797 drawgfxline(dev, line, 1);
2798 swf_ShapeSetEnd(i->tag);
2799 swf_ShapeFree(shape);
2801 int depth = getNewDepth(dev);
2802 msg("<trace> Placing gradient, shape ID %d, depth %d", myshapeid, depth);
2803 i->tag = swf_InsertTag(i->tag,ST_PLACEOBJECT2);
2804 swf_ObjectPlace(i->tag,myshapeid,depth,&i->page_matrix,NULL,NULL);
2806 swf_FreeGradient(swfgradient);free(swfgradient);
2809 static SWFFONT* gfxfont_to_swffont(gfxfont_t*font, const char* id, int version)
2811 SWFFONT*swffont = (SWFFONT*)rfx_calloc(sizeof(SWFFONT));
2813 SRECT bounds = {0,0,0,0};
2815 swffont->version = version;
2816 swffont->name = (U8*)strdup(id);
2817 swffont->layout = (SWFLAYOUT*)rfx_calloc(sizeof(SWFLAYOUT));
2818 swffont->layout->ascent = 0;
2819 swffont->layout->descent = 0;
2820 swffont->layout->leading = 0;
2821 swffont->layout->bounds = (SRECT*)rfx_calloc(sizeof(SRECT)*font->num_glyphs);
2822 swffont->encoding = FONT_ENCODING_UNICODE;
2823 swffont->numchars = font->num_glyphs;
2824 swffont->maxascii = font->max_unicode;
2825 swffont->ascii2glyph = (int*)rfx_calloc(sizeof(int)*swffont->maxascii);
2826 swffont->glyph2ascii = (U16*)rfx_calloc(sizeof(U16)*swffont->numchars);
2827 swffont->glyph = (SWFGLYPH*)rfx_calloc(sizeof(SWFGLYPH)*swffont->numchars);
2828 swffont->glyphnames = (char**)rfx_calloc(sizeof(char*)*swffont->numchars);
2830 SRECT max = {0,0,0,0};
2831 for(t=0;t<font->num_glyphs;t++) {
2835 int u = font->glyphs[t].unicode;
2838 for(s=0;s<font->num_glyphs;s++) {
2839 if(swffont->glyph2ascii[s]==u)
2842 if(u >= 0xe000 || u == 0x0000 || twice) {
2843 /* flash 8 flashtype requires unique unicode IDs for each character.
2844 We use the Unicode private user area to assign characters, hoping that
2845 the font doesn't contain more than 2048 glyphs */
2846 u = 0xe000 + (t&0x1fff);
2848 swffont->glyph2ascii[t] = u;
2850 if(font->glyphs[t].name) {
2851 swffont->glyphnames[t] = strdup(font->glyphs[t].name);
2853 swffont->glyphnames[t] = 0;
2855 advance = font->glyphs[t].advance;
2857 swf_Shape01DrawerInit(&draw, 0);
2858 line = font->glyphs[t].line;
2860 const double scale = GLYPH_SCALE;
2863 c.x = line->sx * scale; c.y = -line->sy * scale;
2864 //to.x = floor(line->x * scale); to.y = floor(-line->y * scale);
2865 to.x = line->x * scale; to.y = -line->y * scale;
2867 /*if(strstr(swffont->name, "BIRNU") && t==90) {
2871 if(line->type == gfx_moveTo) {
2872 draw.moveTo(&draw, &to);
2873 } else if(line->type == gfx_lineTo) {
2874 draw.lineTo(&draw, &to);
2875 } else if(line->type == gfx_splineTo) {
2876 draw.splineTo(&draw, &c, &to);
2881 swffont->glyph[t].shape = swf_ShapeDrawerToShape(&draw);
2883 SRECT bbox = swf_ShapeDrawerGetBBox(&draw);
2884 swf_ExpandRect2(&max, &bbox);
2886 swffont->layout->bounds[t] = bbox;
2888 if(advance<32768.0/20) {
2889 swffont->glyph[t].advance = (int)(advance*20);
2891 //msg("<warning> Advance value overflow in glyph %d", t);
2892 swffont->glyph[t].advance = 32767;
2895 draw.dealloc(&draw);
2897 swf_ExpandRect2(&bounds, &swffont->layout->bounds[t]);
2900 for(t=0;t<font->num_glyphs;t++) {
2901 SRECT bbox = swffont->layout->bounds[t];
2903 /* if the glyph doesn't have a bounding box, use the
2904 combined bounding box (necessary e.g. for space characters) */
2905 if(!(bbox.xmin|bbox.ymin|bbox.xmax|bbox.ymax)) {
2906 swffont->layout->bounds[t] = bbox = max;
2909 /* check that the advance value is reasonable, by comparing it
2910 with the bounding box */
2911 if(bbox.xmax>0 && (bbox.xmax*10 < swffont->glyph[t].advance || !swffont->glyph[t].advance)) {
2912 if(swffont->glyph[t].advance)
2913 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);
2914 swffont->glyph[t].advance = bbox.xmax;
2916 //swffont->glyph[t].advance = bbox.xmax - bbox.xmin;
2920 /* Flash player will use the advance value from the char, and the ascent/descent values
2921 from the layout for text selection.
2922 ascent will extend the char into negative y direction, from the baseline, while descent
2923 will extend in positive y direction, also from the baseline.
2924 The baseline is defined as the y-position zero
2927 swffont->layout->ascent = bounds.ymin<0?-bounds.ymin:0;
2928 swffont->layout->descent = bounds.ymax>0?bounds.ymax:0;
2929 swffont->layout->leading = bounds.ymax - bounds.ymin;
2931 /* if the font has proper ascent/descent values (>0) and those define
2932 greater line spacing that what we estimated from the bounding boxes,
2933 use the font's parameters */
2934 if(font->ascent*20 > swffont->layout->ascent)
2935 swffont->layout->ascent = font->ascent*20;
2936 if(font->descent*20 > swffont->layout->descent)
2937 swffont->layout->descent = font->descent*20;
2939 swf_FontSort(swffont);
2943 static void swf_addfont(gfxdevice_t*dev, gfxfont_t*font)
2945 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
2947 if(i->swffont && i->swffont->name && !strcmp((char*)i->swffont->name,font->id))
2948 return; // the requested font is the current font
2950 fontlist_t*last=0,*l = i->fontlist;
2953 if(!strcmp((char*)l->swffont->name, font->id)) {
2954 return; // we already know this font
2958 l = (fontlist_t*)rfx_calloc(sizeof(fontlist_t));
2959 l->swffont = gfxfont_to_swffont(font, font->id, (i->config_flashversion>=8 && !NO_FONT3)?3:2);
2966 swf_FontSetID(l->swffont, getNewID(i->dev));
2968 if(getScreenLogLevel() >= LOGLEVEL_DEBUG) {
2970 // print font information
2971 msg("<debug> Font %s",font->id);
2972 msg("<debug> | ID: %d", l->swffont->id);
2973 msg("<debug> | Version: %d", l->swffont->version);
2974 msg("<debug> | Name: %s", l->swffont->name);
2975 msg("<debug> | Numchars: %d", l->swffont->numchars);
2976 msg("<debug> | Maxascii: %d", l->swffont->maxascii);
2977 msg("<debug> | Style: %d", l->swffont->style);
2978 msg("<debug> | Encoding: %d", l->swffont->encoding);
2979 for(iii=0; iii<l->swffont->numchars;iii++) {
2980 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,
2981 l->swffont->layout->bounds[iii].xmin/20.0,
2982 l->swffont->layout->bounds[iii].ymin/20.0,
2983 l->swffont->layout->bounds[iii].xmax/20.0,
2984 l->swffont->layout->bounds[iii].ymax/20.0
2990 static void swf_switchfont(gfxdevice_t*dev, const char*fontid)
2992 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
2994 if(i->swffont && i->swffont->name && !strcmp((char*)i->swffont->name,fontid))
2995 return; // the requested font is the current font
2997 fontlist_t*l = i->fontlist;
2999 if(!strcmp((char*)l->swffont->name, fontid)) {
3000 i->swffont = l->swffont;
3005 msg("<error> Unknown font id: %s", fontid);
3009 /* sets the matrix which is to be applied to characters drawn by swfoutput_drawchar() */
3010 static void setfontscale(gfxdevice_t*dev,double m11,double m12, double m21,double m22,double x, double y, char force)
3016 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
3017 if(i->lastfontm11 == m11 &&
3018 i->lastfontm12 == m12 &&
3019 i->lastfontm21 == m21 &&
3020 i->lastfontm22 == m22 && !force)
3025 i->lastfontm11 = m11;
3026 i->lastfontm12 = m12;
3027 i->lastfontm21 = m21;
3028 i->lastfontm22 = m22;
3030 double xsize = sqrt(m11*m11 + m12*m12);
3031 double ysize = sqrt(m21*m21 + m22*m22);
3034 if(i->config_flashversion>=8 && !NO_FONT3)
3037 i->current_font_size = (xsize>ysize?xsize:ysize)*extrazoom;
3038 if(i->current_font_size < 1)
3039 i->current_font_size = 1;
3042 swf_GetMatrix(0, &m);
3044 if(m21 || m12 || fabs(m11+m22)>0.001) {
3045 double ifs = (double)extrazoom/(i->current_font_size);
3046 m.sx = (S32)((m11*ifs)*65536); m.r1 = -(S32)((m21*ifs)*65536);
3047 m.r0 = (S32)((m12*ifs)*65536); m.sy = -(S32)((m22*ifs)*65536);
3050 /* this is the position of the first char to set a new fontmatrix-
3051 we hope that it's close enough to all other characters using the
3052 font, so we use its position as origin for the matrix */
3059 static void swf_drawchar(gfxdevice_t*dev, gfxfont_t*font, int glyph, gfxcolor_t*color, gfxmatrix_t*matrix)
3061 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
3063 msg("<error> swf_drawchar called (glyph %d) without font", glyph);
3067 if(i->config_drawonlyshapes) {
3068 gfxglyph_t*g = &font->glyphs[glyph];
3069 gfxline_t*line2 = gfxline_clone(g->line);
3070 gfxline_transform(line2, matrix);
3071 dev->fill(dev, line2, color);
3072 gfxline_free(line2);
3076 if(!i->swffont || !i->swffont->name || strcmp((char*)i->swffont->name,font->id)) // not equal to current font
3078 swf_switchfont(dev, font->id); // set the current font
3082 msg("<warning> swf_drawchar: Font is NULL");
3085 if(glyph<0 || glyph>=i->swffont->numchars) {
3086 msg("<warning> No character %d in font %s (%d chars)", glyph, FIXNULL((char*)i->swffont->name), i->swffont->numchars);
3089 glyph = i->swffont->glyph2glyph[glyph];
3091 setfontscale(dev, matrix->m00, matrix->m01, matrix->m10, matrix->m11, matrix->tx, matrix->ty, 0);
3093 double det = i->fontmatrix.sx/65536.0 * i->fontmatrix.sy/65536.0 -
3094 i->fontmatrix.r0/65536.0 * i->fontmatrix.r1/65536.0;
3095 if(fabs(det) < 0.0005) {
3096 /* x direction equals y direction- the text is invisible */
3097 msg("<verbose> Not drawing invisible character %d (det=%f, m=[%f %f;%f %f]\n", glyph,
3099 i->fontmatrix.sx/65536.0, i->fontmatrix.r1/65536.0,
3100 i->fontmatrix.r0/65536.0, i->fontmatrix.sy/65536.0);
3104 /*if(i->swffont->glyph[glyph].shape->bitlen <= 16) {
3105 msg("<warning> Glyph %d in current charset (%s, %d characters) is empty",
3106 glyph, FIXNULL((char*)i->swffont->name), i->swffont->numchars);
3110 /* calculate character position with respect to the current font matrix */
3111 double s = 20 * GLYPH_SCALE / det;
3112 double px = matrix->tx - i->fontmatrix.tx/20.0;
3113 double py = matrix->ty - i->fontmatrix.ty/20.0;
3114 int x = (SCOORD)(( px * i->fontmatrix.sy/65536.0 - py * i->fontmatrix.r1/65536.0)*s);
3115 int y = (SCOORD)((- px * i->fontmatrix.r0/65536.0 + py * i->fontmatrix.sx/65536.0)*s);
3116 if(x>32767 || x<-32768 || y>32767 || y<-32768) {
3117 msg("<verbose> Moving character origin to %f %f\n", matrix->tx, matrix->ty);
3119 setfontscale(dev, matrix->m00, matrix->m01, matrix->m10, matrix->m11, matrix->tx, matrix->ty, 1);
3120 /* since we just moved the char origin to the current char's position,
3121 it now has the relative position (0,0) */
3130 msg("<trace> Drawing char %d in font %d at %d,%d in color %02x%02x%02x%02x",
3131 glyph, i->swffont->id, x, y, color->r, color->g, color->b, color->a);
3133 if(color->a == 0 && i->config_invisibletexttofront) {
3134 RGBA color2 = *(RGBA*)color;
3135 if(i->config_flashversion>=8) {
3136 // use "multiply" blend mode
3137 color2.a = color2.r = color2.g = color2.b = 255;
3139 i->topchardata = charbuffer_append(i->topchardata, i->swffont, glyph, x, y, i->current_font_size, color2, &i->fontmatrix);
3141 i->chardata = charbuffer_append(i->chardata, i->swffont, glyph, x, y, i->current_font_size, *(RGBA*)color, &i->fontmatrix);
3143 swf_FontUseGlyph(i->swffont, glyph, i->current_font_size);