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, "showimages")) {
2125 i->config_showimages = atoi(value);
2126 } else if(!strcmp(name, "disablelinks")) {
2127 i->config_disablelinks = atoi(value);
2128 } else if(!strcmp(name, "simpleviewer")) {
2129 i->config_simpleviewer = atoi(value);
2130 } else if(!strcmp(name, "next_bitmap_is_jpeg")) {
2132 } else if(!strcmp(name, "jpegquality")) {
2133 int val = atoi(value);
2135 if(val>101) val=101;
2136 i->config_jpegquality = val;
2137 } else if(!strcmp(name, "splinequality")) {
2138 int v = atoi(value);
2139 v = 500-(v*5); // 100% = 0.25 pixel, 0% = 25 pixel
2141 i->config_splinemaxerror = v;
2142 } else if(!strcmp(name, "fontquality")) {
2143 int v = atoi(value);
2144 v = 500-(v*5); // 100% = 0.25 pixel, 0% = 25 pixel
2146 i->config_fontsplinemaxerror = v;
2147 } else if(!strcmp(name, "linkcolor")) {
2148 if(strlen(value)!=8) {
2149 fprintf(stderr, "Unknown format for option 'linkcolor'. (%s <-> RRGGBBAA)\n", value);
2152 # define NIBBLE(s) (((s)>='0' && (s)<='9')?((s)-'0'):((s)&0x0f)+9)
2153 i->config_linkcolor.r = NIBBLE(value[0])<<4 | NIBBLE(value[1]);
2154 i->config_linkcolor.g = NIBBLE(value[2])<<4 | NIBBLE(value[3]);
2155 i->config_linkcolor.b = NIBBLE(value[4])<<4 | NIBBLE(value[5]);
2156 i->config_linkcolor.a = NIBBLE(value[6])<<4 | NIBBLE(value[7]);
2157 } else if(!strcmp(name, "help")) {
2158 printf("\nSWF layer options:\n");
2159 printf("jpegsubpixels=<pixels> resolution adjustment for jpeg images (same as jpegdpi, but in pixels)\n");
2160 printf("ppmsubpixels=<pixels resolution adjustment for lossless images (same as ppmdpi, but in pixels)\n");
2161 printf("subpixels=<pixels> shortcut for setting both jpegsubpixels and ppmsubpixels\n");
2162 printf("drawonlyshapes convert everything to shapes (currently broken)\n");
2163 printf("ignoredraworder allow to perform a few optimizations for creating smaller SWFs\n");
2164 printf("linksopennewwindow make links open a new browser window\n");
2165 printf("linktarget target window name of new links\n");
2166 printf("linkcolor=<color) color of links (format: RRGGBBAA)\n");
2167 printf("linknameurl Link buttons will be named like the URL they refer to (handy for iterating through links with actionscript)\n");
2168 printf("storeallcharacters don't reduce the fonts to used characters in the output file\n");
2169 printf("enablezlib switch on zlib compression (also done if flashversion>=6)\n");
2170 printf("bboxvars store the bounding box of the SWF file in actionscript variables\n");
2171 printf("dots Take care to handle dots correctly\n");
2172 printf("reordertags=0/1 (default: 1) perform some tag optimizations\n");
2173 printf("internallinkfunction=<name> when the user clicks a internal link (to a different page) in the converted file, this actionscript function is called\n");
2174 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");
2175 printf("disable_polygon_conversion never convert strokes to polygons (will remove capstyles and joint styles)\n");
2176 printf("caplinewidth=<width> the minimum thichness a line needs to have so that capstyles become visible (and are converted)\n");
2177 printf("insertstop put an ActionScript \"STOP\" tag in every frame\n");
2178 printf("protect add a \"protect\" tag to the file, to prevent loading in the Flash editor\n");
2179 printf("flashversion=<version> the SWF fileversion (6)\n");
2180 printf("framerate=<fps> SWF framerate\n");
2181 printf("minlinewidth=<width> convert horizontal/vertical boxes smaller than this width to lines (0.05) \n");
2182 printf("simpleviewer Add next/previous buttons to the SWF\n");
2183 printf("animate insert a showframe tag after each placeobject (animate draw order of PDF files)\n");
2184 printf("jpegquality=<quality> set compression quality of jpeg images\n");
2185 printf("splinequality=<value> Set the quality of spline convertion to value (0-100, default: 100).\n");
2186 printf("disablelinks Disable links.\n");
2193 // --------------------------------------------------------------------
2195 static CXFORM gfxcxform_to_cxform(gfxcxform_t* c)
2198 swf_GetCXForm(0, &cx, 1);
2201 if(c->rg!=0 || c->rb!=0 || c->ra!=0 ||
2202 c->gr!=0 || c->gb!=0 || c->ga!=0 ||
2203 c->br!=0 || c->bg!=0 || c->ba!=0 ||
2204 c->ar!=0 || c->ag!=0 || c->ab!=0)
2205 msg("<warning> CXForm not SWF-compatible");
2207 cx.a0 = (S16)(c->aa*256);
2208 cx.r0 = (S16)(c->rr*256);
2209 cx.g0 = (S16)(c->gg*256);
2210 cx.b0 = (S16)(c->bb*256);
2219 static int imageInCache(gfxdevice_t*dev, void*data, int width, int height)
2223 static void addImageToCache(gfxdevice_t*dev, void*data, int width, int height)
2227 static int add_image(swfoutput_internal*i, gfximage_t*img, int targetwidth, int targetheight, int* newwidth, int* newheight)
2229 gfxdevice_t*dev = i->dev;
2231 RGBA*mem = (RGBA*)img->data;
2233 int sizex = img->width;
2234 int sizey = img->height;
2235 int is_jpeg = i->jpeg;
2238 int newsizex=sizex, newsizey=sizey;
2241 if(is_jpeg && i->config_jpegsubpixels) {
2242 newsizex = (int)(targetwidth*i->config_jpegsubpixels + 0.5);
2243 newsizey = (int)(targetheight*i->config_jpegsubpixels + 0.5);
2244 } else if(!is_jpeg && i->config_ppmsubpixels) {
2245 newsizex = (int)(targetwidth*i->config_ppmsubpixels + 0.5);
2246 newsizey = (int)(targetheight*i->config_ppmsubpixels + 0.5);
2250 if(sizex<=0 || sizey<=0)
2257 /* TODO: cache images */
2259 if(newsizex<sizex || newsizey<sizey) {
2260 msg("<verbose> Scaling %dx%d image to %dx%d", sizex, sizey, newsizex, newsizey);
2261 gfximage_t*ni = gfximage_rescale(img, newsizex, newsizey);
2262 newpic = (RGBA*)ni->data;
2264 *newwidth = sizex = newsizex;
2265 *newheight = sizey = newsizey;
2268 *newwidth = newsizex = sizex;
2269 *newheight = newsizey = sizey;
2272 int num_colors = swf_ImageGetNumberOfPaletteEntries(mem,sizex,sizey,0);
2273 int has_alpha = swf_ImageHasAlpha(mem,sizex,sizey);
2275 msg("<verbose> Drawing %dx%d %s%simage (id %d) at size %dx%d (%dx%d), %s%d colors",
2277 has_alpha?(has_alpha==2?"semi-transparent ":"transparent "):"",
2278 is_jpeg?"jpeg-":"", i->currentswfid+1,
2280 targetwidth, targetheight,
2281 /*newsizex, newsizey,*/
2282 num_colors>256?">":"", num_colors>256?256:num_colors);
2284 /*RGBA* pal = (RGBA*)rfx_alloc(sizeof(RGBA)*num_colors);
2285 swf_ImageGetNumberOfPaletteEntries(mem,sizex,sizey,pal);
2287 for(t=0;t<num_colors;t++) {
2288 printf("%02x%02x%02x%02x ",
2289 pal[t].r, pal[t].g, pal[t].b, pal[t].a);
2296 int cacheid = imageInCache(dev, mem, sizex, sizey);
2299 bitid = getNewID(dev);
2301 i->tag = swf_AddImage(i->tag, bitid, mem, sizex, sizey, i->config_jpegquality);
2302 addImageToCache(dev, mem, sizex, sizey);
2312 static SRECT gfxline_getSWFbbox(gfxline_t*line)
2314 gfxbbox_t bbox = gfxline_getbbox(line);
2316 r.xmin = (int)(bbox.xmin*20);
2317 r.ymin = (int)(bbox.ymin*20);
2318 r.xmax = (int)(bbox.xmax*20);
2319 r.ymax = (int)(bbox.ymax*20);
2323 int line_is_empty(gfxline_t*line)
2326 if(line->type != gfx_moveTo)
2333 static void swf_fillbitmap(gfxdevice_t*dev, gfxline_t*line, gfximage_t*img, gfxmatrix_t*matrix, gfxcxform_t*cxform)
2335 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
2337 if(line_is_empty(line))
2343 int targetx = (int)(sqrt(matrix->m00*matrix->m00 + matrix->m01*matrix->m01)*img->width);
2344 int targety = (int)(sqrt(matrix->m10*matrix->m10 + matrix->m11*matrix->m11)*img->height);
2346 int newwidth=0,newheight=0;
2347 int bitid = add_image(i, img, targetx, targety, &newwidth, &newheight);
2350 double fx = (double)img->width / (double)newwidth;
2351 double fy = (double)img->height / (double)newheight;
2354 m.sx = (int)(65536*20*matrix->m00*fx); m.r1 = (int)(65536*20*matrix->m10*fy);
2355 m.r0 = (int)(65536*20*matrix->m01*fx); m.sy = (int)(65536*20*matrix->m11*fy);
2356 m.tx = (int)(matrix->tx*20);
2357 m.ty = (int)(matrix->ty*20);
2360 int myshapeid = getNewID(dev);
2361 i->tag = swf_InsertTag(i->tag,ST_DEFINESHAPE);
2363 swf_ShapeNew(&shape);
2364 int fsid = swf_ShapeAddBitmapFillStyle(shape,&m,bitid,1);
2366 if(i->config_showimages) {
2367 RGBA pink = {255,255,0,255};
2368 lsid = swf_ShapeAddLineStyle(shape, 20, &pink);
2370 swf_SetU16(i->tag, myshapeid);
2371 SRECT r = gfxline_getSWFbbox(line);
2372 r = swf_ClipRect(i->pagebbox, r);
2373 swf_SetRect(i->tag,&r);
2374 swf_SetShapeStyles(i->tag,shape);
2375 swf_ShapeCountBits(shape,NULL,NULL);
2376 swf_SetShapeBits(i->tag,shape);
2377 swf_ShapeSetAll(i->tag,shape,UNDEFINED_COORD,UNDEFINED_COORD,lsid,fsid,0);
2378 i->swflastx = i->swflasty = UNDEFINED_COORD;
2379 drawgfxline(dev, line, 1);
2380 swf_ShapeSetEnd(i->tag);
2381 swf_ShapeFree(shape);
2383 msg("<trace> Placing image, shape ID %d, bitmap ID %d", myshapeid, bitid);
2384 i->tag = swf_InsertTag(i->tag,ST_PLACEOBJECT2);
2385 CXFORM cxform2 = gfxcxform_to_cxform(cxform);
2386 swf_ObjectPlace(i->tag,myshapeid,getNewDepth(dev),&i->page_matrix,&cxform2,NULL);
2389 static RGBA col_black = {255,0,0,0};
2391 static void drawoutline(gfxdevice_t*dev, gfxline_t*line)
2393 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
2395 int myshapeid = getNewID(dev);
2396 i->tag = swf_InsertTag(i->tag,ST_DEFINESHAPE3);
2399 swf_ShapeNew(&shape);
2400 int lsid = swf_ShapeAddLineStyle(shape,1,&col_black);
2402 swf_SetU16(i->tag,myshapeid);
2403 SRECT r = gfxline_getSWFbbox(line);
2404 r = swf_ClipRect(i->pagebbox, r);
2405 swf_SetRect(i->tag,&r);
2406 swf_SetShapeStyles(i->tag,shape);
2407 swf_ShapeCountBits(shape,NULL,NULL);
2408 swf_SetShapeBits(i->tag,shape);
2409 swf_ShapeSetAll(i->tag,shape,UNDEFINED_COORD,UNDEFINED_COORD,lsid,0,0);
2410 drawgfxline(dev, line, 1);
2411 swf_ShapeSetEnd(i->tag);
2412 swf_ShapeFree(shape);
2414 i->tag = swf_InsertTag(i->tag,ST_PLACEOBJECT2);
2415 swf_ObjectPlace(i->tag, myshapeid, getNewDepth(dev), 0,0,0);
2418 static void swf_startclip(gfxdevice_t*dev, gfxline_t*line)
2420 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
2421 if(i->config_noclips)
2427 if(i->clippos >= 127)
2429 msg("<warning> Too many clip levels.");
2433 if(i->config_showclipshapes)
2434 drawoutline(dev, line);
2436 int myshapeid = getNewID(dev);
2437 i->tag = swf_InsertTag(i->tag,ST_DEFINESHAPE3);
2439 memset(&col, 0, sizeof(RGBA));
2442 swf_ShapeNew(&shape);
2443 int fsid = swf_ShapeAddSolidFillStyle(shape,&col);
2445 RGBA markcol = {0,i->mark[0],i->mark[1],i->mark[2]};
2446 swf_ShapeAddSolidFillStyle(shape,&markcol);
2448 swf_SetU16(i->tag,myshapeid);
2449 SRECT r = gfxline_getSWFbbox(line);
2450 r = swf_ClipRect(i->pagebbox, r);
2451 swf_SetRect(i->tag,&r);
2452 swf_SetShapeStyles(i->tag,shape);
2453 swf_ShapeCountBits(shape,NULL,NULL);
2454 swf_SetShapeBits(i->tag,shape);
2455 swf_ShapeSetAll(i->tag,shape,UNDEFINED_COORD,UNDEFINED_COORD,0,fsid,0);
2456 i->swflastx = i->swflasty = UNDEFINED_COORD;
2457 i->shapeisempty = 1;
2458 drawgfxline(dev, line, 1);
2459 if(i->shapeisempty) {
2460 /* an empty clip shape is equivalent to a shape with no area */
2461 int x = line?line->x:0;
2462 int y = line?line->y:0;
2463 moveto(dev, i->tag, x,y);
2464 lineto(dev, i->tag, x,y);
2465 lineto(dev, i->tag, x,y);
2467 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)) {
2468 if(i->config_watermark) {
2469 gfxbbox_t r; r.xmin = r.ymin = 0;r.xmax = i->max_x;r.ymax = i->max_y;
2470 draw_watermark(dev, r, 1);
2473 swf_ShapeSetEnd(i->tag);
2474 swf_ShapeFree(shape);
2476 /* TODO: remember the bbox, and check all shapes against it */
2478 msg("<trace> Placing clip ID %d", myshapeid);
2479 i->tag = swf_InsertTag(i->tag,ST_PLACEOBJECT2);
2480 i->cliptags[i->clippos] = i->tag;
2481 i->clipshapes[i->clippos] = myshapeid;
2482 i->clipdepths[i->clippos] = getNewDepth(dev);
2486 static void swf_endclip(gfxdevice_t*dev)
2488 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
2489 if(i->config_noclips)
2497 msg("<error> Invalid end of clipping region");
2501 /*swf_ObjectPlaceClip(i->cliptags[i->clippos],i->clipshapes[i->clippos],i->clipdepths[i->clippos],&i->page_matrix,NULL,NULL,
2502 / * clip to depth: * / i->depth <= i->clipdepths[i->clippos]? i->depth : i->depth - 1);
2504 swf_ObjectPlaceClip(i->cliptags[i->clippos],i->clipshapes[i->clippos],i->clipdepths[i->clippos],&i->page_matrix,NULL,NULL,i->depth);
2506 static int gfxline_type(gfxline_t*line)
2512 int haszerosegments=0;
2515 if(line->type == gfx_moveTo) {
2518 } else if(line->type == gfx_lineTo) {
2522 } else if(line->type == gfx_splineTo) {
2524 if(tmpsplines>lines)
2532 if(lines==0 && splines==0) return 0;
2533 else if(lines==1 && splines==0) return 1;
2534 else if(lines==0 && splines==1) return 2;
2535 else if(splines==0) return 3;
2539 static int gfxline_has_dots(gfxline_t*line)
2547 if(line->type == gfx_moveTo) {
2548 /* test the length of the preceding line, and assume it is a dot if
2549 it's length is less than 1.0. But *only* if there's a noticable
2550 gap between the previous line and the next moveTo. (I've come
2551 across a PDF where thousands of "dots" were stringed together,
2553 int last_short_gap = short_gap;
2554 if((fabs(line->x - x) + fabs(line->y - y)) < 1.0) {
2559 if(isline && dist < 1 && !short_gap && !last_short_gap) {
2564 } else if(line->type == gfx_lineTo) {
2565 dist += fabs(line->x - x) + fabs(line->y - y);
2567 } else if(line->type == gfx_splineTo) {
2568 dist += fabs(line->sx - x) + fabs(line->sy - y) +
2569 fabs(line->x - line->sx) + fabs(line->y - line->sy);
2576 if(isline && dist < 1 && !short_gap) {
2582 static int gfxline_fix_short_edges(gfxline_t*line)
2586 if(line->type == gfx_lineTo) {
2587 if(fabs(line->x - x) + fabs(line->y - y) < 0.01) {
2590 } else if(line->type == gfx_splineTo) {
2591 if(fabs(line->sx - x) + fabs(line->sy - y) +
2592 fabs(line->x - line->sx) + fabs(line->y - line->sy) < 0.01) {
2603 static char is_inside_page(gfxdevice_t*dev, gfxcoord_t x, gfxcoord_t y)
2605 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
2606 if(x<i->min_x || x>i->max_x) return 0;
2607 if(y<i->min_y || y>i->max_y) return 0;
2611 gfxline_t* gfxline_move(gfxline_t*line, double x, double y)
2613 gfxline_t*l = line = gfxline_clone(line);
2625 //#define NORMALIZE_POLYGON_POSITIONS
2627 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)
2629 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
2630 if(line_is_empty(line))
2632 int type = gfxline_type(line);
2633 int has_dots = gfxline_has_dots(line);
2634 gfxbbox_t r = gfxline_getbbox(line);
2635 int is_outside_page = !is_inside_page(dev, r.xmin, r.ymin) || !is_inside_page(dev, r.xmax, r.ymax);
2637 /* TODO: * split line into segments, and perform this check for all segments */
2639 if(i->config_disable_polygon_conversion || /*type>=5 ||*/
2641 (width <= i->config_caplinewidth
2642 || (cap_style == gfx_capRound && joint_style == gfx_joinRound)
2643 || (cap_style == gfx_capRound && type<=2))))
2647 /* convert line to polygon */
2648 msg("<trace> draw as polygon, type=%d dots=%d", type, has_dots);
2650 gfxline_fix_short_edges(line);
2651 /* we need to convert the line into a polygon */
2652 gfxpoly_t* poly = gfxpoly_from_stroke(line, width, cap_style, joint_style, miterLimit, DEFAULT_GRID);
2653 gfxline_t*gfxline = gfxline_from_gfxpoly(poly);
2654 dev->fill(dev, gfxline, color);
2655 gfxline_free(gfxline);
2656 gfxpoly_destroy(poly);
2660 msg("<trace> draw as stroke, type=%d dots=%d", type, has_dots);
2663 if(i->config_normalize_polygon_positions) {
2665 double startx = 0, starty = 0;
2666 if(line && line->type == gfx_moveTo) {
2670 line = gfxline_move(line, -startx, -starty);
2671 i->shapeposx = (int)(startx*20);
2672 i->shapeposy = (int)(starty*20);
2675 swfoutput_setstrokecolor(dev, color->r, color->g, color->b, color->a);
2676 swfoutput_setlinewidth(dev, width);
2679 drawgfxline(dev, line, 0);
2681 if(i->config_normalize_polygon_positions) {
2682 free(line); //account for _move
2687 static void swf_fill(gfxdevice_t*dev, gfxline_t*line, gfxcolor_t*color)
2689 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
2690 if(line_is_empty(line))
2694 gfxbbox_t r = gfxline_getbbox(line);
2695 int is_outside_page = !is_inside_page(dev, r.xmin, r.ymin) || !is_inside_page(dev, r.xmax, r.ymax);
2699 if(!i->config_ignoredraworder)
2702 if(i->config_normalize_polygon_positions) {
2704 double startx = 0, starty = 0;
2705 if(line && line->type == gfx_moveTo) {
2709 line = gfxline_move(line, -startx, -starty);
2710 i->shapeposx = (int)(startx*20);
2711 i->shapeposy = (int)(starty*20);
2714 swfoutput_setfillcolor(dev, color->r, color->g, color->b, color->a);
2717 drawgfxline(dev, line, 1);
2719 if(i->currentswfid==2 && r.xmin==0 && r.ymin==0 && r.xmax==i->max_x && r.ymax==i->max_y) {
2720 if(i->config_watermark) {
2721 draw_watermark(dev, r, 1);
2725 msg("<trace> end of swf_fill (shapeid=%d)", i->shapeid);
2727 if(i->config_normalize_polygon_positions) {
2728 free(line); //account for _move
2732 static GRADIENT* gfxgradient_to_GRADIENT(gfxgradient_t*gradient)
2735 gfxgradient_t*g = gradient;
2740 GRADIENT* swfgradient = malloc(sizeof(GRADIENT));
2741 swfgradient->num = num;
2742 swfgradient->rgba = malloc(sizeof(swfgradient->rgba[0])*num);
2743 swfgradient->ratios = malloc(sizeof(swfgradient->ratios[0])*num);
2748 swfgradient->ratios[num] = g->pos*255;
2749 swfgradient->rgba[num] = *(RGBA*)&g->color;
2756 static void swf_fillgradient(gfxdevice_t*dev, gfxline_t*line, gfxgradient_t*gradient, gfxgradienttype_t type, gfxmatrix_t*matrix)
2758 if(line_is_empty(line))
2760 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
2762 if(line_is_empty(line))
2765 GRADIENT* swfgradient = gfxgradient_to_GRADIENT(gradient);
2772 double f = type==gfxgradient_radial?4:4;
2774 m.sx = (int)(matrix->m00*20*f); m.r1 = (int)(matrix->m10*20*f);
2775 m.r0 = (int)(matrix->m01*20*f); m.sy = (int)(matrix->m11*20*f);
2776 m.tx = (int)(matrix->tx*20);
2777 m.ty = (int)(matrix->ty*20);
2780 int myshapeid = getNewID(dev);
2781 i->tag = swf_InsertTag(i->tag, ST_DEFINESHAPE2);
2783 swf_ShapeNew(&shape);
2784 int fsid = swf_ShapeAddGradientFillStyle(shape,&m,swfgradient,type==gfxgradient_radial);
2785 swf_SetU16(i->tag, myshapeid);
2786 SRECT r = gfxline_getSWFbbox(line);
2787 r = swf_ClipRect(i->pagebbox, r);
2788 swf_SetRect(i->tag,&r);
2789 swf_SetShapeStyles(i->tag,shape);
2790 swf_ShapeCountBits(shape,NULL,NULL);
2791 swf_SetShapeBits(i->tag,shape);
2792 swf_ShapeSetAll(i->tag,shape,UNDEFINED_COORD,UNDEFINED_COORD,0,fsid,0);
2793 i->swflastx = i->swflasty = UNDEFINED_COORD;
2794 drawgfxline(dev, line, 1);
2795 swf_ShapeSetEnd(i->tag);
2796 swf_ShapeFree(shape);
2798 int depth = getNewDepth(dev);
2799 msg("<trace> Placing gradient, shape ID %d, depth %d", myshapeid, depth);
2800 i->tag = swf_InsertTag(i->tag,ST_PLACEOBJECT2);
2801 swf_ObjectPlace(i->tag,myshapeid,depth,&i->page_matrix,NULL,NULL);
2803 swf_FreeGradient(swfgradient);free(swfgradient);
2806 static SWFFONT* gfxfont_to_swffont(gfxfont_t*font, const char* id, int version)
2808 SWFFONT*swffont = (SWFFONT*)rfx_calloc(sizeof(SWFFONT));
2810 SRECT bounds = {0,0,0,0};
2812 swffont->version = version;
2813 swffont->name = (U8*)strdup(id);
2814 swffont->layout = (SWFLAYOUT*)rfx_calloc(sizeof(SWFLAYOUT));
2815 swffont->layout->ascent = 0;
2816 swffont->layout->descent = 0;
2817 swffont->layout->leading = 0;
2818 swffont->layout->bounds = (SRECT*)rfx_calloc(sizeof(SRECT)*font->num_glyphs);
2819 swffont->encoding = FONT_ENCODING_UNICODE;
2820 swffont->numchars = font->num_glyphs;
2821 swffont->maxascii = font->max_unicode;
2822 swffont->ascii2glyph = (int*)rfx_calloc(sizeof(int)*swffont->maxascii);
2823 swffont->glyph2ascii = (U16*)rfx_calloc(sizeof(U16)*swffont->numchars);
2824 swffont->glyph = (SWFGLYPH*)rfx_calloc(sizeof(SWFGLYPH)*swffont->numchars);
2825 swffont->glyphnames = (char**)rfx_calloc(sizeof(char*)*swffont->numchars);
2827 SRECT max = {0,0,0,0};
2828 for(t=0;t<font->num_glyphs;t++) {
2832 int u = font->glyphs[t].unicode;
2835 for(s=0;s<font->num_glyphs;s++) {
2836 if(swffont->glyph2ascii[s]==u)
2839 if(u >= 0xe000 || u == 0x0000 || twice) {
2840 /* flash 8 flashtype requires unique unicode IDs for each character.
2841 We use the Unicode private user area to assign characters, hoping that
2842 the font doesn't contain more than 2048 glyphs */
2843 u = 0xe000 + (t&0x1fff);
2845 swffont->glyph2ascii[t] = u;
2847 if(font->glyphs[t].name) {
2848 swffont->glyphnames[t] = strdup(font->glyphs[t].name);
2850 swffont->glyphnames[t] = 0;
2852 advance = font->glyphs[t].advance;
2854 swf_Shape01DrawerInit(&draw, 0);
2855 line = font->glyphs[t].line;
2857 const double scale = GLYPH_SCALE;
2860 c.x = line->sx * scale; c.y = -line->sy * scale;
2861 //to.x = floor(line->x * scale); to.y = floor(-line->y * scale);
2862 to.x = line->x * scale; to.y = -line->y * scale;
2864 /*if(strstr(swffont->name, "BIRNU") && t==90) {
2868 if(line->type == gfx_moveTo) {
2869 draw.moveTo(&draw, &to);
2870 } else if(line->type == gfx_lineTo) {
2871 draw.lineTo(&draw, &to);
2872 } else if(line->type == gfx_splineTo) {
2873 draw.splineTo(&draw, &c, &to);
2878 swffont->glyph[t].shape = swf_ShapeDrawerToShape(&draw);
2880 SRECT bbox = swf_ShapeDrawerGetBBox(&draw);
2881 swf_ExpandRect2(&max, &bbox);
2883 swffont->layout->bounds[t] = bbox;
2885 if(advance<32768.0/20) {
2886 swffont->glyph[t].advance = (int)(advance*20);
2888 //msg("<warning> Advance value overflow in glyph %d", t);
2889 swffont->glyph[t].advance = 32767;
2892 draw.dealloc(&draw);
2894 swf_ExpandRect2(&bounds, &swffont->layout->bounds[t]);
2897 for(t=0;t<font->num_glyphs;t++) {
2898 SRECT bbox = swffont->layout->bounds[t];
2900 /* if the glyph doesn't have a bounding box, use the
2901 combined bounding box (necessary e.g. for space characters) */
2902 if(!(bbox.xmin|bbox.ymin|bbox.xmax|bbox.ymax)) {
2903 swffont->layout->bounds[t] = bbox = max;
2906 /* check that the advance value is reasonable, by comparing it
2907 with the bounding box */
2908 if(bbox.xmax>0 && (bbox.xmax*10 < swffont->glyph[t].advance || !swffont->glyph[t].advance)) {
2909 if(swffont->glyph[t].advance)
2910 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);
2911 swffont->glyph[t].advance = bbox.xmax;
2913 //swffont->glyph[t].advance = bbox.xmax - bbox.xmin;
2917 /* Flash player will use the advance value from the char, and the ascent/descent values
2918 from the layout for text selection.
2919 ascent will extend the char into negative y direction, from the baseline, while descent
2920 will extend in positive y direction, also from the baseline.
2921 The baseline is defined as the y-position zero
2924 swffont->layout->ascent = bounds.ymin<0?-bounds.ymin:0;
2925 swffont->layout->descent = bounds.ymax>0?bounds.ymax:0;
2926 swffont->layout->leading = bounds.ymax - bounds.ymin;
2928 /* if the font has proper ascent/descent values (>0) and those define
2929 greater line spacing that what we estimated from the bounding boxes,
2930 use the font's parameters */
2931 if(font->ascent*20 > swffont->layout->ascent)
2932 swffont->layout->ascent = font->ascent*20;
2933 if(font->descent*20 > swffont->layout->descent)
2934 swffont->layout->descent = font->descent*20;
2936 swf_FontSort(swffont);
2940 static void swf_addfont(gfxdevice_t*dev, gfxfont_t*font)
2942 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
2944 if(i->swffont && i->swffont->name && !strcmp((char*)i->swffont->name,font->id))
2945 return; // the requested font is the current font
2947 fontlist_t*last=0,*l = i->fontlist;
2950 if(!strcmp((char*)l->swffont->name, font->id)) {
2951 return; // we already know this font
2955 l = (fontlist_t*)rfx_calloc(sizeof(fontlist_t));
2956 l->swffont = gfxfont_to_swffont(font, font->id, (i->config_flashversion>=8 && !NO_FONT3)?3:2);
2963 swf_FontSetID(l->swffont, getNewID(i->dev));
2965 if(getScreenLogLevel() >= LOGLEVEL_DEBUG) {
2967 // print font information
2968 msg("<debug> Font %s",font->id);
2969 msg("<debug> | ID: %d", l->swffont->id);
2970 msg("<debug> | Version: %d", l->swffont->version);
2971 msg("<debug> | Name: %s", l->swffont->name);
2972 msg("<debug> | Numchars: %d", l->swffont->numchars);
2973 msg("<debug> | Maxascii: %d", l->swffont->maxascii);
2974 msg("<debug> | Style: %d", l->swffont->style);
2975 msg("<debug> | Encoding: %d", l->swffont->encoding);
2976 for(iii=0; iii<l->swffont->numchars;iii++) {
2977 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,
2978 l->swffont->layout->bounds[iii].xmin/20.0,
2979 l->swffont->layout->bounds[iii].ymin/20.0,
2980 l->swffont->layout->bounds[iii].xmax/20.0,
2981 l->swffont->layout->bounds[iii].ymax/20.0
2987 static void swf_switchfont(gfxdevice_t*dev, const char*fontid)
2989 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
2991 if(i->swffont && i->swffont->name && !strcmp((char*)i->swffont->name,fontid))
2992 return; // the requested font is the current font
2994 fontlist_t*l = i->fontlist;
2996 if(!strcmp((char*)l->swffont->name, fontid)) {
2997 i->swffont = l->swffont;
3002 msg("<error> Unknown font id: %s", fontid);
3006 /* sets the matrix which is to be applied to characters drawn by swfoutput_drawchar() */
3007 static void setfontscale(gfxdevice_t*dev,double m11,double m12, double m21,double m22,double x, double y, char force)
3013 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
3014 if(i->lastfontm11 == m11 &&
3015 i->lastfontm12 == m12 &&
3016 i->lastfontm21 == m21 &&
3017 i->lastfontm22 == m22 && !force)
3022 i->lastfontm11 = m11;
3023 i->lastfontm12 = m12;
3024 i->lastfontm21 = m21;
3025 i->lastfontm22 = m22;
3027 double xsize = sqrt(m11*m11 + m12*m12);
3028 double ysize = sqrt(m21*m21 + m22*m22);
3031 if(i->config_flashversion>=8 && !NO_FONT3)
3034 i->current_font_size = (xsize>ysize?xsize:ysize)*extrazoom;
3035 if(i->current_font_size < 1)
3036 i->current_font_size = 1;
3039 swf_GetMatrix(0, &m);
3041 if(m21 || m12 || fabs(m11+m22)>0.001) {
3042 double ifs = (double)extrazoom/(i->current_font_size);
3043 m.sx = (S32)((m11*ifs)*65536); m.r1 = -(S32)((m21*ifs)*65536);
3044 m.r0 = (S32)((m12*ifs)*65536); m.sy = -(S32)((m22*ifs)*65536);
3047 /* this is the position of the first char to set a new fontmatrix-
3048 we hope that it's close enough to all other characters using the
3049 font, so we use its position as origin for the matrix */
3056 static void swf_drawchar(gfxdevice_t*dev, gfxfont_t*font, int glyph, gfxcolor_t*color, gfxmatrix_t*matrix)
3058 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
3060 msg("<error> swf_drawchar called (glyph %d) without font", glyph);
3064 if(i->config_drawonlyshapes) {
3065 gfxglyph_t*g = &font->glyphs[glyph];
3066 gfxline_t*line2 = gfxline_clone(g->line);
3067 gfxline_transform(line2, matrix);
3068 dev->fill(dev, line2, color);
3069 gfxline_free(line2);
3073 if(!i->swffont || !i->swffont->name || strcmp((char*)i->swffont->name,font->id)) // not equal to current font
3075 swf_switchfont(dev, font->id); // set the current font
3079 msg("<warning> swf_drawchar: Font is NULL");
3082 if(glyph<0 || glyph>=i->swffont->numchars) {
3083 msg("<warning> No character %d in font %s (%d chars)", glyph, FIXNULL((char*)i->swffont->name), i->swffont->numchars);
3086 glyph = i->swffont->glyph2glyph[glyph];
3088 setfontscale(dev, matrix->m00, matrix->m01, matrix->m10, matrix->m11, matrix->tx, matrix->ty, 0);
3090 double det = i->fontmatrix.sx/65536.0 * i->fontmatrix.sy/65536.0 -
3091 i->fontmatrix.r0/65536.0 * i->fontmatrix.r1/65536.0;
3092 if(fabs(det) < 0.0005) {
3093 /* x direction equals y direction- the text is invisible */
3094 msg("<verbose> Not drawing invisible character %d (det=%f, m=[%f %f;%f %f]\n", glyph,
3096 i->fontmatrix.sx/65536.0, i->fontmatrix.r1/65536.0,
3097 i->fontmatrix.r0/65536.0, i->fontmatrix.sy/65536.0);
3101 /*if(i->swffont->glyph[glyph].shape->bitlen <= 16) {
3102 msg("<warning> Glyph %d in current charset (%s, %d characters) is empty",
3103 glyph, FIXNULL((char*)i->swffont->name), i->swffont->numchars);
3107 /* calculate character position with respect to the current font matrix */
3108 double s = 20 * GLYPH_SCALE / det;
3109 double px = matrix->tx - i->fontmatrix.tx/20.0;
3110 double py = matrix->ty - i->fontmatrix.ty/20.0;
3111 int x = (SCOORD)(( px * i->fontmatrix.sy/65536.0 - py * i->fontmatrix.r1/65536.0)*s);
3112 int y = (SCOORD)((- px * i->fontmatrix.r0/65536.0 + py * i->fontmatrix.sx/65536.0)*s);
3113 if(x>32767 || x<-32768 || y>32767 || y<-32768) {
3114 msg("<verbose> Moving character origin to %f %f\n", matrix->tx, matrix->ty);
3116 setfontscale(dev, matrix->m00, matrix->m01, matrix->m10, matrix->m11, matrix->tx, matrix->ty, 1);
3117 /* since we just moved the char origin to the current char's position,
3118 it now has the relative position (0,0) */
3127 msg("<trace> Drawing char %d in font %d at %d,%d in color %02x%02x%02x%02x",
3128 glyph, i->swffont->id, x, y, color->r, color->g, color->b, color->a);
3130 if(color->a == 0 && i->config_invisibletexttofront) {
3131 RGBA color2 = *(RGBA*)color;
3132 if(i->config_flashversion>=8) {
3133 // use "multiply" blend mode
3134 color2.a = color2.r = color2.g = color2.b = 255;
3136 i->topchardata = charbuffer_append(i->topchardata, i->swffont, glyph, x, y, i->current_font_size, color2, &i->fontmatrix);
3138 i->chardata = charbuffer_append(i->chardata, i->swffont, glyph, x, y, i->current_font_size, *(RGBA*)color, &i->fontmatrix);
3140 swf_FontUseGlyph(i->swffont, glyph, i->current_font_size);