3 Part of the swftools package.
5 Copyright (c) 2001,2002,2003,2004,2005 Matthias Kramm <kramm@quiss.org>
7 Swftools is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 2 of the License, or
10 (at your option) any later version.
12 Swftools is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
17 You should have received a copy of the GNU General Public License
18 along with swftools; if not, write to the Free Software
19 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
24 #include "../../config.h"
40 #include "../rfxswf.h"
41 #include "../gfxdevice.h"
42 #include "../gfxtools.h"
44 #include "../gfxpoly.h"
47 #define CHARDATAMAX 1024
51 typedef struct _charatposition {
60 typedef struct _chararray {
61 charatposition_t chr[CHARDATAMAX+1];
63 struct _chararray *next;
66 typedef struct _charbuffer {
70 struct _charbuffer *next;
73 typedef struct _fontlist
76 struct _fontlist*next;
79 typedef long int twip;
81 typedef struct _swfmatrix {
82 double m11,m12,m21,m22,m31,m32;
85 typedef struct _swfoutput_internal
87 gfxdevice_t*dev; // the gfxdevice object where this internal struct resides
89 double config_dumpfonts;
90 double config_ppmsubpixels;
91 double config_jpegsubpixels;
93 int config_invisibletexttofront;
95 int config_simpleviewer;
96 int config_opennewwindow;
97 int config_ignoredraworder;
98 int config_drawonlyshapes;
99 int config_frameresets;
100 int config_linknameurl;
101 int config_jpegquality;
102 int config_storeallcharacters;
103 int config_enablezlib;
104 int config_insertstoptag;
105 int config_watermark;
107 int config_flashversion;
108 int config_reordertags;
109 int config_showclipshapes;
110 int config_splinemaxerror;
111 int config_fontsplinemaxerror;
112 int config_filloverlap;
115 int config_disable_polygon_conversion;
116 int config_normalize_polygon_positions;
117 char config_disablelinks;
118 RGBA config_linkcolor;
119 float config_minlinewidth;
120 double config_caplinewidth;
121 char* config_linktarget;
122 char*config_internallinkfunction;
123 char*config_externallinkfunction;
125 double config_framerate;
129 fontlist_t* fontlist;
168 int pic_height[1024];
175 char fillstylechanged;
177 int jpeg; //next image type
184 charbuffer_t* chardata;
185 charbuffer_t* topchardata; //chars supposed to be above everything else
192 int current_font_size;
194 double lastfontm11,lastfontm12,lastfontm21,lastfontm22;
205 } swfoutput_internal;
207 static const int NO_FONT3=0;
209 static void swf_fillbitmap(gfxdevice_t*driver, gfxline_t*line, gfximage_t*img, gfxmatrix_t*move, gfxcxform_t*cxform);
210 static int swf_setparameter(gfxdevice_t*driver, const char*key, const char*value);
211 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);
212 static void swf_startclip(gfxdevice_t*dev, gfxline_t*line);
213 static void swf_endclip(gfxdevice_t*dev);
214 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);
215 static void swf_fill(gfxdevice_t*dev, gfxline_t*line, gfxcolor_t*color);
216 static void swf_fillbitmap(gfxdevice_t*dev, gfxline_t*line, gfximage_t*img, gfxmatrix_t*matrix, gfxcxform_t*cxform);
217 static void swf_fillgradient(gfxdevice_t*dev, gfxline_t*line, gfxgradient_t*gradient, gfxgradienttype_t type, gfxmatrix_t*matrix);
218 static void swf_drawchar(gfxdevice_t*dev, gfxfont_t*font, int glyph, gfxcolor_t*color, gfxmatrix_t*matrix);
219 static void swf_addfont(gfxdevice_t*dev, gfxfont_t*font);
220 static void swf_drawlink(gfxdevice_t*dev, gfxline_t*line, const char*action);
221 static void swf_startframe(gfxdevice_t*dev, int width, int height);
222 static void swf_endframe(gfxdevice_t*dev);
223 static void swfoutput_namedlink(gfxdevice_t*dev, char*name, gfxline_t*points);
224 static void swfoutput_linktopage(gfxdevice_t*dev, int page, gfxline_t*points);
225 static void swfoutput_linktourl(gfxdevice_t*dev, const char*url, gfxline_t*points);
227 static gfxresult_t* swf_finish(gfxdevice_t*driver);
229 static swfoutput_internal* init_internal_struct()
231 swfoutput_internal*i = (swfoutput_internal*)malloc(sizeof(swfoutput_internal));
232 memset(i, 0, sizeof(swfoutput_internal));
256 i->fillstylechanged = 0;
263 i->config_disablelinks=0;
264 i->config_dumpfonts=0;
265 i->config_ppmsubpixels=0;
266 i->config_jpegsubpixels=0;
267 i->config_opennewwindow=1;
268 i->config_ignoredraworder=0;
269 i->config_drawonlyshapes=0;
270 i->config_jpegquality=85;
271 i->config_storeallcharacters=0;
273 i->config_enablezlib=0;
274 i->config_insertstoptag=0;
275 i->config_flashversion=6;
276 i->config_framerate=0.25;
277 i->config_splinemaxerror=1;
278 i->config_fontsplinemaxerror=1;
279 i->config_filloverlap=0;
281 i->config_bboxvars=0;
282 i->config_showclipshapes=0;
283 i->config_minlinewidth=0.05;
284 i->config_caplinewidth=1;
285 i->config_linktarget=0;
286 i->config_internallinkfunction=0;
287 i->config_externallinkfunction=0;
288 i->config_reordertags=1;
289 i->config_linknameurl=0;
291 i->config_linkcolor.r = i->config_linkcolor.g = i->config_linkcolor.b = 255;
292 i->config_linkcolor.a = 0x40;
297 static int id_error = 0;
299 static U16 getNewID(gfxdevice_t* dev)
301 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
302 if(i->currentswfid == 65535) {
304 msg("<error> ID Table overflow");
305 msg("<error> This file is too complex to render- SWF only supports 65536 shapes at once");
311 return ++i->currentswfid;
313 static U16 getNewDepth(gfxdevice_t* dev)
315 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
316 if(i->depth == 65520) {
318 msg("<error> Depth Table overflow");
319 msg("<error> This file is too complex to render- SWF only supports 65536 shapes at once");
328 static void startshape(gfxdevice_t* dev);
329 static void starttext(gfxdevice_t* dev);
330 static void endshape(gfxdevice_t* dev);
331 static void endtext(gfxdevice_t* dev);
333 typedef struct _plotxy
338 static inline int twipsnap(double f)
340 /* if(f < -0x40000000/20.0) {
341 fprintf(stderr, "Warning: Coordinate underflow (%f)\n", f);
342 f = -0x40000000/20.0;
343 } else if(f>0x3fffffff/20.0) {
344 fprintf(stderr, "Warning: Coordinate overflow (%f)\n", f);
348 /* clamp coordinates to a rectangle with the property that we
349 can represent a line from the upper left corner to the upper
350 right corner using no more than 64 strokes */
351 const double min = -(1<<(18+4))/20.0;
352 const double max = ((1<<(18+4))-1)/20.0;
354 fprintf(stderr, "Warning: Coordinate underflow (%f)\n", f);
357 fprintf(stderr, "Warning: Coordinate overflow (%f)\n", f);
364 // write a move-to command into the swf
365 static int movetoxy(gfxdevice_t*dev, TAG*tag, plotxy_t p0)
367 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
368 int rx = twipsnap(p0.x);
369 int ry = twipsnap(p0.y);
370 if(rx!=i->swflastx || ry!=i->swflasty || i->fillstylechanged) {
371 swf_ShapeSetMove (tag, i->shape, rx,ry);
372 i->fillstylechanged = 0;
379 static int moveto(gfxdevice_t*dev, TAG*tag, double x, double y)
381 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
385 return movetoxy(dev, tag, p);
387 static void addPointToBBox(gfxdevice_t*dev, int px, int py)
389 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
395 swf_ExpandRect(&i->bboxrect, p);
397 swf_ExpandRect3(&i->bboxrect, p, i->linewidth*3/2);
401 /*static void plot(gfxdevice_t*dev, int x, int y, TAG*tag)
403 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
404 int width = i->linewidth/4;
408 //swf_ShapeSetLine(tag, i->shape,-width,-width);
409 //swf_ShapeSetLine(tag, i->shape,width*2,0);
410 //swf_ShapeSetLine(tag, i->shape,0,width*2);
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,width);
416 swf_ShapeSetLine(tag, i->shape,-width,0);
417 swf_ShapeSetLine(tag, i->shape,width,-width);
418 swf_ShapeSetLine(tag, i->shape,width,width);
419 swf_ShapeSetLine(tag, i->shape,-width,width);
420 swf_ShapeSetLine(tag, i->shape,-width,-width);
421 swf_ShapeSetLine(tag, i->shape,width,0);
423 addPointToBBox(dev, x-width ,y-width);
424 addPointToBBox(dev, x+width ,y+width);
427 // write a line-to command into the swf
428 static void linetoxy(gfxdevice_t*dev, TAG*tag, plotxy_t p0)
430 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
431 int px = twipsnap(p0.x);
432 int py = twipsnap(p0.y);
433 int rx = (px-i->swflastx);
434 int ry = (py-i->swflasty);
436 swf_ShapeSetLine (tag, i->shape, rx,ry);
437 addPointToBBox(dev, i->swflastx,i->swflasty);
438 addPointToBBox(dev, px,py);
439 } /* this is a nice idea, but doesn't work with current flash
440 players (the pixel will be invisible if they're not
441 precisely on a pixel boundary)
442 Besides, we should only do this if this lineto itself
443 is again followed by a "move".
444 else if(!i->fill && i->config_dots) {
445 // treat lines of length 0 as plots, making them
446 // at least 1 twip wide so Flash will display them
447 //plot(dev, i->swflastx, i->swflasty, tag);
448 swf_ShapeSetLine (tag, i->shape, rx+1,ry);
455 static void lineto(gfxdevice_t*dev, TAG*tag, double x, double y)
460 linetoxy(dev,tag, p);
463 // write a spline-to command into the swf
464 static void splineto(gfxdevice_t*dev, TAG*tag, plotxy_t control,plotxy_t end)
466 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
467 int lastlastx = i->swflastx;
468 int lastlasty = i->swflasty;
470 int cx = (twipsnap(control.x)-i->swflastx);
471 int cy = (twipsnap(control.y)-i->swflasty);
474 int ex = (twipsnap(end.x)-i->swflastx);
475 int ey = (twipsnap(end.y)-i->swflasty);
479 if((cx || cy) && (ex || ey)) {
480 swf_ShapeSetCurve(tag, i->shape, cx,cy,ex,ey);
481 addPointToBBox(dev, lastlastx ,lastlasty );
482 addPointToBBox(dev, lastlastx+cx,lastlasty+cy);
483 addPointToBBox(dev, lastlastx+cx+ex,lastlasty+cy+ey);
484 } else if(cx || cy || ex || ey) {
485 swf_ShapeSetLine(tag, i->shape, cx+ex,cy+ey);
486 addPointToBBox(dev, lastlastx ,lastlasty );
487 addPointToBBox(dev, lastlastx+cx,lastlasty+cy);
488 addPointToBBox(dev, lastlastx+cx+ex,lastlasty+cy+ey);
494 /* write a line, given two points and the transformation
496 /*static void line(gfxdevice_t*dev, TAG*tag, plotxy_t p0, plotxy_t p1)
498 moveto(dev, tag, p0);
499 lineto(dev, tag, p1);
502 void resetdrawer(gfxdevice_t*dev)
504 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
509 static void stopFill(gfxdevice_t*dev)
511 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
512 if(i->lastwasfill!=0)
514 swf_ShapeSetStyle(i->tag,i->shape,i->linestyleid,0x8000,0);
515 i->fillstylechanged = 1;
519 static void startFill(gfxdevice_t*dev)
521 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
522 if(i->lastwasfill!=1)
524 swf_ShapeSetStyle(i->tag,i->shape,0x8000,i->fillstyleid,0);
525 i->fillstylechanged = 1;
530 static inline int colorcompare(RGBA*a,RGBA*b)
542 static SRECT getcharacterbbox(chararray_t*chardata, MATRIX* m, int flashversion)
546 memset(&r, 0, sizeof(r));
549 if(debug) printf("\n");
551 double div = 1.0 / 1024.0;
552 if(flashversion>=8 && !NO_FONT3) {
557 for(t=0;t<chardata->pos;t++) {
558 charatposition_t*chr = &chardata->chr[t];
559 SRECT b = chr->font->layout->bounds[chr->charid];
560 b.xmin = floor((b.xmin*(double)chr->size) *div);
561 b.ymin = floor((b.ymin*(double)chr->size) *div);
562 b.xmax = ceil((b.xmax*(double)chr->size) *div);
563 b.ymax = ceil((b.ymax*(double)chr->size) *div);
570 /* until we solve the INTERNAL_SCALING problem (see below)
571 make sure the bounding box is big enough */
577 b = swf_TurnRect(b, m);
579 if(debug) printf("(%f,%f,%f,%f) -> (%f,%f,%f,%f) [font %d, char %d]\n",
580 chr->font->layout->bounds[chr->charid].xmin/20.0,
581 chr->font->layout->bounds[chr->charid].ymin/20.0,
582 chr->font->layout->bounds[chr->charid].xmax/20.0,
583 chr->font->layout->bounds[chr->charid].ymax/20.0,
590 swf_ExpandRect2(&r, &b);
592 chardata = chardata->next;
594 if(debug) printf("-----> (%f,%f,%f,%f)\n",
602 static chararray_t*chararray_reverse(chararray_t*buf)
604 chararray_t*prev = 0;
606 chararray_t*next = buf->next;
614 static void chararray_writetotag(chararray_t*_chardata, TAG*tag)
618 color.r = _chardata?_chardata->chr[0].color.r^255:0;
627 int charadvance[128];
630 int glyphbits=1; //TODO: can this be zero?
633 if(tag->id != ST_DEFINETEXT &&
634 tag->id != ST_DEFINETEXT2) {
635 msg("<error> internal error: charbuffer_put needs an text tag, not %d\n",tag->id);
639 msg("<warning> charbuffer_put called with zero characters");
642 for(pass = 0; pass < 2; pass++)
652 advancebits++; // add sign bit
653 swf_SetU8(tag, glyphbits);
654 swf_SetU8(tag, advancebits);
657 chararray_t*chardata = _chardata;
662 assert(!chardata->next || chardata->pos == CHARDATAMAX);
663 assert(chardata->pos);
665 int to = chardata->next?chardata->pos-1:chardata->pos;
669 char islast = t==chardata->pos;
671 charatposition_t*chr = &chardata->chr[t];
673 if(lastfont != chardata->chr[t].font ||
674 lastx!=chardata->chr[t].x ||
675 lasty!=chardata->chr[t].y ||
676 !colorcompare(&color, &chardata->chr[t].color) ||
678 lastsize != chardata->chr[t].size ||
681 if(charstorepos && pass==0)
684 for(s=0;s<charstorepos;s++)
686 while(charids[s]>=(1<<glyphbits))
688 while(charadvance[s]>=(1<<advancebits))
692 if(charstorepos && pass==1)
694 tag->writeBit = 0; // Q&D
695 swf_SetBits(tag, 0, 1); // GLYPH Record
696 swf_SetBits(tag, charstorepos, 7); // number of glyphs
698 for(s=0;s<charstorepos;s++)
700 swf_SetBits(tag, charids[s], glyphbits);
701 swf_SetBits(tag, charadvance[s], advancebits);
706 if(pass == 1 && !islast)
712 if(lastx != chr->x ||
722 if(!colorcompare(&color, &chr->color))
727 font.id = chr->font->id;
728 if(lastfont != chr->font || lastsize != chr->size)
731 tag->writeBit = 0; // Q&D
732 swf_TextSetInfoRecord(tag, newfont, chr->size, newcolor, newx, newy);
735 lastfont = chr->font;
738 lastsize = chr->size;
745 if(t<chardata->pos-1) nextx = chardata->chr[t+1].x;
746 if(t==chardata->pos-1 && chardata->next) nextx = chardata->next->chr[0].x;
747 int dx = nextx-chr->x;
750 if(dx>=0 && (dx<(1<<(advancebits-1)) || pass==0)) {
757 charids[charstorepos] = chr->charid;
758 charadvance[charstorepos] = advance;
761 chardata = chardata->next;
766 static void chararray_destroy(chararray_t*chr)
769 chararray_t*next = chr->next;
776 static inline int matrix_diff(MATRIX*m1, MATRIX*m2)
778 return memcmp(m1,m2,sizeof(MATRIX));
780 static charbuffer_t*charbuffer_append(charbuffer_t*buf, SWFFONT*font, int charid, int x,int y, int size, RGBA color, MATRIX*m)
782 if(!buf || matrix_diff(&buf->matrix,m)) {
783 charbuffer_t*n = rfx_calloc(sizeof(charbuffer_t));
788 if(!buf->last || buf->last->pos == CHARDATAMAX) {
789 chararray_t*n = rfx_calloc(sizeof(chararray_t));
791 buf->array = buf->last = n;
797 chararray_t*a = buf->last;
798 a->chr[a->pos].font = font;
799 a->chr[a->pos].charid = charid;
800 a->chr[a->pos].x = x;
801 a->chr[a->pos].y = y;
802 a->chr[a->pos].color = color;
803 a->chr[a->pos].size = size;
808 /* Notice: we can only put chars in the range -1639,1638 (-32768/20,32768/20).
809 So if we set this value to high, the char coordinates will overflow.
810 If we set it to low, however, the char positions will be inaccurate */
811 #define GLYPH_SCALE 1
813 static void chararray_writetodev(gfxdevice_t*dev, chararray_t*array, MATRIX*matrix, char invisible)
815 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
817 int textid = getNewID(dev);
818 i->tag = swf_InsertTag(i->tag,ST_DEFINETEXT2);
819 swf_SetU16(i->tag, textid);
821 r = getcharacterbbox(array, matrix, i->config_flashversion);
822 r = swf_ClipRect(i->pagebbox, r);
823 swf_SetRect(i->tag,&r);
824 swf_SetMatrix(i->tag, matrix);
825 msg("<trace> Placing text as ID %d", textid);
826 chararray_writetotag(array, i->tag);
831 if(i->swf->fileVersion >= 8) {
832 i->tag = swf_InsertTag(i->tag, ST_CSMTEXTSETTINGS);
833 swf_SetU16(i->tag, textid);
835 //swf_SetU8(i->tag, /*subpixel grid*/(2<<3)|/*flashtype*/0x40);
836 swf_SetU8(i->tag, /*grid*/(1<<3)|/*flashtype*/0x40);
837 //swf_SetU8(i->tag, /*no grid*/(0<<3)|/*flashtype*/0x40);
839 swf_SetU32(i->tag, 0);//thickness
840 swf_SetU32(i->tag, 0);//sharpness
841 //swf_SetU32(i->tag, 0x20000);//thickness
842 //swf_SetU32(i->tag, 0x800000);//sharpness
843 swf_SetU8(i->tag, 0);//reserved
845 if(invisible && i->config_flashversion>=8) {
846 i->tag = swf_InsertTag(i->tag,ST_PLACEOBJECT3);
847 swf_ObjectPlaceBlend(i->tag,textid,getNewDepth(dev),&i->page_matrix,NULL,NULL,BLENDMODE_MULTIPLY);
849 i->tag = swf_InsertTag(i->tag,ST_PLACEOBJECT2);
850 swf_ObjectPlace(i->tag,textid,getNewDepth(dev),&i->page_matrix,NULL,NULL);
854 static void charbuffer_writetodevandfree(gfxdevice_t*dev, charbuffer_t*buf, char invisible)
857 charbuffer_t*next = buf->next;buf->next = 0;
858 chararray_writetodev(dev, buf->array, &buf->matrix, invisible);
859 chararray_destroy(buf->array);
865 static void endtext(gfxdevice_t*dev)
867 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
870 charbuffer_writetodevandfree(dev, i->chardata, 0);i->chardata = 0;
874 static int watermark2_width=47;
875 static int watermark2_height=11;
876 static int watermark2[47] = {95,1989,71,0,2015,337,1678,0,2015,5,1921,320,1938,25,2006,1024,
877 1042,21,13,960,1039,976,8,2000,1359,1088,31,1989,321,1728,0,1152,
878 1344,832,0,1984,0,896,1088,1088,896,0,1984,128,256,512,1984};
880 static void draw_watermark(gfxdevice_t*dev, gfxbbox_t r, char drawall)
882 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
883 double wx = r.xmax / 5.0;
884 double tx = r.xmax*4.0 / 5.0;
885 double ty = r.ymax-wx*watermark2_height/watermark2_width;
886 double sx = (r.xmax - tx) / watermark2_width;
887 double sy = (r.ymax - ty) / watermark2_height;
890 if(ty > 0 && px > 1.0 && py > 1.0) {
892 for(y=0;y<watermark2_height;y++)
893 for(x=0;x<watermark2_width;x++) {
894 if(((watermark2[x]>>y)&1)) {
895 if(!drawall && rand()%5)
897 unsigned int b = rand();
898 moveto(dev, i->tag, x*sx+tx+((b>>1)&1)/20.0, y*sy+ty+((b>>3)&1)/20.0);
899 lineto(dev, i->tag, x*sx+px+tx+((b>>2)&1)/20.0, y*sy+ty+((b>>3)&1)/20.0);
900 lineto(dev, i->tag, x*sx+px+tx+((b>>2)&1)/20.0, y*sy+py+ty+((b>>4)&1)/20.0);
901 lineto(dev, i->tag, x*sx+tx+((b>>1)&1)/20.0, y*sy+py+ty+((b>>4)&1)/20.0);
902 lineto(dev, i->tag, x*sx+tx+((b>>1)&1)/20.0, y*sy+ty+((b>>3)&1)/20.0);
908 static void swfoutput_setfillcolor(gfxdevice_t* dev, U8 r, U8 g, U8 b, U8 a)
910 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
911 if(i->fillrgb.r == r &&
914 i->fillrgb.a == a) return;
923 static void insert_watermark(gfxdevice_t*dev, char drawall)
925 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
926 if(!drawall && i->watermarks>20)
932 swfoutput_setfillcolor(dev, 0,0,255,192);
934 swfoutput_setfillcolor(dev, rand(),rand(),rand(),(rand()&127)+128);
939 gfxbbox_t r; r.xmin = r.ymin = 0;
942 draw_watermark(dev, r, drawall);
948 static void endpage(gfxdevice_t*dev)
950 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
958 charbuffer_writetodevandfree(dev, i->topchardata, 1);
965 if(i->config_watermark) {
966 insert_watermark(dev, 1);
972 static void addViewer(gfxdevice_t* dev)
974 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
977 RGBA button_colors[3]= {{0xbf,0x00,0x00,0x80},{0xbf,0x20,0x20,0xc0}, {0xbf,0xc0,0xc0,0xff}};
979 int button_sizex = 20;
980 int button_sizey = 20;
982 RGBA black = {255,0,0,0};
984 i->tag = swf_InsertTag(i->tag,ST_DEFINESHAPE3);
986 int ls1 = swf_ShapeAddLineStyle(s,40,&black);
987 int fs1 = swf_ShapeAddSolidFillStyle(s,&button_colors[t/2]);
988 int shapeid = ids[t] = getNewID(dev);
989 swf_SetU16(i->tag,shapeid);
991 r.xmin = -20*button_sizex;
992 r.xmax = 20*button_sizex;
994 r.ymax = 40*button_sizey;
995 swf_SetRect(i->tag,&r); // set shape bounds
996 swf_SetShapeHeader(i->tag,s); // write all styles to tag
997 swf_ShapeSetAll(i->tag,s,0*button_sizex,0,ls1,fs1,0);
998 swf_ShapeSetLine(i->tag,s,(1-(t&1)*2)*20*button_sizex,20*button_sizey);
999 swf_ShapeSetLine(i->tag,s,-(1-(t&1)*2)*20*button_sizex,20*button_sizey);
1000 swf_ShapeSetLine(i->tag,s,0,-40*button_sizey);
1001 swf_ShapeSetEnd(i->tag); // finish drawing
1002 swf_ShapeFree(s); // clean shape structure (which isn't needed anymore after writing the tag)
1004 ActionTAG*a1=0,*a2=0,*a3=0;
1005 a1 = action_NextFrame(a1);
1006 a1 = action_Stop(a1);
1007 a1 = action_End(a1);
1009 a2 = action_PreviousFrame(a2);
1010 a2 = action_Stop(a2);
1011 a2 = action_End(a2);
1013 a3 = action_Stop(a3);
1014 a3 = action_End(a3);
1016 i->tag = swf_InsertTag(i->tag, ST_DOACTION);
1017 swf_ActionSet(i->tag,a3);
1019 i->tag = swf_InsertTag(i->tag,ST_DEFINEBUTTON);
1020 int buttonid1 = getNewID(dev);
1021 swf_SetU16(i->tag, buttonid1);
1022 swf_ButtonSetRecord(i->tag,BS_UP|BS_HIT,ids[0],1,NULL,NULL);
1023 swf_ButtonSetRecord(i->tag,BS_OVER,ids[2],1,NULL,NULL);
1024 swf_ButtonSetRecord(i->tag,BS_DOWN,ids[4],1,NULL,NULL);
1025 swf_SetU8(i->tag,0); // end of button records
1026 swf_ActionSet(i->tag,a1);
1028 i->tag = swf_InsertTag(i->tag,ST_DEFINEBUTTON);
1029 int buttonid2 = getNewID(dev);
1030 swf_SetU16(i->tag, buttonid2);
1031 swf_ButtonSetRecord(i->tag,BS_UP|BS_HIT,ids[1],1,NULL,NULL);
1032 swf_ButtonSetRecord(i->tag,BS_OVER,ids[3],1,NULL,NULL);
1033 swf_ButtonSetRecord(i->tag,BS_DOWN,ids[5],1,NULL,NULL);
1034 swf_SetU8(i->tag,0); // end of button records
1035 swf_ActionSet(i->tag,a2);
1037 i->tag = swf_InsertTag(i->tag,ST_PLACEOBJECT2);
1039 swf_GetMatrix(0, &m);
1040 m.tx = button_sizex*20+200;
1041 swf_ObjectPlace(i->tag, buttonid2, 65534,&m,0,0);
1042 i->tag = swf_InsertTag(i->tag,ST_PLACEOBJECT2);
1043 m.tx = button_sizex*20+200+200;
1044 swf_ObjectPlace(i->tag, buttonid1, 65535,&m,0,0);
1048 void swf_startframe(gfxdevice_t*dev, int width, int height)
1050 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1052 if(i->config_protect) {
1053 i->tag = swf_InsertTag(i->tag, ST_PROTECT);
1054 i->config_protect = 0;
1056 if(i->config_simpleviewer) {
1061 if(!i->firstpage && !i->pagefinished)
1064 msg("<verbose> Starting new SWF page of size %dx%d", width, height);
1066 swf_GetMatrix(0, &i->page_matrix);
1067 i->page_matrix.tx = 0;
1068 i->page_matrix.ty = 0;
1075 /* create a bbox structure with the page size. This is used
1076 for clipping shape and text bounding boxes. As we don't want to
1077 generate bounding boxes which extend beyond the movie size (in
1078 order to not confuse Flash), we clip everything against i->pagebbox */
1079 i->pagebbox.xmin = 0;
1080 i->pagebbox.ymin = 0;
1081 i->pagebbox.xmax = width*20;
1082 i->pagebbox.ymax = height*20;
1084 /* increase SWF's bounding box */
1085 swf_ExpandRect2(&i->swf->movieSize, &i->pagebbox);
1087 i->lastframeno = i->frameno;
1089 i->pagefinished = 0;
1093 void swf_endframe(gfxdevice_t*dev)
1095 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1097 if(!i->pagefinished)
1100 if( (i->swf->fileVersion <= 8) && (i->config_insertstoptag) ) {
1102 atag = action_Stop(atag);
1103 atag = action_End(atag);
1104 i->tag = swf_InsertTag(i->tag,ST_DOACTION);
1105 swf_ActionSet(i->tag,atag);
1107 i->tag = swf_InsertTag(i->tag,ST_SHOWFRAME);
1110 for(i->depth;i->depth>i->startdepth;i->depth--) {
1111 i->tag = swf_InsertTag(i->tag,ST_REMOVEOBJECT2);
1112 swf_SetU16(i->tag,i->depth);
1114 i->depth = i->startdepth;
1116 if(i->config_frameresets) {
1117 for(i->currentswfid;i->currentswfid>i->startids;i->currentswfid--) {
1118 i->tag = swf_InsertTag(i->tag,ST_FREECHARACTER);
1119 swf_SetU16(i->tag,i->currentswfid);
1121 i->currentswfid = i->startids;
1125 static void setBackground(gfxdevice_t*dev, int x1, int y1, int x2, int y2)
1127 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1129 rgb.a = rgb.r = rgb.g = rgb.b = 0xff;
1133 int shapeid = getNewID(dev);
1138 i->tag = swf_InsertTag(i->tag, ST_DEFINESHAPE);
1140 fs1 = swf_ShapeAddSolidFillStyle(s, &rgb);
1141 swf_SetU16(i->tag,shapeid);
1142 swf_SetRect(i->tag,&r);
1143 swf_SetShapeHeader(i->tag,s);
1144 swf_ShapeSetAll(i->tag,s,x1,y1,ls1,fs1,0);
1145 swf_ShapeSetLine(i->tag,s,(x2-x1),0);
1146 swf_ShapeSetLine(i->tag,s,0,(y2-y1));
1147 swf_ShapeSetLine(i->tag,s,(x1-x2),0);
1148 swf_ShapeSetLine(i->tag,s,0,(y1-y2));
1149 swf_ShapeSetEnd(i->tag);
1151 i->tag = swf_InsertTag(i->tag, ST_PLACEOBJECT2);
1152 swf_ObjectPlace(i->tag,shapeid,getNewDepth(dev),0,0,0);
1153 i->tag = swf_InsertTag(i->tag, ST_PLACEOBJECT2);
1154 swf_ObjectPlaceClip(i->tag,shapeid,getNewDepth(dev),0,0,0,65535);
1157 /* initialize the swf writer */
1158 void gfxdevice_swf_init(gfxdevice_t* dev)
1160 memset(dev, 0, sizeof(gfxdevice_t));
1164 dev->internal = init_internal_struct(); // set config to default values
1166 dev->startpage = swf_startframe;
1167 dev->endpage = swf_endframe;
1168 dev->finish = swf_finish;
1169 dev->fillbitmap = swf_fillbitmap;
1170 dev->setparameter = swf_setparameter;
1171 dev->stroke = swf_stroke;
1172 dev->startclip = swf_startclip;
1173 dev->endclip = swf_endclip;
1174 dev->fill = swf_fill;
1175 dev->fillbitmap = swf_fillbitmap;
1176 dev->fillgradient = swf_fillgradient;
1177 dev->addfont = swf_addfont;
1178 dev->drawchar = swf_drawchar;
1179 dev->drawlink = swf_drawlink;
1181 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1184 msg("<verbose> initializing swf output\n", i->max_x,i->max_y);
1188 i->swf = (SWF*)rfx_calloc(sizeof(SWF));
1189 i->swf->fileVersion = 0;
1190 i->swf->frameRate = 0x80;
1191 i->swf->movieSize.xmin = 0;
1192 i->swf->movieSize.ymin = 0;
1193 i->swf->movieSize.xmax = 0;
1194 i->swf->movieSize.ymax = 0;
1195 i->swf->fileAttributes = 9; // as3, local-with-network
1197 i->swf->firstTag = swf_InsertTag(NULL,ST_SETBACKGROUNDCOLOR);
1198 i->tag = i->swf->firstTag;
1200 rgb.a = rgb.r = rgb.g = rgb.b = 0xff;
1202 swf_SetRGB(i->tag,&rgb);
1204 i->startdepth = i->depth = 0;
1205 i->startids = i->currentswfid = 0;
1208 static void startshape(gfxdevice_t*dev)
1210 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1215 //if(i->chardatapos && i->chardata[i->chardatapos-1].color.a)
1218 i->tag = swf_InsertTag(i->tag,ST_DEFINESHAPE3);
1220 swf_ShapeNew(&i->shape);
1221 i->linestyleid = swf_ShapeAddLineStyle(i->shape,i->linewidth,&i->strokergb);
1222 i->fillstyleid = swf_ShapeAddSolidFillStyle(i->shape,&i->fillrgb);
1224 RGBA markcol = {0,i->mark[0],i->mark[1],i->mark[2]};
1225 swf_ShapeAddSolidFillStyle(i->shape,&markcol);
1228 i->shapeid = getNewID(dev);
1230 msg("<debug> Using shape id %d", i->shapeid);
1232 swf_SetU16(i->tag,i->shapeid); // ID
1234 i->bboxrectpos = i->tag->len;
1236 swf_SetRect(i->tag,&i->pagebbox);
1238 memset(&i->bboxrect, 0, sizeof(i->bboxrect));
1240 swf_SetShapeStyles(i->tag,i->shape);
1241 swf_ShapeCountBits(i->shape,NULL,NULL);
1242 swf_SetShapeBits(i->tag,i->shape);
1244 /* TODO: do we really need this? */
1245 //swf_ShapeSetAll(i->tag,i->shape,/*x*/0,/*y*/0,i->linestyleid,0,0);
1246 //swf_ShapeSetAll(i->tag,i->shape,/*x*/UNDEFINED_COORD,/*y*/UNDEFINED_COORD,i->linestyleid,0,0);
1247 i->swflastx=i->swflasty=UNDEFINED_COORD;
1248 i->lastwasfill = -1;
1249 i->shapeisempty = 1;
1252 static void starttext(gfxdevice_t*dev)
1254 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1258 if(i->config_watermark) {
1259 insert_watermark(dev, 0);
1262 i->swflastx=i->swflasty=0;
1266 /* TODO: move to ../lib/rfxswf */
1267 void changeRect(gfxdevice_t*dev, TAG*tag, int pos, SRECT*newrect)
1269 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1270 /* determine length of old rect */
1274 swf_GetRect(tag, &old);
1275 swf_ResetReadBits(tag);
1276 int pos_end = tag->pos;
1278 int len = tag->len - pos_end;
1279 U8*data = (U8*)malloc(len);
1280 memcpy(data, &tag->data[pos_end], len);
1283 swf_SetRect(tag, newrect);
1284 swf_SetBlock(tag, data, len);
1286 tag->pos = tag->readBit = 0;
1289 void cancelshape(gfxdevice_t*dev)
1291 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1292 /* delete old shape tag */
1294 i->tag = i->tag->prev;
1295 swf_DeleteTag(0, todel);
1296 if(i->shape) {swf_ShapeFree(i->shape);i->shape=0;}
1298 i->bboxrectpos = -1;
1300 // i->currentswfid--; // doesn't work, for some reason
1303 void fixAreas(gfxdevice_t*dev)
1305 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1306 if(!i->shapeisempty && i->fill &&
1307 (i->bboxrect.xmin == i->bboxrect.xmax ||
1308 i->bboxrect.ymin == i->bboxrect.ymax) &&
1309 i->config_minlinewidth >= 0.001
1311 msg("<debug> Shape has size 0: width=%.2f height=%.2f",
1312 (i->bboxrect.xmax-i->bboxrect.xmin)/20.0,
1313 (i->bboxrect.ymax-i->bboxrect.ymin)/20.0
1316 SRECT r = i->bboxrect;
1318 if(r.xmin == r.xmax && r.ymin == r.ymax) {
1319 /* this thing comes down to a single dot- nothing to fix here */
1325 RGBA save_col = i->strokergb;
1326 int save_width = i->linewidth;
1328 i->strokergb = i->fillrgb;
1329 i->linewidth = (int)(i->config_minlinewidth*20);
1330 if(i->linewidth==0) i->linewidth = 1;
1335 moveto(dev, i->tag, r.xmin/20.0,r.ymin/20.0);
1336 lineto(dev, i->tag, r.xmax/20.0,r.ymax/20.0);
1338 i->strokergb = save_col;
1339 i->linewidth = save_width;
1344 static void endshape_noput(gfxdevice_t*dev)
1346 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1349 //changeRect(dev, i->tag, i->bboxrectpos, &i->bboxrect);
1352 swf_ShapeFree(i->shape);
1360 static void endshape(gfxdevice_t*dev)
1362 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1368 if(i->shapeisempty ||
1370 (i->bboxrect.xmin == i->bboxrect.xmax &&
1371 i->bboxrect.ymin == i->bboxrect.ymax))
1373 // delete the shape again, we didn't do anything
1374 msg("<debug> cancelling shape: bbox is (%f,%f,%f,%f)",
1375 i->bboxrect.xmin /20.0,
1376 i->bboxrect.ymin /20.0,
1377 i->bboxrect.xmax /20.0,
1378 i->bboxrect.ymax /20.0
1384 swf_ShapeSetEnd(i->tag);
1386 SRECT r = swf_ClipRect(i->pagebbox, i->bboxrect);
1387 changeRect(dev, i->tag, i->bboxrectpos, &r);
1389 msg("<trace> Placing shape ID %d", i->shapeid);
1391 i->tag = swf_InsertTag(i->tag,ST_PLACEOBJECT2);
1392 MATRIX m = i->page_matrix;
1393 m.tx += i->shapeposx;
1394 m.ty += i->shapeposy;
1395 swf_ObjectPlace(i->tag,i->shapeid,getNewDepth(dev),&m,NULL,NULL);
1397 if(i->config_animate) {
1398 i->tag = swf_InsertTag(i->tag,ST_SHOWFRAME);
1401 swf_ShapeFree(i->shape);
1404 i->bboxrectpos = -1;
1411 void wipeSWF(SWF*swf)
1413 TAG*tag = swf->firstTag;
1415 TAG*next = tag->next;
1416 if(tag->id != ST_SETBACKGROUNDCOLOR &&
1417 tag->id != ST_END &&
1418 tag->id != ST_DOACTION &&
1419 tag->id != ST_SHOWFRAME) {
1420 swf_DeleteTag(swf, tag);
1426 void swfoutput_finalize(gfxdevice_t*dev)
1428 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1430 if(i->tag && i->tag->id == ST_END)
1431 return; //already done
1433 i->swf->fileVersion = i->config_flashversion;
1434 i->swf->frameRate = i->config_framerate*0x100;
1436 if(i->config_bboxvars) {
1437 TAG* tag = swf_InsertTag(i->swf->firstTag, ST_DOACTION);
1439 a = action_PushString(a, "xmin");
1440 a = action_PushFloat(a, i->swf->movieSize.xmin / 20.0);
1441 a = action_SetVariable(a);
1442 a = action_PushString(a, "ymin");
1443 a = action_PushFloat(a, i->swf->movieSize.ymin / 20.0);
1444 a = action_SetVariable(a);
1445 a = action_PushString(a, "xmax");
1446 a = action_PushFloat(a, i->swf->movieSize.xmax / 20.0);
1447 a = action_SetVariable(a);
1448 a = action_PushString(a, "ymax");
1449 a = action_PushFloat(a, i->swf->movieSize.ymax / 20.0);
1450 a = action_SetVariable(a);
1451 a = action_PushString(a, "width");
1452 a = action_PushFloat(a, (i->swf->movieSize.xmax - i->swf->movieSize.xmin) / 20.0);
1453 a = action_SetVariable(a);
1454 a = action_PushString(a, "height");
1455 a = action_PushFloat(a, (i->swf->movieSize.ymax - i->swf->movieSize.ymin) / 20.0);
1456 a = action_SetVariable(a);
1458 swf_ActionSet(tag, a);
1463 free(i->mark);i->mark = 0;
1467 fontlist_t *iterator = i->fontlist;
1469 TAG*mtag = i->swf->firstTag;
1470 if(iterator->swffont) {
1471 if(!i->config_storeallcharacters) {
1472 msg("<debug> Reducing font %s", iterator->swffont->name);
1473 swf_FontReduce(iterator->swffont);
1475 int used = iterator->swffont->use && iterator->swffont->use->used_glyphs;
1477 if(i->config_flashversion<8 || NO_FONT3) {
1478 mtag = swf_InsertTag(mtag, ST_DEFINEFONT2);
1479 swf_FontSetDefine2(mtag, iterator->swffont);
1481 mtag = swf_InsertTag(mtag, ST_DEFINEFONT3);
1482 swf_FontSetDefine2(mtag, iterator->swffont);
1484 swf_FontCreateAlignZones(iterator->swffont);
1486 if(iterator->swffont->alignzones) {
1487 mtag = swf_InsertTag(mtag, ST_DEFINEFONTALIGNZONES);
1488 swf_FontSetAlignZones(mtag, iterator->swffont);
1494 iterator = iterator->next;
1497 i->tag = swf_InsertTag(i->tag,ST_END);
1498 TAG* tag = i->tag->prev;
1500 /* remove the removeobject2 tags between the last ST_SHOWFRAME
1501 and the ST_END- they confuse the flash player */
1502 while(tag->id == ST_REMOVEOBJECT2) {
1503 TAG* prev = tag->prev;
1504 swf_DeleteTag(i->swf, tag);
1511 if(i->config_enablezlib || i->config_flashversion>=6) {
1512 i->swf->compressed = 1;
1515 /* Add AVM2 actionscript */
1516 if(i->config_flashversion>=9 &&
1517 (i->config_insertstoptag || i->hasbuttons) && !i->config_linknameurl) {
1518 swf_AddButtonLinks(i->swf, i->config_insertstoptag,
1519 i->config_internallinkfunction||i->config_externallinkfunction);
1521 // if(i->config_reordertags)
1522 // swf_Optimize(i->swf);
1525 int swfresult_save(gfxresult_t*gfx, const char*filename)
1527 SWF*swf = (SWF*)gfx->internal;
1530 fi = open(filename, O_BINARY|O_CREAT|O_TRUNC|O_WRONLY, 0777);
1535 msg("<fatal> Could not create \"%s\". ", FIXNULL(filename));
1539 if FAILED(swf_WriteSWF(fi,swf))
1540 msg("<error> WriteSWF() failed.\n");
1546 void* swfresult_get(gfxresult_t*gfx, const char*name)
1548 SWF*swf = (SWF*)gfx->internal;
1549 if(!strcmp(name, "swf")) {
1550 return (void*)swf_CopySWF(swf);
1551 } else if(!strcmp(name, "xmin")) {
1552 return (void*)(ptroff_t)(swf->movieSize.xmin/20);
1553 } else if(!strcmp(name, "ymin")) {
1554 return (void*)(ptroff_t)(swf->movieSize.ymin/20);
1555 } else if(!strcmp(name, "xmax")) {
1556 return (void*)(ptroff_t)(swf->movieSize.xmax/20);
1557 } else if(!strcmp(name, "ymax")) {
1558 return (void*)(ptroff_t)(swf->movieSize.ymax/20);
1559 } else if(!strcmp(name, "width")) {
1560 return (void*)(ptroff_t)((swf->movieSize.xmax - swf->movieSize.xmin)/20);
1561 } else if(!strcmp(name, "height")) {
1562 return (void*)(ptroff_t)((swf->movieSize.ymax - swf->movieSize.ymin)/20);
1566 void swfresult_destroy(gfxresult_t*gfx)
1569 swf_FreeTags((SWF*)gfx->internal);
1570 free(gfx->internal);
1573 memset(gfx, 0, sizeof(gfxresult_t));
1577 static void swfoutput_destroy(gfxdevice_t* dev);
1579 gfxresult_t* swf_finish(gfxdevice_t* dev)
1581 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1584 if(i->config_linktarget) {
1585 free(i->config_linktarget);
1586 i->config_linktarget = 0;
1589 swfoutput_finalize(dev);
1590 SWF* swf = i->swf;i->swf = 0;
1591 swfoutput_destroy(dev);
1593 result = (gfxresult_t*)rfx_calloc(sizeof(gfxresult_t));
1594 result->internal = swf;
1595 result->save = swfresult_save;
1597 result->get = swfresult_get;
1598 result->destroy = swfresult_destroy;
1602 /* Perform cleaning up */
1603 static void swfoutput_destroy(gfxdevice_t* dev)
1605 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1607 /* not initialized yet- nothing to destroy */
1611 fontlist_t *tmp,*iterator = i->fontlist;
1613 if(iterator->swffont) {
1614 swf_FontFree(iterator->swffont);iterator->swffont=0;
1617 iterator = iterator->next;
1620 if(i->swf) {swf_FreeTags(i->swf);free(i->swf);i->swf = 0;}
1623 memset(dev, 0, sizeof(gfxdevice_t));
1626 static void swfoutput_setstrokecolor(gfxdevice_t* dev, U8 r, U8 g, U8 b, U8 a)
1628 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1629 if(i->strokergb.r == r &&
1630 i->strokergb.g == g &&
1631 i->strokergb.b == b &&
1632 i->strokergb.a == a) return;
1642 //#define ROUND_UP 19
1643 //#define ROUND_UP 10
1645 static void swfoutput_setlinewidth(gfxdevice_t*dev, double _linewidth)
1647 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1648 if(i->linewidth == (U16)(_linewidth*20+19.0/20.0))
1652 i->linewidth = (U16)(_linewidth*20+19.0/20.0);
1656 static void drawlink(gfxdevice_t*dev, ActionTAG*,ActionTAG*, gfxline_t*points, char mouseover, char*type, const char*url);
1657 static void swfoutput_namedlink(gfxdevice_t*dev, char*name, gfxline_t*points);
1658 static void swfoutput_linktopage(gfxdevice_t*dev, int page, gfxline_t*points);
1659 static void swfoutput_linktourl(gfxdevice_t*dev, const char*url, gfxline_t*points);
1661 /*void swfoutput_drawlink(gfxdevice_t*dev, char*url, gfxline_t*points)
1663 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1664 dev->drawlink(dev, points, url);
1667 void swf_drawlink(gfxdevice_t*dev, gfxline_t*points, const char*url)
1669 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1671 if(i->config_disablelinks)
1674 if(!strncmp("http://pdf2swf:", url, 15)) {
1675 char*tmp = strdup(url);
1676 int l = strlen(tmp);
1679 swfoutput_namedlink(dev, tmp+15, points);
1682 } else if(!strncmp("page", url, 4)) {
1685 if(url[t]<'0' || url[t]>'9')
1688 int page = atoi(&url[4]);
1689 if(page<0) page = 0;
1690 swfoutput_linktopage(dev, page, points);
1693 swfoutput_linktourl(dev, url, points);
1696 void swfoutput_linktourl(gfxdevice_t*dev, const char*url, gfxline_t*points)
1698 ActionTAG* actions = 0;
1699 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1705 /* TODO: escape special characters in url */
1707 if(i->config_externallinkfunction && i->config_flashversion<=8) {
1708 actions = action_PushString(actions, url); //parameter
1709 actions = action_PushInt(actions, 1); //number of parameters (1)
1710 actions = action_PushString(actions, i->config_externallinkfunction); //function name
1711 actions = action_CallFunction(actions);
1712 } else if(!i->config_linktarget) {
1713 if(!i->config_opennewwindow)
1714 actions = action_GetUrl(actions, url, "_parent");
1716 actions = action_GetUrl(actions, url, "_this");
1718 actions = action_GetUrl(actions, url, i->config_linktarget);
1720 actions = action_End(actions);
1722 drawlink(dev, actions, 0, points, 0, "url", url);
1724 swf_ActionFree(actions);
1726 void swfoutput_linktopage(gfxdevice_t*dev, int page, gfxline_t*points)
1728 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1729 ActionTAG* actions = 0;
1736 if(!i->config_internallinkfunction || i->config_flashversion>=9) {
1737 actions = action_GotoFrame(actions, page-1);
1738 actions = action_End(actions);
1740 actions = action_PushInt(actions, page); //parameter
1741 actions = action_PushInt(actions, 1); //number of parameters (1)
1742 actions = action_PushString(actions, i->config_internallinkfunction); //function name
1743 actions = action_CallFunction(actions);
1744 actions = action_End(actions);
1748 sprintf(name, "page%d", page);
1750 drawlink(dev, actions, 0, points, 0, "page", name);
1752 swf_ActionFree(actions);
1755 /* Named Links (a.k.a. Acrobatmenu) are used to implement various gadgets
1756 of the viewer objects, like subtitles, index elements etc.
1758 void swfoutput_namedlink(gfxdevice_t*dev, char*name, gfxline_t*points)
1760 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1761 ActionTAG *actions1,*actions2;
1762 char*tmp = strdup(name);
1771 if(!strncmp(tmp, "call:", 5))
1773 char*x = strchr(&tmp[5], ':');
1775 actions1 = action_PushInt(0, 0); //number of parameters (0)
1776 actions1 = action_PushString(actions1, &tmp[5]); //function name
1777 actions1 = action_CallFunction(actions1);
1778 actions1 = action_End(actions1);
1781 actions1 = action_PushString(0, x+1); //parameter
1782 actions1 = action_PushInt(actions1, 1); //number of parameters (1)
1783 actions1 = action_PushString(actions1, &tmp[5]); //function name
1784 actions1 = action_CallFunction(actions1);
1785 actions1 = action_End(actions1);
1787 actions2 = action_End(0);
1793 actions1 = action_PushString(0, "/:subtitle");
1794 actions1 = action_PushString(actions1, name);
1795 actions1 = action_SetVariable(actions1);
1796 actions1 = action_End(actions1);
1798 actions2 = action_PushString(0, "/:subtitle");
1799 actions2 = action_PushString(actions2, "");
1800 actions2 = action_SetVariable(actions2);
1801 actions2 = action_End(actions2);
1805 drawlink(dev, actions1, actions2, points, mouseover, type, name);
1807 swf_ActionFree(actions1);
1808 swf_ActionFree(actions2);
1812 static void drawgfxline(gfxdevice_t*dev, gfxline_t*line, int fill)
1814 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1815 gfxcoord_t lastx=0,lasty=0,px=0,py=0;
1817 int lines= 0, splines=0;
1824 /* check whether the next segment is zero */
1825 if(line->type == gfx_moveTo) {
1826 moveto(dev, i->tag, line->x, line->y);
1827 px = lastx = line->x;
1828 py = lasty = line->y;
1830 } if(line->type == gfx_lineTo) {
1831 lineto(dev, i->tag, line->x, line->y);
1836 } else if(line->type == gfx_splineTo) {
1838 s.x = line->sx;p.x = line->x;
1839 s.y = line->sy;p.y = line->y;
1840 splineto(dev, i->tag, s, p);
1848 msg("<trace> drawgfxline, %d lines, %d splines", lines, splines);
1852 static void drawlink(gfxdevice_t*dev, ActionTAG*actions1, ActionTAG*actions2, gfxline_t*points, char mouseover, char*type, const char*url)
1854 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1863 int buttonid = getNewID(dev);
1864 gfxbbox_t bbox = gfxline_getbbox(points);
1866 if(i->config_linknameurl) {
1874 myshapeid = getNewID(dev);
1875 i->tag = swf_InsertTag(i->tag,ST_DEFINESHAPE3);
1876 swf_ShapeNew(&i->shape);
1877 rgb.r = rgb.b = rgb.a = rgb.g = 0;
1878 fsid = swf_ShapeAddSolidFillStyle(i->shape,&rgb);
1879 swf_SetU16(i->tag, myshapeid);
1880 r.xmin = (int)(bbox.xmin*20);
1881 r.ymin = (int)(bbox.ymin*20);
1882 r.xmax = (int)(bbox.xmax*20);
1883 r.ymax = (int)(bbox.ymax*20);
1884 r = swf_ClipRect(i->pagebbox, r);
1885 swf_SetRect(i->tag,&r);
1886 swf_SetShapeStyles(i->tag,i->shape);
1887 swf_ShapeCountBits(i->shape,NULL,NULL);
1888 swf_SetShapeBits(i->tag,i->shape);
1889 swf_ShapeSetAll(i->tag,i->shape,/*x*/0,/*y*/0,0,fsid,0);
1890 i->swflastx = i->swflasty = 0;
1891 drawgfxline(dev, points, 1);
1892 swf_ShapeSetEnd(i->tag);
1893 swf_ShapeFree(i->shape);
1896 myshapeid2 = getNewID(dev);
1897 i->tag = swf_InsertTag(i->tag,ST_DEFINESHAPE3);
1898 swf_ShapeNew(&i->shape);
1900 rgb = i->config_linkcolor;
1902 fsid = swf_ShapeAddSolidFillStyle(i->shape,&rgb);
1903 swf_SetU16(i->tag, myshapeid2);
1904 r.xmin = (int)(bbox.xmin*20);
1905 r.ymin = (int)(bbox.ymin*20);
1906 r.xmax = (int)(bbox.xmax*20);
1907 r.ymax = (int)(bbox.ymax*20);
1908 r = swf_ClipRect(i->pagebbox, r);
1909 swf_SetRect(i->tag,&r);
1910 swf_SetShapeStyles(i->tag,i->shape);
1911 swf_ShapeCountBits(i->shape,NULL,NULL);
1912 swf_SetShapeBits(i->tag,i->shape);
1913 swf_ShapeSetAll(i->tag,i->shape,/*x*/0,/*y*/0,0,fsid,0);
1914 i->swflastx = i->swflasty = 0;
1915 drawgfxline(dev, points, 1);
1916 swf_ShapeSetEnd(i->tag);
1917 swf_ShapeFree(i->shape);
1921 i->tag = swf_InsertTag(i->tag,ST_DEFINEBUTTON);
1922 swf_SetU16(i->tag,buttonid); //id
1923 swf_ButtonSetFlags(i->tag, 0); //menu=no
1924 swf_ButtonSetRecord(i->tag,0x01,myshapeid,i->depth,0,0);
1925 swf_ButtonSetRecord(i->tag,0x02,myshapeid2,i->depth,0,0);
1926 swf_ButtonSetRecord(i->tag,0x04,myshapeid2,i->depth,0,0);
1927 swf_ButtonSetRecord(i->tag,0x08,myshapeid,i->depth,0,0);
1928 swf_SetU8(i->tag,0);
1929 swf_ActionSet(i->tag,actions1);
1930 swf_SetU8(i->tag,0);
1934 i->tag = swf_InsertTag(i->tag,ST_DEFINEBUTTON2);
1935 swf_SetU16(i->tag,buttonid); //id
1936 swf_ButtonSetFlags(i->tag, 0); //menu=no
1937 swf_ButtonSetRecord(i->tag,0x01,myshapeid,i->depth,0,0);
1938 swf_ButtonSetRecord(i->tag,0x02,myshapeid2,i->depth,0,0);
1939 swf_ButtonSetRecord(i->tag,0x04,myshapeid2,i->depth,0,0);
1940 swf_ButtonSetRecord(i->tag,0x08,myshapeid,i->depth,0,0);
1941 swf_SetU8(i->tag,0); // end of button records
1942 swf_ButtonSetCondition(i->tag, BC_IDLE_OVERUP);
1943 swf_ActionSet(i->tag,actions1);
1945 swf_ButtonSetCondition(i->tag, BC_OVERUP_IDLE);
1946 swf_ActionSet(i->tag,actions2);
1947 swf_SetU8(i->tag,0);
1948 swf_ButtonPostProcess(i->tag, 2);
1950 swf_SetU8(i->tag,0);
1951 swf_ButtonPostProcess(i->tag, 1);
1957 const char* name = 0;
1958 if(i->config_linknameurl) {
1959 buf2 = malloc(strlen(type)+strlen(url)+2);
1960 sprintf(buf2, "%s:%s", type, url);
1964 sprintf(buf, "button%d", buttonid);
1967 msg("<trace> Placing link ID %d", buttonid);
1968 i->tag = swf_InsertTag(i->tag,ST_PLACEOBJECT2);
1970 if(posx!=0 || posy!=0) {
1972 p.x = (int)(posx*20);
1973 p.y = (int)(posy*20);
1974 p = swf_TurnPoint(p, &i->page_matrix);
1979 swf_ObjectPlace(i->tag, buttonid, getNewDepth(dev),&m,0,(U8*)name);
1981 swf_ObjectPlace(i->tag, buttonid, getNewDepth(dev),&i->page_matrix,0,(U8*)name);
1991 for(t=0;t<picpos;t++)
1993 if(pic_xids[t] == xid &&
1994 pic_yids[t] == yid) {
1995 width = pic_width[t];
1996 height = pic_height[t];
2000 pic_ids[picpos] = swfoutput_drawimagelosslessN(&output, pic, pal, width, height, x1,y1,x2,y2,x3,y3,x4,y4, numpalette);
2001 pic_xids[picpos] = xid;
2002 pic_yids[picpos] = yid;
2003 pic_width[picpos] = width;
2004 pic_height[picpos] = height;
2007 pic[width*y+x] = buf[0];
2011 xid += pal[1].r*3 + pal[1].g*11 + pal[1].b*17;
2012 yid += pal[1].r*7 + pal[1].g*5 + pal[1].b*23;
2016 xid += x*r+x*b*3+x*g*7+x*a*11;
2017 yid += y*r*3+y*b*17+y*g*19+y*a*11;
2019 for(t=0;t<picpos;t++)
2021 if(pic_xids[t] == xid &&
2022 pic_yids[t] == yid) {
2031 int swf_setparameter(gfxdevice_t*dev, const char*name, const char*value)
2033 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
2035 msg("<trace> swfdevice: %s=%s", name, value);
2036 if(!strcmp(name, "jpegsubpixels")) {
2037 i->config_jpegsubpixels = atof(value);
2038 } else if(!strcmp(name, "ppmsubpixels")) {
2039 i->config_ppmsubpixels = atof(value);
2040 } else if(!strcmp(name, "subpixels")) {
2041 i->config_ppmsubpixels = i->config_jpegsubpixels = atof(value);
2042 } else if(!strcmp(name, "drawonlyshapes")) {
2043 i->config_drawonlyshapes = atoi(value);
2044 } else if(!strcmp(name, "ignoredraworder")) {
2045 i->config_ignoredraworder = atoi(value);
2046 } else if(!strcmp(name, "mark")) {
2047 if(!value || !value[0]) {
2048 if(i->mark) free(i->mark);
2052 i->mark = strdup("...");
2053 for(t=0;t<3;t++) if(value[t]) i->mark[t] = value[t];
2055 } else if(!strcmp(name, "filloverlap")) {
2056 i->config_filloverlap = atoi(value);
2057 } else if(!strcmp(name, "linksopennewwindow")) {
2058 i->config_opennewwindow = atoi(value);
2059 } else if(!strcmp(name, "opennewwindow")) {
2060 i->config_opennewwindow = atoi(value);
2061 } else if(!strcmp(name, "storeallcharacters")) {
2062 i->config_storeallcharacters = atoi(value);
2063 } else if(!strcmp(name, "enablezlib")) {
2064 i->config_enablezlib = atoi(value);
2065 } else if(!strcmp(name, "bboxvars")) {
2066 i->config_bboxvars = atoi(value);
2067 } else if(!strcmp(name, "dots")) {
2068 i->config_dots = atoi(value);
2069 } else if(!strcmp(name, "frameresets")) {
2070 i->config_frameresets = atoi(value);
2071 } else if(!strcmp(name, "showclipshapes")) {
2072 i->config_showclipshapes = atoi(value);
2073 } else if(!strcmp(name, "reordertags")) {
2074 i->config_reordertags = atoi(value);
2075 } else if(!strcmp(name, "internallinkfunction")) {
2076 i->config_internallinkfunction = strdup(value);
2077 } else if(!strcmp(name, "externallinkfunction")) {
2078 i->config_externallinkfunction = strdup(value);
2079 } else if(!strcmp(name, "linkfunction")) { //sets both internallinkfunction and externallinkfunction
2080 i->config_internallinkfunction = strdup(value);
2081 i->config_externallinkfunction = strdup(value);
2082 } else if(!strcmp(name, "disable_polygon_conversion")) {
2083 i->config_disable_polygon_conversion = atoi(value);
2084 } else if(!strcmp(name, "normalize_polygon_positions")) {
2085 i->config_normalize_polygon_positions = atoi(value);
2086 } else if(!strcmp(name, "wxwindowparams")) {
2087 i->config_watermark = atoi(value);
2088 } else if(!strcmp(name, "insertstop")) {
2089 i->config_insertstoptag = atoi(value);
2090 } else if(!strcmp(name, "protect")) {
2091 i->config_protect = atoi(value);
2092 if(i->config_protect && i->tag) {
2093 i->tag = swf_InsertTag(i->tag, ST_PROTECT);
2095 } else if(!strcmp(name, "flashversion")) {
2096 i->config_flashversion = atoi(value);
2098 i->swf->fileVersion = i->config_flashversion;
2100 } else if(!strcmp(name, "framerate")) {
2101 i->config_framerate = atof(value);
2103 i->swf->frameRate = i->config_framerate*0x100;
2105 } else if(!strcmp(name, "minlinewidth")) {
2106 i->config_minlinewidth = atof(value);
2107 } else if(!strcmp(name, "caplinewidth")) {
2108 i->config_caplinewidth = atof(value);
2109 } else if(!strcmp(name, "linktarget")) {
2110 i->config_linktarget = strdup(value);
2111 } else if(!strcmp(name, "invisibletexttofront")) {
2112 i->config_invisibletexttofront = atoi(value);
2113 } else if(!strcmp(name, "noclips")) {
2114 i->config_noclips = atoi(value);
2115 } else if(!strcmp(name, "dumpfonts")) {
2116 i->config_dumpfonts = atoi(value);
2117 } else if(!strcmp(name, "animate")) {
2118 i->config_animate = atoi(value);
2119 } else if(!strcmp(name, "disablelinks")) {
2120 i->config_disablelinks = atoi(value);
2121 } else if(!strcmp(name, "simpleviewer")) {
2122 i->config_simpleviewer = atoi(value);
2123 } else if(!strcmp(name, "next_bitmap_is_jpeg")) {
2125 } else if(!strcmp(name, "jpegquality")) {
2126 int val = atoi(value);
2128 if(val>101) val=101;
2129 i->config_jpegquality = val;
2130 } else if(!strcmp(name, "splinequality")) {
2131 int v = atoi(value);
2132 v = 500-(v*5); // 100% = 0.25 pixel, 0% = 25 pixel
2134 i->config_splinemaxerror = v;
2135 } else if(!strcmp(name, "fontquality")) {
2136 int v = atoi(value);
2137 v = 500-(v*5); // 100% = 0.25 pixel, 0% = 25 pixel
2139 i->config_fontsplinemaxerror = v;
2140 } else if(!strcmp(name, "linkcolor")) {
2141 if(strlen(value)!=8) {
2142 fprintf(stderr, "Unknown format for option 'linkcolor'. (%s <-> RRGGBBAA)\n", value);
2145 # define NIBBLE(s) (((s)>='0' && (s)<='9')?((s)-'0'):((s)&0x0f)+9)
2146 i->config_linkcolor.r = NIBBLE(value[0])<<4 | NIBBLE(value[1]);
2147 i->config_linkcolor.g = NIBBLE(value[2])<<4 | NIBBLE(value[3]);
2148 i->config_linkcolor.b = NIBBLE(value[4])<<4 | NIBBLE(value[5]);
2149 i->config_linkcolor.a = NIBBLE(value[6])<<4 | NIBBLE(value[7]);
2150 } else if(!strcmp(name, "help")) {
2151 printf("\nSWF layer options:\n");
2152 printf("jpegsubpixels=<pixels> resolution adjustment for jpeg images (same as jpegdpi, but in pixels)\n");
2153 printf("ppmsubpixels=<pixels resolution adjustment for lossless images (same as ppmdpi, but in pixels)\n");
2154 printf("subpixels=<pixels> shortcut for setting both jpegsubpixels and ppmsubpixels\n");
2155 printf("drawonlyshapes convert everything to shapes (currently broken)\n");
2156 printf("ignoredraworder allow to perform a few optimizations for creating smaller SWFs\n");
2157 printf("linksopennewwindow make links open a new browser window\n");
2158 printf("linktarget target window name of new links\n");
2159 printf("linkcolor=<color) color of links (format: RRGGBBAA)\n");
2160 printf("linknameurl Link buttons will be named like the URL they refer to (handy for iterating through links with actionscript)\n");
2161 printf("storeallcharacters don't reduce the fonts to used characters in the output file\n");
2162 printf("enablezlib switch on zlib compression (also done if flashversion>=6)\n");
2163 printf("bboxvars store the bounding box of the SWF file in actionscript variables\n");
2164 printf("dots Take care to handle dots correctly\n");
2165 printf("reordertags=0/1 (default: 1) perform some tag optimizations\n");
2166 printf("internallinkfunction=<name> when the user clicks a internal link (to a different page) in the converted file, this actionscript function is called\n");
2167 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");
2168 printf("disable_polygon_conversion never convert strokes to polygons (will remove capstyles and joint styles)\n");
2169 printf("caplinewidth=<width> the minimum thichness a line needs to have so that capstyles become visible (and are converted)\n");
2170 printf("insertstop put an ActionScript \"STOP\" tag in every frame\n");
2171 printf("protect add a \"protect\" tag to the file, to prevent loading in the Flash editor\n");
2172 printf("flashversion=<version> the SWF fileversion (6)\n");
2173 printf("framerate=<fps> SWF framerate\n");
2174 printf("minlinewidth=<width> convert horizontal/vertical boxes smaller than this width to lines (0.05) \n");
2175 printf("simpleviewer Add next/previous buttons to the SWF\n");
2176 printf("animate insert a showframe tag after each placeobject (animate draw order of PDF files)\n");
2177 printf("jpegquality=<quality> set compression quality of jpeg images\n");
2178 printf("splinequality=<value> Set the quality of spline convertion to value (0-100, default: 100).\n");
2179 printf("disablelinks Disable links.\n");
2186 // --------------------------------------------------------------------
2188 static CXFORM gfxcxform_to_cxform(gfxcxform_t* c)
2191 swf_GetCXForm(0, &cx, 1);
2194 if(c->rg!=0 || c->rb!=0 || c->ra!=0 ||
2195 c->gr!=0 || c->gb!=0 || c->ga!=0 ||
2196 c->br!=0 || c->bg!=0 || c->ba!=0 ||
2197 c->ar!=0 || c->ag!=0 || c->ab!=0)
2198 msg("<warning> CXForm not SWF-compatible");
2200 cx.a0 = (S16)(c->aa*256);
2201 cx.r0 = (S16)(c->rr*256);
2202 cx.g0 = (S16)(c->gg*256);
2203 cx.b0 = (S16)(c->bb*256);
2212 static int imageInCache(gfxdevice_t*dev, void*data, int width, int height)
2216 static void addImageToCache(gfxdevice_t*dev, void*data, int width, int height)
2220 static int add_image(swfoutput_internal*i, gfximage_t*img, int targetwidth, int targetheight, int* newwidth, int* newheight)
2222 gfxdevice_t*dev = i->dev;
2224 RGBA*mem = (RGBA*)img->data;
2226 int sizex = img->width;
2227 int sizey = img->height;
2228 int is_jpeg = i->jpeg;
2231 int newsizex=sizex, newsizey=sizey;
2234 if(is_jpeg && i->config_jpegsubpixels) {
2235 newsizex = (int)(targetwidth*i->config_jpegsubpixels + 0.5);
2236 newsizey = (int)(targetheight*i->config_jpegsubpixels + 0.5);
2237 } else if(!is_jpeg && i->config_ppmsubpixels) {
2238 newsizex = (int)(targetwidth*i->config_ppmsubpixels + 0.5);
2239 newsizey = (int)(targetheight*i->config_ppmsubpixels + 0.5);
2243 if(sizex<=0 || sizey<=0)
2250 /* TODO: cache images */
2252 if(newsizex<sizex || newsizey<sizey) {
2253 msg("<verbose> Scaling %dx%d image to %dx%d", sizex, sizey, newsizex, newsizey);
2254 newpic = swf_ImageScale(mem, sizex, sizey, newsizex, newsizey);
2255 *newwidth = sizex = newsizex;
2256 *newheight = sizey = newsizey;
2259 *newwidth = newsizex = sizex;
2260 *newheight = newsizey = sizey;
2263 int num_colors = swf_ImageGetNumberOfPaletteEntries(mem,sizex,sizey,0);
2264 int has_alpha = swf_ImageHasAlpha(mem,sizex,sizey);
2266 msg("<verbose> Drawing %dx%d %s%simage (id %d) at size %dx%d (%dx%d), %s%d colors",
2268 has_alpha?(has_alpha==2?"semi-transparent ":"transparent "):"",
2269 is_jpeg?"jpeg-":"", i->currentswfid+1,
2271 targetwidth, targetheight,
2272 /*newsizex, newsizey,*/
2273 num_colors>256?">":"", num_colors>256?256:num_colors);
2275 /*RGBA* pal = (RGBA*)rfx_alloc(sizeof(RGBA)*num_colors);
2276 swf_ImageGetNumberOfPaletteEntries(mem,sizex,sizey,pal);
2278 for(t=0;t<num_colors;t++) {
2279 printf("%02x%02x%02x%02x ",
2280 pal[t].r, pal[t].g, pal[t].b, pal[t].a);
2287 int cacheid = imageInCache(dev, mem, sizex, sizey);
2290 bitid = getNewID(dev);
2292 i->tag = swf_AddImage(i->tag, bitid, mem, sizex, sizey, i->config_jpegquality);
2293 addImageToCache(dev, mem, sizex, sizey);
2303 static SRECT gfxline_getSWFbbox(gfxline_t*line)
2305 gfxbbox_t bbox = gfxline_getbbox(line);
2307 r.xmin = (int)(bbox.xmin*20);
2308 r.ymin = (int)(bbox.ymin*20);
2309 r.xmax = (int)(bbox.xmax*20);
2310 r.ymax = (int)(bbox.ymax*20);
2314 int line_is_empty(gfxline_t*line)
2317 if(line->type != gfx_moveTo)
2324 static void swf_fillbitmap(gfxdevice_t*dev, gfxline_t*line, gfximage_t*img, gfxmatrix_t*matrix, gfxcxform_t*cxform)
2326 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
2328 if(line_is_empty(line))
2334 int targetx = (int)(sqrt(matrix->m00*matrix->m00 + matrix->m01*matrix->m01)*img->width);
2335 int targety = (int)(sqrt(matrix->m10*matrix->m10 + matrix->m11*matrix->m11)*img->height);
2337 int newwidth=0,newheight=0;
2338 int bitid = add_image(i, img, targetx, targety, &newwidth, &newheight);
2341 double fx = (double)img->width / (double)newwidth;
2342 double fy = (double)img->height / (double)newheight;
2345 m.sx = (int)(65536*20*matrix->m00*fx); m.r1 = (int)(65536*20*matrix->m10*fy);
2346 m.r0 = (int)(65536*20*matrix->m01*fx); m.sy = (int)(65536*20*matrix->m11*fy);
2347 m.tx = (int)(matrix->tx*20);
2348 m.ty = (int)(matrix->ty*20);
2351 int myshapeid = getNewID(dev);
2352 i->tag = swf_InsertTag(i->tag,ST_DEFINESHAPE);
2354 swf_ShapeNew(&shape);
2355 int fsid = swf_ShapeAddBitmapFillStyle(shape,&m,bitid,1);
2356 swf_SetU16(i->tag, myshapeid);
2357 SRECT r = gfxline_getSWFbbox(line);
2358 r = swf_ClipRect(i->pagebbox, r);
2359 swf_SetRect(i->tag,&r);
2360 swf_SetShapeStyles(i->tag,shape);
2361 swf_ShapeCountBits(shape,NULL,NULL);
2362 swf_SetShapeBits(i->tag,shape);
2363 swf_ShapeSetAll(i->tag,shape,UNDEFINED_COORD,UNDEFINED_COORD,0,fsid,0);
2364 i->swflastx = i->swflasty = UNDEFINED_COORD;
2365 drawgfxline(dev, line, 1);
2366 swf_ShapeSetEnd(i->tag);
2367 swf_ShapeFree(shape);
2369 msg("<trace> Placing image, shape ID %d, bitmap ID %d", myshapeid, bitid);
2370 i->tag = swf_InsertTag(i->tag,ST_PLACEOBJECT2);
2371 CXFORM cxform2 = gfxcxform_to_cxform(cxform);
2372 swf_ObjectPlace(i->tag,myshapeid,getNewDepth(dev),&i->page_matrix,&cxform2,NULL);
2375 static RGBA col_black = {255,0,0,0};
2377 static void drawoutline(gfxdevice_t*dev, gfxline_t*line)
2379 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
2381 int myshapeid = getNewID(dev);
2382 i->tag = swf_InsertTag(i->tag,ST_DEFINESHAPE3);
2385 swf_ShapeNew(&shape);
2386 int lsid = swf_ShapeAddLineStyle(shape,1,&col_black);
2388 swf_SetU16(i->tag,myshapeid);
2389 SRECT r = gfxline_getSWFbbox(line);
2390 r = swf_ClipRect(i->pagebbox, r);
2391 swf_SetRect(i->tag,&r);
2392 swf_SetShapeStyles(i->tag,shape);
2393 swf_ShapeCountBits(shape,NULL,NULL);
2394 swf_SetShapeBits(i->tag,shape);
2395 swf_ShapeSetAll(i->tag,shape,UNDEFINED_COORD,UNDEFINED_COORD,lsid,0,0);
2396 drawgfxline(dev, line, 1);
2397 swf_ShapeSetEnd(i->tag);
2398 swf_ShapeFree(shape);
2400 i->tag = swf_InsertTag(i->tag,ST_PLACEOBJECT2);
2401 swf_ObjectPlace(i->tag, myshapeid, getNewDepth(dev), 0,0,0);
2404 static void swf_startclip(gfxdevice_t*dev, gfxline_t*line)
2406 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
2407 if(i->config_noclips)
2413 if(i->clippos >= 127)
2415 msg("<warning> Too many clip levels.");
2419 if(i->config_showclipshapes)
2420 drawoutline(dev, line);
2422 int myshapeid = getNewID(dev);
2423 i->tag = swf_InsertTag(i->tag,ST_DEFINESHAPE3);
2425 memset(&col, 0, sizeof(RGBA));
2428 swf_ShapeNew(&shape);
2429 int fsid = swf_ShapeAddSolidFillStyle(shape,&col);
2431 RGBA markcol = {0,i->mark[0],i->mark[1],i->mark[2]};
2432 swf_ShapeAddSolidFillStyle(shape,&markcol);
2434 swf_SetU16(i->tag,myshapeid);
2435 SRECT r = gfxline_getSWFbbox(line);
2436 r = swf_ClipRect(i->pagebbox, r);
2437 swf_SetRect(i->tag,&r);
2438 swf_SetShapeStyles(i->tag,shape);
2439 swf_ShapeCountBits(shape,NULL,NULL);
2440 swf_SetShapeBits(i->tag,shape);
2441 swf_ShapeSetAll(i->tag,shape,UNDEFINED_COORD,UNDEFINED_COORD,0,fsid,0);
2442 i->swflastx = i->swflasty = UNDEFINED_COORD;
2443 i->shapeisempty = 1;
2444 drawgfxline(dev, line, 1);
2445 if(i->shapeisempty) {
2446 /* an empty clip shape is equivalent to a shape with no area */
2447 int x = line?line->x:0;
2448 int y = line?line->y:0;
2449 moveto(dev, i->tag, x,y);
2450 lineto(dev, i->tag, x,y);
2451 lineto(dev, i->tag, x,y);
2453 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)) {
2454 if(i->config_watermark) {
2455 gfxbbox_t r; r.xmin = r.ymin = 0;r.xmax = i->max_x;r.ymax = i->max_y;
2456 draw_watermark(dev, r, 1);
2459 swf_ShapeSetEnd(i->tag);
2460 swf_ShapeFree(shape);
2462 /* TODO: remember the bbox, and check all shapes against it */
2464 msg("<trace> Placing clip ID %d", myshapeid);
2465 i->tag = swf_InsertTag(i->tag,ST_PLACEOBJECT2);
2466 i->cliptags[i->clippos] = i->tag;
2467 i->clipshapes[i->clippos] = myshapeid;
2468 i->clipdepths[i->clippos] = getNewDepth(dev);
2472 static void swf_endclip(gfxdevice_t*dev)
2474 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
2475 if(i->config_noclips)
2483 msg("<error> Invalid end of clipping region");
2487 /*swf_ObjectPlaceClip(i->cliptags[i->clippos],i->clipshapes[i->clippos],i->clipdepths[i->clippos],&i->page_matrix,NULL,NULL,
2488 / * clip to depth: * / i->depth <= i->clipdepths[i->clippos]? i->depth : i->depth - 1);
2490 swf_ObjectPlaceClip(i->cliptags[i->clippos],i->clipshapes[i->clippos],i->clipdepths[i->clippos],&i->page_matrix,NULL,NULL,i->depth);
2492 static int gfxline_type(gfxline_t*line)
2498 int haszerosegments=0;
2501 if(line->type == gfx_moveTo) {
2504 } else if(line->type == gfx_lineTo) {
2508 } else if(line->type == gfx_splineTo) {
2510 if(tmpsplines>lines)
2518 if(lines==0 && splines==0) return 0;
2519 else if(lines==1 && splines==0) return 1;
2520 else if(lines==0 && splines==1) return 2;
2521 else if(splines==0) return 3;
2525 static int gfxline_has_dots(gfxline_t*line)
2533 if(line->type == gfx_moveTo) {
2534 /* test the length of the preceding line, and assume it is a dot if
2535 it's length is less than 1.0. But *only* if there's a noticable
2536 gap between the previous line and the next moveTo. (I've come
2537 across a PDF where thousands of "dots" were stringed together,
2539 int last_short_gap = short_gap;
2540 if((fabs(line->x - x) + fabs(line->y - y)) < 1.0) {
2545 if(isline && dist < 1 && !short_gap && !last_short_gap) {
2550 } else if(line->type == gfx_lineTo) {
2551 dist += fabs(line->x - x) + fabs(line->y - y);
2553 } else if(line->type == gfx_splineTo) {
2554 dist += fabs(line->sx - x) + fabs(line->sy - y) +
2555 fabs(line->x - line->sx) + fabs(line->y - line->sy);
2562 if(isline && dist < 1 && !short_gap) {
2568 static int gfxline_fix_short_edges(gfxline_t*line)
2572 if(line->type == gfx_lineTo) {
2573 if(fabs(line->x - x) + fabs(line->y - y) < 0.01) {
2576 } else if(line->type == gfx_splineTo) {
2577 if(fabs(line->sx - x) + fabs(line->sy - y) +
2578 fabs(line->x - line->sx) + fabs(line->y - line->sy) < 0.01) {
2589 static char is_inside_page(gfxdevice_t*dev, gfxcoord_t x, gfxcoord_t y)
2591 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
2592 if(x<i->min_x || x>i->max_x) return 0;
2593 if(y<i->min_y || y>i->max_y) return 0;
2597 gfxline_t* gfxline_move(gfxline_t*line, double x, double y)
2599 gfxline_t*l = line = gfxline_clone(line);
2611 //#define NORMALIZE_POLYGON_POSITIONS
2613 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)
2615 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
2616 if(line_is_empty(line))
2618 int type = gfxline_type(line);
2619 int has_dots = gfxline_has_dots(line);
2620 gfxbbox_t r = gfxline_getbbox(line);
2621 int is_outside_page = !is_inside_page(dev, r.xmin, r.ymin) || !is_inside_page(dev, r.xmax, r.ymax);
2623 /* TODO: * split line into segments, and perform this check for all segments */
2625 if(i->config_disable_polygon_conversion || /*type>=5 ||*/
2627 (width <= i->config_caplinewidth
2628 || (cap_style == gfx_capRound && joint_style == gfx_joinRound)
2629 || (cap_style == gfx_capRound && type<=2))))
2633 /* convert line to polygon */
2634 msg("<trace> draw as polygon, type=%d dots=%d", type, has_dots);
2636 gfxline_fix_short_edges(line);
2637 /* we need to convert the line into a polygon */
2638 gfxpoly_t* poly = gfxpoly_from_stroke(line, width, cap_style, joint_style, miterLimit, DEFAULT_GRID);
2639 gfxline_t*gfxline = gfxline_from_gfxpoly(poly);
2640 dev->fill(dev, gfxline, color);
2641 gfxline_free(gfxline);
2642 gfxpoly_destroy(poly);
2646 msg("<trace> draw as stroke, type=%d dots=%d", type, has_dots);
2649 if(i->config_normalize_polygon_positions) {
2651 double startx = 0, starty = 0;
2652 if(line && line->type == gfx_moveTo) {
2656 line = gfxline_move(line, -startx, -starty);
2657 i->shapeposx = (int)(startx*20);
2658 i->shapeposy = (int)(starty*20);
2661 swfoutput_setstrokecolor(dev, color->r, color->g, color->b, color->a);
2662 swfoutput_setlinewidth(dev, width);
2665 drawgfxline(dev, line, 0);
2667 if(i->config_normalize_polygon_positions) {
2668 free(line); //account for _move
2673 static void swf_fill(gfxdevice_t*dev, gfxline_t*line, gfxcolor_t*color)
2675 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
2676 if(line_is_empty(line))
2680 gfxbbox_t r = gfxline_getbbox(line);
2681 int is_outside_page = !is_inside_page(dev, r.xmin, r.ymin) || !is_inside_page(dev, r.xmax, r.ymax);
2685 if(!i->config_ignoredraworder)
2688 if(i->config_normalize_polygon_positions) {
2690 double startx = 0, starty = 0;
2691 if(line && line->type == gfx_moveTo) {
2695 line = gfxline_move(line, -startx, -starty);
2696 i->shapeposx = (int)(startx*20);
2697 i->shapeposy = (int)(starty*20);
2700 swfoutput_setfillcolor(dev, color->r, color->g, color->b, color->a);
2703 drawgfxline(dev, line, 1);
2705 if(i->currentswfid==2 && r.xmin==0 && r.ymin==0 && r.xmax==i->max_x && r.ymax==i->max_y) {
2706 if(i->config_watermark) {
2707 draw_watermark(dev, r, 1);
2711 msg("<trace> end of swf_fill (shapeid=%d)", i->shapeid);
2713 if(i->config_normalize_polygon_positions) {
2714 free(line); //account for _move
2718 static GRADIENT* gfxgradient_to_GRADIENT(gfxgradient_t*gradient)
2721 gfxgradient_t*g = gradient;
2726 GRADIENT* swfgradient = malloc(sizeof(GRADIENT));
2727 swfgradient->num = num;
2728 swfgradient->rgba = malloc(sizeof(swfgradient->rgba[0])*num);
2729 swfgradient->ratios = malloc(sizeof(swfgradient->ratios[0])*num);
2734 swfgradient->ratios[num] = g->pos*255;
2735 swfgradient->rgba[num] = *(RGBA*)&g->color;
2742 static void swf_fillgradient(gfxdevice_t*dev, gfxline_t*line, gfxgradient_t*gradient, gfxgradienttype_t type, gfxmatrix_t*matrix)
2744 if(line_is_empty(line))
2746 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
2748 if(line_is_empty(line))
2751 GRADIENT* swfgradient = gfxgradient_to_GRADIENT(gradient);
2758 double f = type==gfxgradient_radial?4:4;
2760 m.sx = (int)(matrix->m00*20*f); m.r1 = (int)(matrix->m10*20*f);
2761 m.r0 = (int)(matrix->m01*20*f); m.sy = (int)(matrix->m11*20*f);
2762 m.tx = (int)(matrix->tx*20);
2763 m.ty = (int)(matrix->ty*20);
2766 int myshapeid = getNewID(dev);
2767 i->tag = swf_InsertTag(i->tag, ST_DEFINESHAPE2);
2769 swf_ShapeNew(&shape);
2770 int fsid = swf_ShapeAddGradientFillStyle(shape,&m,swfgradient,type==gfxgradient_radial);
2771 swf_SetU16(i->tag, myshapeid);
2772 SRECT r = gfxline_getSWFbbox(line);
2773 r = swf_ClipRect(i->pagebbox, r);
2774 swf_SetRect(i->tag,&r);
2775 swf_SetShapeStyles(i->tag,shape);
2776 swf_ShapeCountBits(shape,NULL,NULL);
2777 swf_SetShapeBits(i->tag,shape);
2778 swf_ShapeSetAll(i->tag,shape,UNDEFINED_COORD,UNDEFINED_COORD,0,fsid,0);
2779 i->swflastx = i->swflasty = UNDEFINED_COORD;
2780 drawgfxline(dev, line, 1);
2781 swf_ShapeSetEnd(i->tag);
2782 swf_ShapeFree(shape);
2784 int depth = getNewDepth(dev);
2785 msg("<trace> Placing gradient, shape ID %d, depth %d", myshapeid, depth);
2786 i->tag = swf_InsertTag(i->tag,ST_PLACEOBJECT2);
2787 swf_ObjectPlace(i->tag,myshapeid,depth,&i->page_matrix,NULL,NULL);
2789 swf_FreeGradient(swfgradient);free(swfgradient);
2792 static SWFFONT* gfxfont_to_swffont(gfxfont_t*font, const char* id, int version)
2794 SWFFONT*swffont = (SWFFONT*)rfx_calloc(sizeof(SWFFONT));
2796 SRECT bounds = {0,0,0,0};
2798 swffont->version = version;
2799 swffont->name = (U8*)strdup(id);
2800 swffont->layout = (SWFLAYOUT*)rfx_calloc(sizeof(SWFLAYOUT));
2801 swffont->layout->ascent = 0;
2802 swffont->layout->descent = 0;
2803 swffont->layout->leading = 0;
2804 swffont->layout->bounds = (SRECT*)rfx_calloc(sizeof(SRECT)*font->num_glyphs);
2805 swffont->encoding = FONT_ENCODING_UNICODE;
2806 swffont->numchars = font->num_glyphs;
2807 swffont->maxascii = font->max_unicode;
2808 swffont->ascii2glyph = (int*)rfx_calloc(sizeof(int)*swffont->maxascii);
2809 swffont->glyph2ascii = (U16*)rfx_calloc(sizeof(U16)*swffont->numchars);
2810 swffont->glyph = (SWFGLYPH*)rfx_calloc(sizeof(SWFGLYPH)*swffont->numchars);
2811 swffont->glyphnames = (char**)rfx_calloc(sizeof(char*)*swffont->numchars);
2812 for(t=0;t<font->max_unicode;t++) {
2813 swffont->ascii2glyph[t] = font->unicode2glyph[t];
2815 SRECT max = {0,0,0,0};
2816 for(t=0;t<font->num_glyphs;t++) {
2820 swffont->glyph2ascii[t] = font->glyphs[t].unicode;
2821 if(swffont->glyph2ascii[t] == 0xffff || swffont->glyph2ascii[t] == 0x0000) {
2822 /* flash 8 flashtype requires unique unicode IDs for each character.
2823 We use the Unicode private user area to assign characters, hoping that
2824 the font doesn't contain more than 2048 glyphs */
2825 swffont->glyph2ascii[t] = 0xe000 + (t&0x1fff);
2828 if(font->glyphs[t].name) {
2829 swffont->glyphnames[t] = strdup(font->glyphs[t].name);
2831 swffont->glyphnames[t] = 0;
2833 advance = font->glyphs[t].advance;
2835 swf_Shape01DrawerInit(&draw, 0);
2836 line = font->glyphs[t].line;
2838 const double scale = GLYPH_SCALE;
2841 c.x = line->sx * scale; c.y = -line->sy * scale;
2842 //to.x = floor(line->x * scale); to.y = floor(-line->y * scale);
2843 to.x = line->x * scale; to.y = -line->y * scale;
2844 if(line->type == gfx_moveTo) {
2845 draw.moveTo(&draw, &to);
2846 } else if(line->type == gfx_lineTo) {
2847 draw.lineTo(&draw, &to);
2848 } else if(line->type == gfx_splineTo) {
2849 draw.splineTo(&draw, &c, &to);
2854 swffont->glyph[t].shape = swf_ShapeDrawerToShape(&draw);
2856 SRECT bbox = swf_ShapeDrawerGetBBox(&draw);
2857 swf_ExpandRect2(&max, &bbox);
2859 swffont->layout->bounds[t] = bbox;
2861 if(advance<32768.0/20) {
2862 swffont->glyph[t].advance = (int)(advance*20);
2864 //msg("<warning> Advance value overflow in glyph %d", t);
2865 swffont->glyph[t].advance = 32767;
2868 draw.dealloc(&draw);
2870 swf_ExpandRect2(&bounds, &swffont->layout->bounds[t]);
2872 for(t=0;t<font->num_glyphs;t++) {
2873 SRECT bbox = swffont->layout->bounds[t];
2875 /* if the glyph doesn't have a bounding box, use the
2876 combined bounding box (necessary e.g. for space characters) */
2877 if(!(bbox.xmin|bbox.ymin|bbox.xmax|bbox.ymax)) {
2878 swffont->layout->bounds[t] = bbox = max;
2881 /* check that the advance value is reasonable, by comparing it
2882 with the bounding box */
2883 if(bbox.xmax>0 && (bbox.xmax*10 < swffont->glyph[t].advance || !swffont->glyph[t].advance)) {
2884 if(swffont->glyph[t].advance)
2885 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);
2886 swffont->glyph[t].advance = bbox.xmax;
2888 //swffont->glyph[t].advance = bbox.xmax - bbox.xmin;
2892 /* Flash player will use the advance value from the char, and the ascent/descent values
2893 from the layout for text selection.
2894 ascent will extend the char into negative y direction, from the baseline, while descent
2895 will extend in positive y direction, also from the baseline.
2896 The baseline is defined as the y-position zero
2899 swffont->layout->ascent = bounds.ymin<0?-bounds.ymin:0;
2900 swffont->layout->descent = bounds.ymax>0?bounds.ymax:0;
2901 swffont->layout->leading = bounds.ymax - bounds.ymin;
2903 /* if the font has proper ascent/descent values (>0) and those define
2904 greater line spacing that what we estimated from the bounding boxes,
2905 use the font's parameters */
2906 if(font->ascent*20 > swffont->layout->ascent)
2907 swffont->layout->ascent = font->ascent*20;
2908 if(font->descent*20 > swffont->layout->descent)
2909 swffont->layout->descent = font->descent*20;
2914 static void swf_addfont(gfxdevice_t*dev, gfxfont_t*font)
2916 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
2918 if(i->swffont && i->swffont->name && !strcmp((char*)i->swffont->name,font->id))
2919 return; // the requested font is the current font
2921 fontlist_t*last=0,*l = i->fontlist;
2924 if(!strcmp((char*)l->swffont->name, font->id)) {
2925 return; // we already know this font
2929 l = (fontlist_t*)rfx_calloc(sizeof(fontlist_t));
2930 l->swffont = gfxfont_to_swffont(font, font->id, (i->config_flashversion>=8 && !NO_FONT3)?3:2);
2937 swf_FontSetID(l->swffont, getNewID(i->dev));
2939 if(getScreenLogLevel() >= LOGLEVEL_DEBUG) {
2941 // print font information
2942 msg("<debug> Font %s",font->id);
2943 msg("<debug> | ID: %d", l->swffont->id);
2944 msg("<debug> | Version: %d", l->swffont->version);
2945 msg("<debug> | Name: %s", l->swffont->name);
2946 msg("<debug> | Numchars: %d", l->swffont->numchars);
2947 msg("<debug> | Maxascii: %d", l->swffont->maxascii);
2948 msg("<debug> | Style: %d", l->swffont->style);
2949 msg("<debug> | Encoding: %d", l->swffont->encoding);
2950 for(iii=0; iii<l->swffont->numchars;iii++) {
2951 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,
2952 l->swffont->layout->bounds[iii].xmin/20.0,
2953 l->swffont->layout->bounds[iii].ymin/20.0,
2954 l->swffont->layout->bounds[iii].xmax/20.0,
2955 l->swffont->layout->bounds[iii].ymax/20.0
2958 for(t=0;t<l->swffont->maxascii;t++) {
2959 if(l->swffont->ascii2glyph[t] == iii)
2960 msg("<debug> | - maps to %d",t);
2966 static void swf_switchfont(gfxdevice_t*dev, const char*fontid)
2968 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
2970 if(i->swffont && i->swffont->name && !strcmp((char*)i->swffont->name,fontid))
2971 return; // the requested font is the current font
2973 fontlist_t*l = i->fontlist;
2975 if(!strcmp((char*)l->swffont->name, fontid)) {
2976 i->swffont = l->swffont;
2981 msg("<error> Unknown font id: %s", fontid);
2985 /* sets the matrix which is to be applied to characters drawn by swfoutput_drawchar() */
2986 static void setfontscale(gfxdevice_t*dev,double m11,double m12, double m21,double m22,double x, double y, char force)
2992 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
2993 if(i->lastfontm11 == m11 &&
2994 i->lastfontm12 == m12 &&
2995 i->lastfontm21 == m21 &&
2996 i->lastfontm22 == m22 && !force)
3001 i->lastfontm11 = m11;
3002 i->lastfontm12 = m12;
3003 i->lastfontm21 = m21;
3004 i->lastfontm22 = m22;
3006 double xsize = sqrt(m11*m11 + m12*m12);
3007 double ysize = sqrt(m21*m21 + m22*m22);
3010 if(i->config_flashversion>=8 && !NO_FONT3)
3013 i->current_font_size = (xsize>ysize?xsize:ysize)*extrazoom;
3014 if(i->current_font_size < 1)
3015 i->current_font_size = 1;
3018 swf_GetMatrix(0, &m);
3020 if(m21 || m12 || fabs(m11+m22)>0.001) {
3021 double ifs = (double)extrazoom/(i->current_font_size);
3022 m.sx = (S32)((m11*ifs)*65536); m.r1 = -(S32)((m21*ifs)*65536);
3023 m.r0 = (S32)((m12*ifs)*65536); m.sy = -(S32)((m22*ifs)*65536);
3026 /* this is the position of the first char to set a new fontmatrix-
3027 we hope that it's close enough to all other characters using the
3028 font, so we use its position as origin for the matrix */
3035 static void swf_drawchar(gfxdevice_t*dev, gfxfont_t*font, int glyph, gfxcolor_t*color, gfxmatrix_t*matrix)
3037 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
3039 msg("<error> swf_drawchar called (glyph %d) without font", glyph);
3043 if(i->config_drawonlyshapes) {
3044 gfxglyph_t*g = &font->glyphs[glyph];
3045 gfxline_t*line2 = gfxline_clone(g->line);
3046 gfxline_transform(line2, matrix);
3047 dev->fill(dev, line2, color);
3048 gfxline_free(line2);
3052 if(!i->swffont || !i->swffont->name || strcmp((char*)i->swffont->name,font->id)) // not equal to current font
3054 swf_switchfont(dev, font->id); // set the current font
3058 msg("<warning> swf_drawchar: Font is NULL");
3061 if(glyph<0 || glyph>=i->swffont->numchars) {
3062 msg("<warning> No character %d in font %s (%d chars)", glyph, FIXNULL((char*)i->swffont->name), i->swffont->numchars);
3066 setfontscale(dev, matrix->m00, matrix->m01, matrix->m10, matrix->m11, matrix->tx, matrix->ty, 0);
3068 double det = i->fontmatrix.sx/65536.0 * i->fontmatrix.sy/65536.0 -
3069 i->fontmatrix.r0/65536.0 * i->fontmatrix.r1/65536.0;
3070 if(fabs(det) < 0.0005) {
3071 /* x direction equals y direction- the text is invisible */
3072 msg("<verbose> Not drawing invisible character %d (det=%f, m=[%f %f;%f %f]\n", glyph,
3074 i->fontmatrix.sx/65536.0, i->fontmatrix.r1/65536.0,
3075 i->fontmatrix.r0/65536.0, i->fontmatrix.sy/65536.0);
3079 /*if(i->swffont->glyph[glyph].shape->bitlen <= 16) {
3080 msg("<warning> Glyph %d in current charset (%s, %d characters) is empty",
3081 glyph, FIXNULL((char*)i->swffont->name), i->swffont->numchars);
3085 /* calculate character position with respect to the current font matrix */
3086 double s = 20 * GLYPH_SCALE / det;
3087 double px = matrix->tx - i->fontmatrix.tx/20.0;
3088 double py = matrix->ty - i->fontmatrix.ty/20.0;
3089 int x = (SCOORD)(( px * i->fontmatrix.sy/65536.0 - py * i->fontmatrix.r1/65536.0)*s);
3090 int y = (SCOORD)((- px * i->fontmatrix.r0/65536.0 + py * i->fontmatrix.sx/65536.0)*s);
3091 if(x>32767 || x<-32768 || y>32767 || y<-32768) {
3092 msg("<verbose> Moving character origin to %f %f\n", matrix->tx, matrix->ty);
3094 setfontscale(dev, matrix->m00, matrix->m01, matrix->m10, matrix->m11, matrix->tx, matrix->ty, 1);
3095 /* since we just moved the char origin to the current char's position,
3096 it now has the relative position (0,0) */
3105 msg("<trace> Drawing char %d in font %d at %d,%d in color %02x%02x%02x%02x",
3106 glyph, i->swffont->id, x, y, color->r, color->g, color->b, color->a);
3108 if(color->a == 0 && i->config_invisibletexttofront) {
3109 RGBA color2 = *(RGBA*)color;
3110 if(i->config_flashversion>=8) {
3111 // use "multiply" blend mode
3112 color2.a = color2.r = color2.g = color2.b = 255;
3114 i->topchardata = charbuffer_append(i->topchardata, i->swffont, glyph, x, y, i->current_font_size, color2, &i->fontmatrix);
3116 i->chardata = charbuffer_append(i->chardata, i->swffont, glyph, x, y, i->current_font_size, *(RGBA*)color, &i->fontmatrix);
3118 swf_FontUseGlyph(i->swffont, glyph);