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 if(i->config_flashversion>=10)
1485 swf_FontCreateAlignZones(iterator->swffont);
1487 if(iterator->swffont->alignzones) {
1488 mtag = swf_InsertTag(mtag, ST_DEFINEFONTALIGNZONES);
1489 swf_FontSetAlignZones(mtag, iterator->swffont);
1495 iterator = iterator->next;
1498 i->tag = swf_InsertTag(i->tag,ST_END);
1499 TAG* tag = i->tag->prev;
1501 /* remove the removeobject2 tags between the last ST_SHOWFRAME
1502 and the ST_END- they confuse the flash player */
1503 while(tag->id == ST_REMOVEOBJECT2) {
1504 TAG* prev = tag->prev;
1505 swf_DeleteTag(i->swf, tag);
1512 if(i->config_enablezlib || i->config_flashversion>=6) {
1513 i->swf->compressed = 1;
1516 /* Add AVM2 actionscript */
1517 if(i->config_flashversion>=9 &&
1518 (i->config_insertstoptag || i->hasbuttons) && !i->config_linknameurl) {
1519 swf_AddButtonLinks(i->swf, i->config_insertstoptag,
1520 i->config_internallinkfunction||i->config_externallinkfunction);
1522 // if(i->config_reordertags)
1523 // swf_Optimize(i->swf);
1526 int swfresult_save(gfxresult_t*gfx, const char*filename)
1528 SWF*swf = (SWF*)gfx->internal;
1531 fi = open(filename, O_BINARY|O_CREAT|O_TRUNC|O_WRONLY, 0777);
1536 msg("<fatal> Could not create \"%s\". ", FIXNULL(filename));
1540 if FAILED(swf_WriteSWF(fi,swf))
1541 msg("<error> WriteSWF() failed.\n");
1547 void* swfresult_get(gfxresult_t*gfx, const char*name)
1549 SWF*swf = (SWF*)gfx->internal;
1550 if(!strcmp(name, "swf")) {
1551 return (void*)swf_CopySWF(swf);
1552 } else if(!strcmp(name, "xmin")) {
1553 return (void*)(ptroff_t)(swf->movieSize.xmin/20);
1554 } else if(!strcmp(name, "ymin")) {
1555 return (void*)(ptroff_t)(swf->movieSize.ymin/20);
1556 } else if(!strcmp(name, "xmax")) {
1557 return (void*)(ptroff_t)(swf->movieSize.xmax/20);
1558 } else if(!strcmp(name, "ymax")) {
1559 return (void*)(ptroff_t)(swf->movieSize.ymax/20);
1560 } else if(!strcmp(name, "width")) {
1561 return (void*)(ptroff_t)((swf->movieSize.xmax - swf->movieSize.xmin)/20);
1562 } else if(!strcmp(name, "height")) {
1563 return (void*)(ptroff_t)((swf->movieSize.ymax - swf->movieSize.ymin)/20);
1567 void swfresult_destroy(gfxresult_t*gfx)
1570 swf_FreeTags((SWF*)gfx->internal);
1571 free(gfx->internal);
1574 memset(gfx, 0, sizeof(gfxresult_t));
1578 static void swfoutput_destroy(gfxdevice_t* dev);
1580 gfxresult_t* swf_finish(gfxdevice_t* dev)
1582 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1585 if(i->config_linktarget) {
1586 free(i->config_linktarget);
1587 i->config_linktarget = 0;
1590 swfoutput_finalize(dev);
1591 SWF* swf = i->swf;i->swf = 0;
1592 swfoutput_destroy(dev);
1594 result = (gfxresult_t*)rfx_calloc(sizeof(gfxresult_t));
1595 result->internal = swf;
1596 result->save = swfresult_save;
1598 result->get = swfresult_get;
1599 result->destroy = swfresult_destroy;
1603 /* Perform cleaning up */
1604 static void swfoutput_destroy(gfxdevice_t* dev)
1606 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1608 /* not initialized yet- nothing to destroy */
1612 fontlist_t *tmp,*iterator = i->fontlist;
1614 if(iterator->swffont) {
1615 swf_FontFree(iterator->swffont);iterator->swffont=0;
1618 iterator = iterator->next;
1621 if(i->swf) {swf_FreeTags(i->swf);free(i->swf);i->swf = 0;}
1624 memset(dev, 0, sizeof(gfxdevice_t));
1627 static void swfoutput_setstrokecolor(gfxdevice_t* dev, U8 r, U8 g, U8 b, U8 a)
1629 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1630 if(i->strokergb.r == r &&
1631 i->strokergb.g == g &&
1632 i->strokergb.b == b &&
1633 i->strokergb.a == a) return;
1643 //#define ROUND_UP 19
1644 //#define ROUND_UP 10
1646 static void swfoutput_setlinewidth(gfxdevice_t*dev, double _linewidth)
1648 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1649 if(i->linewidth == (U16)(_linewidth*20+19.0/20.0))
1653 i->linewidth = (U16)(_linewidth*20+19.0/20.0);
1657 static void drawlink(gfxdevice_t*dev, ActionTAG*,ActionTAG*, gfxline_t*points, char mouseover, char*type, const char*url);
1658 static void swfoutput_namedlink(gfxdevice_t*dev, char*name, gfxline_t*points);
1659 static void swfoutput_linktopage(gfxdevice_t*dev, int page, gfxline_t*points);
1660 static void swfoutput_linktourl(gfxdevice_t*dev, const char*url, gfxline_t*points);
1662 /*void swfoutput_drawlink(gfxdevice_t*dev, char*url, gfxline_t*points)
1664 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1665 dev->drawlink(dev, points, url);
1668 void swf_drawlink(gfxdevice_t*dev, gfxline_t*points, const char*url)
1670 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1672 if(i->config_disablelinks)
1675 if(!strncmp("http://pdf2swf:", url, 15)) {
1676 char*tmp = strdup(url);
1677 int l = strlen(tmp);
1680 swfoutput_namedlink(dev, tmp+15, points);
1683 } else if(!strncmp("page", url, 4)) {
1686 if(url[t]<'0' || url[t]>'9')
1689 int page = atoi(&url[4]);
1690 if(page<0) page = 0;
1691 swfoutput_linktopage(dev, page, points);
1694 swfoutput_linktourl(dev, url, points);
1697 void swfoutput_linktourl(gfxdevice_t*dev, const char*url, gfxline_t*points)
1699 ActionTAG* actions = 0;
1700 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1706 /* TODO: escape special characters in url */
1708 if(i->config_externallinkfunction && i->config_flashversion<=8) {
1709 actions = action_PushString(actions, url); //parameter
1710 actions = action_PushInt(actions, 1); //number of parameters (1)
1711 actions = action_PushString(actions, i->config_externallinkfunction); //function name
1712 actions = action_CallFunction(actions);
1713 } else if(!i->config_linktarget) {
1714 if(!i->config_opennewwindow)
1715 actions = action_GetUrl(actions, url, "_parent");
1717 actions = action_GetUrl(actions, url, "_this");
1719 actions = action_GetUrl(actions, url, i->config_linktarget);
1721 actions = action_End(actions);
1723 drawlink(dev, actions, 0, points, 0, "url", url);
1725 swf_ActionFree(actions);
1727 void swfoutput_linktopage(gfxdevice_t*dev, int page, gfxline_t*points)
1729 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1730 ActionTAG* actions = 0;
1737 if(!i->config_internallinkfunction || i->config_flashversion>=9) {
1738 actions = action_GotoFrame(actions, page-1);
1739 actions = action_End(actions);
1741 actions = action_PushInt(actions, page); //parameter
1742 actions = action_PushInt(actions, 1); //number of parameters (1)
1743 actions = action_PushString(actions, i->config_internallinkfunction); //function name
1744 actions = action_CallFunction(actions);
1745 actions = action_End(actions);
1749 sprintf(name, "page%d", page);
1751 drawlink(dev, actions, 0, points, 0, "page", name);
1753 swf_ActionFree(actions);
1756 /* Named Links (a.k.a. Acrobatmenu) are used to implement various gadgets
1757 of the viewer objects, like subtitles, index elements etc.
1759 void swfoutput_namedlink(gfxdevice_t*dev, char*name, gfxline_t*points)
1761 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1762 ActionTAG *actions1,*actions2;
1763 char*tmp = strdup(name);
1772 if(!strncmp(tmp, "call:", 5))
1774 char*x = strchr(&tmp[5], ':');
1776 actions1 = action_PushInt(0, 0); //number of parameters (0)
1777 actions1 = action_PushString(actions1, &tmp[5]); //function name
1778 actions1 = action_CallFunction(actions1);
1779 actions1 = action_End(actions1);
1782 actions1 = action_PushString(0, x+1); //parameter
1783 actions1 = action_PushInt(actions1, 1); //number of parameters (1)
1784 actions1 = action_PushString(actions1, &tmp[5]); //function name
1785 actions1 = action_CallFunction(actions1);
1786 actions1 = action_End(actions1);
1788 actions2 = action_End(0);
1794 actions1 = action_PushString(0, "/:subtitle");
1795 actions1 = action_PushString(actions1, name);
1796 actions1 = action_SetVariable(actions1);
1797 actions1 = action_End(actions1);
1799 actions2 = action_PushString(0, "/:subtitle");
1800 actions2 = action_PushString(actions2, "");
1801 actions2 = action_SetVariable(actions2);
1802 actions2 = action_End(actions2);
1806 drawlink(dev, actions1, actions2, points, mouseover, type, name);
1808 swf_ActionFree(actions1);
1809 swf_ActionFree(actions2);
1813 static void drawgfxline(gfxdevice_t*dev, gfxline_t*line, int fill)
1815 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1816 gfxcoord_t lastx=0,lasty=0,px=0,py=0;
1818 int lines= 0, splines=0;
1825 /* check whether the next segment is zero */
1826 if(line->type == gfx_moveTo) {
1827 moveto(dev, i->tag, line->x, line->y);
1828 px = lastx = line->x;
1829 py = lasty = line->y;
1831 } if(line->type == gfx_lineTo) {
1832 lineto(dev, i->tag, line->x, line->y);
1837 } else if(line->type == gfx_splineTo) {
1839 s.x = line->sx;p.x = line->x;
1840 s.y = line->sy;p.y = line->y;
1841 splineto(dev, i->tag, s, p);
1849 msg("<trace> drawgfxline, %d lines, %d splines", lines, splines);
1853 static void drawlink(gfxdevice_t*dev, ActionTAG*actions1, ActionTAG*actions2, gfxline_t*points, char mouseover, char*type, const char*url)
1855 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1864 int buttonid = getNewID(dev);
1865 gfxbbox_t bbox = gfxline_getbbox(points);
1867 if(i->config_linknameurl) {
1875 myshapeid = getNewID(dev);
1876 i->tag = swf_InsertTag(i->tag,ST_DEFINESHAPE3);
1877 swf_ShapeNew(&i->shape);
1878 rgb.r = rgb.b = rgb.a = rgb.g = 0;
1879 fsid = swf_ShapeAddSolidFillStyle(i->shape,&rgb);
1880 swf_SetU16(i->tag, myshapeid);
1881 r.xmin = (int)(bbox.xmin*20);
1882 r.ymin = (int)(bbox.ymin*20);
1883 r.xmax = (int)(bbox.xmax*20);
1884 r.ymax = (int)(bbox.ymax*20);
1885 r = swf_ClipRect(i->pagebbox, r);
1886 swf_SetRect(i->tag,&r);
1887 swf_SetShapeStyles(i->tag,i->shape);
1888 swf_ShapeCountBits(i->shape,NULL,NULL);
1889 swf_SetShapeBits(i->tag,i->shape);
1890 swf_ShapeSetAll(i->tag,i->shape,/*x*/0,/*y*/0,0,fsid,0);
1891 i->swflastx = i->swflasty = 0;
1892 drawgfxline(dev, points, 1);
1893 swf_ShapeSetEnd(i->tag);
1894 swf_ShapeFree(i->shape);
1897 myshapeid2 = getNewID(dev);
1898 i->tag = swf_InsertTag(i->tag,ST_DEFINESHAPE3);
1899 swf_ShapeNew(&i->shape);
1901 rgb = i->config_linkcolor;
1903 fsid = swf_ShapeAddSolidFillStyle(i->shape,&rgb);
1904 swf_SetU16(i->tag, myshapeid2);
1905 r.xmin = (int)(bbox.xmin*20);
1906 r.ymin = (int)(bbox.ymin*20);
1907 r.xmax = (int)(bbox.xmax*20);
1908 r.ymax = (int)(bbox.ymax*20);
1909 r = swf_ClipRect(i->pagebbox, r);
1910 swf_SetRect(i->tag,&r);
1911 swf_SetShapeStyles(i->tag,i->shape);
1912 swf_ShapeCountBits(i->shape,NULL,NULL);
1913 swf_SetShapeBits(i->tag,i->shape);
1914 swf_ShapeSetAll(i->tag,i->shape,/*x*/0,/*y*/0,0,fsid,0);
1915 i->swflastx = i->swflasty = 0;
1916 drawgfxline(dev, points, 1);
1917 swf_ShapeSetEnd(i->tag);
1918 swf_ShapeFree(i->shape);
1922 i->tag = swf_InsertTag(i->tag,ST_DEFINEBUTTON);
1923 swf_SetU16(i->tag,buttonid); //id
1924 swf_ButtonSetFlags(i->tag, 0); //menu=no
1925 swf_ButtonSetRecord(i->tag,0x01,myshapeid,i->depth,0,0);
1926 swf_ButtonSetRecord(i->tag,0x02,myshapeid2,i->depth,0,0);
1927 swf_ButtonSetRecord(i->tag,0x04,myshapeid2,i->depth,0,0);
1928 swf_ButtonSetRecord(i->tag,0x08,myshapeid,i->depth,0,0);
1929 swf_SetU8(i->tag,0);
1930 swf_ActionSet(i->tag,actions1);
1931 swf_SetU8(i->tag,0);
1935 i->tag = swf_InsertTag(i->tag,ST_DEFINEBUTTON2);
1936 swf_SetU16(i->tag,buttonid); //id
1937 swf_ButtonSetFlags(i->tag, 0); //menu=no
1938 swf_ButtonSetRecord(i->tag,0x01,myshapeid,i->depth,0,0);
1939 swf_ButtonSetRecord(i->tag,0x02,myshapeid2,i->depth,0,0);
1940 swf_ButtonSetRecord(i->tag,0x04,myshapeid2,i->depth,0,0);
1941 swf_ButtonSetRecord(i->tag,0x08,myshapeid,i->depth,0,0);
1942 swf_SetU8(i->tag,0); // end of button records
1943 swf_ButtonSetCondition(i->tag, BC_IDLE_OVERUP);
1944 swf_ActionSet(i->tag,actions1);
1946 swf_ButtonSetCondition(i->tag, BC_OVERUP_IDLE);
1947 swf_ActionSet(i->tag,actions2);
1948 swf_SetU8(i->tag,0);
1949 swf_ButtonPostProcess(i->tag, 2);
1951 swf_SetU8(i->tag,0);
1952 swf_ButtonPostProcess(i->tag, 1);
1958 const char* name = 0;
1959 if(i->config_linknameurl) {
1960 buf2 = malloc(strlen(type)+strlen(url)+2);
1961 sprintf(buf2, "%s:%s", type, url);
1965 sprintf(buf, "button%d", buttonid);
1968 msg("<trace> Placing link ID %d", buttonid);
1969 i->tag = swf_InsertTag(i->tag,ST_PLACEOBJECT2);
1971 if(posx!=0 || posy!=0) {
1973 p.x = (int)(posx*20);
1974 p.y = (int)(posy*20);
1975 p = swf_TurnPoint(p, &i->page_matrix);
1980 swf_ObjectPlace(i->tag, buttonid, getNewDepth(dev),&m,0,(U8*)name);
1982 swf_ObjectPlace(i->tag, buttonid, getNewDepth(dev),&i->page_matrix,0,(U8*)name);
1992 for(t=0;t<picpos;t++)
1994 if(pic_xids[t] == xid &&
1995 pic_yids[t] == yid) {
1996 width = pic_width[t];
1997 height = pic_height[t];
2001 pic_ids[picpos] = swfoutput_drawimagelosslessN(&output, pic, pal, width, height, x1,y1,x2,y2,x3,y3,x4,y4, numpalette);
2002 pic_xids[picpos] = xid;
2003 pic_yids[picpos] = yid;
2004 pic_width[picpos] = width;
2005 pic_height[picpos] = height;
2008 pic[width*y+x] = buf[0];
2012 xid += pal[1].r*3 + pal[1].g*11 + pal[1].b*17;
2013 yid += pal[1].r*7 + pal[1].g*5 + pal[1].b*23;
2017 xid += x*r+x*b*3+x*g*7+x*a*11;
2018 yid += y*r*3+y*b*17+y*g*19+y*a*11;
2020 for(t=0;t<picpos;t++)
2022 if(pic_xids[t] == xid &&
2023 pic_yids[t] == yid) {
2032 int swf_setparameter(gfxdevice_t*dev, const char*name, const char*value)
2034 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
2036 msg("<trace> swfdevice: %s=%s", name, value);
2037 if(!strcmp(name, "jpegsubpixels")) {
2038 i->config_jpegsubpixels = atof(value);
2039 } else if(!strcmp(name, "ppmsubpixels")) {
2040 i->config_ppmsubpixels = atof(value);
2041 } else if(!strcmp(name, "subpixels")) {
2042 i->config_ppmsubpixels = i->config_jpegsubpixels = atof(value);
2043 } else if(!strcmp(name, "drawonlyshapes")) {
2044 i->config_drawonlyshapes = atoi(value);
2045 } else if(!strcmp(name, "ignoredraworder")) {
2046 i->config_ignoredraworder = atoi(value);
2047 } else if(!strcmp(name, "mark")) {
2048 if(!value || !value[0]) {
2049 if(i->mark) free(i->mark);
2053 i->mark = strdup("...");
2054 for(t=0;t<3;t++) if(value[t]) i->mark[t] = value[t];
2056 } else if(!strcmp(name, "filloverlap")) {
2057 i->config_filloverlap = atoi(value);
2058 } else if(!strcmp(name, "linksopennewwindow")) {
2059 i->config_opennewwindow = atoi(value);
2060 } else if(!strcmp(name, "opennewwindow")) {
2061 i->config_opennewwindow = atoi(value);
2062 } else if(!strcmp(name, "storeallcharacters")) {
2063 i->config_storeallcharacters = atoi(value);
2064 } else if(!strcmp(name, "enablezlib")) {
2065 i->config_enablezlib = atoi(value);
2066 } else if(!strcmp(name, "bboxvars")) {
2067 i->config_bboxvars = atoi(value);
2068 } else if(!strcmp(name, "dots")) {
2069 i->config_dots = atoi(value);
2070 } else if(!strcmp(name, "frameresets")) {
2071 i->config_frameresets = atoi(value);
2072 } else if(!strcmp(name, "showclipshapes")) {
2073 i->config_showclipshapes = atoi(value);
2074 } else if(!strcmp(name, "reordertags")) {
2075 i->config_reordertags = atoi(value);
2076 } else if(!strcmp(name, "internallinkfunction")) {
2077 i->config_internallinkfunction = strdup(value);
2078 } else if(!strcmp(name, "externallinkfunction")) {
2079 i->config_externallinkfunction = strdup(value);
2080 } else if(!strcmp(name, "linkfunction")) { //sets both internallinkfunction and externallinkfunction
2081 i->config_internallinkfunction = strdup(value);
2082 i->config_externallinkfunction = strdup(value);
2083 } else if(!strcmp(name, "disable_polygon_conversion")) {
2084 i->config_disable_polygon_conversion = atoi(value);
2085 } else if(!strcmp(name, "normalize_polygon_positions")) {
2086 i->config_normalize_polygon_positions = atoi(value);
2087 } else if(!strcmp(name, "wxwindowparams")) {
2088 i->config_watermark = atoi(value);
2089 } else if(!strcmp(name, "insertstop")) {
2090 i->config_insertstoptag = atoi(value);
2091 } else if(!strcmp(name, "protect")) {
2092 i->config_protect = atoi(value);
2093 if(i->config_protect && i->tag) {
2094 i->tag = swf_InsertTag(i->tag, ST_PROTECT);
2096 } else if(!strcmp(name, "flashversion")) {
2097 i->config_flashversion = atoi(value);
2099 i->swf->fileVersion = i->config_flashversion;
2101 } else if(!strcmp(name, "framerate")) {
2102 i->config_framerate = atof(value);
2104 i->swf->frameRate = i->config_framerate*0x100;
2106 } else if(!strcmp(name, "minlinewidth")) {
2107 i->config_minlinewidth = atof(value);
2108 } else if(!strcmp(name, "caplinewidth")) {
2109 i->config_caplinewidth = atof(value);
2110 } else if(!strcmp(name, "linktarget")) {
2111 i->config_linktarget = strdup(value);
2112 } else if(!strcmp(name, "invisibletexttofront")) {
2113 i->config_invisibletexttofront = atoi(value);
2114 } else if(!strcmp(name, "noclips")) {
2115 i->config_noclips = atoi(value);
2116 } else if(!strcmp(name, "dumpfonts")) {
2117 i->config_dumpfonts = atoi(value);
2118 } else if(!strcmp(name, "animate")) {
2119 i->config_animate = atoi(value);
2120 } else if(!strcmp(name, "disablelinks")) {
2121 i->config_disablelinks = atoi(value);
2122 } else if(!strcmp(name, "simpleviewer")) {
2123 i->config_simpleviewer = atoi(value);
2124 } else if(!strcmp(name, "next_bitmap_is_jpeg")) {
2126 } else if(!strcmp(name, "jpegquality")) {
2127 int val = atoi(value);
2129 if(val>101) val=101;
2130 i->config_jpegquality = val;
2131 } else if(!strcmp(name, "splinequality")) {
2132 int v = atoi(value);
2133 v = 500-(v*5); // 100% = 0.25 pixel, 0% = 25 pixel
2135 i->config_splinemaxerror = v;
2136 } else if(!strcmp(name, "fontquality")) {
2137 int v = atoi(value);
2138 v = 500-(v*5); // 100% = 0.25 pixel, 0% = 25 pixel
2140 i->config_fontsplinemaxerror = v;
2141 } else if(!strcmp(name, "linkcolor")) {
2142 if(strlen(value)!=8) {
2143 fprintf(stderr, "Unknown format for option 'linkcolor'. (%s <-> RRGGBBAA)\n", value);
2146 # define NIBBLE(s) (((s)>='0' && (s)<='9')?((s)-'0'):((s)&0x0f)+9)
2147 i->config_linkcolor.r = NIBBLE(value[0])<<4 | NIBBLE(value[1]);
2148 i->config_linkcolor.g = NIBBLE(value[2])<<4 | NIBBLE(value[3]);
2149 i->config_linkcolor.b = NIBBLE(value[4])<<4 | NIBBLE(value[5]);
2150 i->config_linkcolor.a = NIBBLE(value[6])<<4 | NIBBLE(value[7]);
2151 } else if(!strcmp(name, "help")) {
2152 printf("\nSWF layer options:\n");
2153 printf("jpegsubpixels=<pixels> resolution adjustment for jpeg images (same as jpegdpi, but in pixels)\n");
2154 printf("ppmsubpixels=<pixels resolution adjustment for lossless images (same as ppmdpi, but in pixels)\n");
2155 printf("subpixels=<pixels> shortcut for setting both jpegsubpixels and ppmsubpixels\n");
2156 printf("drawonlyshapes convert everything to shapes (currently broken)\n");
2157 printf("ignoredraworder allow to perform a few optimizations for creating smaller SWFs\n");
2158 printf("linksopennewwindow make links open a new browser window\n");
2159 printf("linktarget target window name of new links\n");
2160 printf("linkcolor=<color) color of links (format: RRGGBBAA)\n");
2161 printf("linknameurl Link buttons will be named like the URL they refer to (handy for iterating through links with actionscript)\n");
2162 printf("storeallcharacters don't reduce the fonts to used characters in the output file\n");
2163 printf("enablezlib switch on zlib compression (also done if flashversion>=6)\n");
2164 printf("bboxvars store the bounding box of the SWF file in actionscript variables\n");
2165 printf("dots Take care to handle dots correctly\n");
2166 printf("reordertags=0/1 (default: 1) perform some tag optimizations\n");
2167 printf("internallinkfunction=<name> when the user clicks a internal link (to a different page) in the converted file, this actionscript function is called\n");
2168 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");
2169 printf("disable_polygon_conversion never convert strokes to polygons (will remove capstyles and joint styles)\n");
2170 printf("caplinewidth=<width> the minimum thichness a line needs to have so that capstyles become visible (and are converted)\n");
2171 printf("insertstop put an ActionScript \"STOP\" tag in every frame\n");
2172 printf("protect add a \"protect\" tag to the file, to prevent loading in the Flash editor\n");
2173 printf("flashversion=<version> the SWF fileversion (6)\n");
2174 printf("framerate=<fps> SWF framerate\n");
2175 printf("minlinewidth=<width> convert horizontal/vertical boxes smaller than this width to lines (0.05) \n");
2176 printf("simpleviewer Add next/previous buttons to the SWF\n");
2177 printf("animate insert a showframe tag after each placeobject (animate draw order of PDF files)\n");
2178 printf("jpegquality=<quality> set compression quality of jpeg images\n");
2179 printf("splinequality=<value> Set the quality of spline convertion to value (0-100, default: 100).\n");
2180 printf("disablelinks Disable links.\n");
2187 // --------------------------------------------------------------------
2189 static CXFORM gfxcxform_to_cxform(gfxcxform_t* c)
2192 swf_GetCXForm(0, &cx, 1);
2195 if(c->rg!=0 || c->rb!=0 || c->ra!=0 ||
2196 c->gr!=0 || c->gb!=0 || c->ga!=0 ||
2197 c->br!=0 || c->bg!=0 || c->ba!=0 ||
2198 c->ar!=0 || c->ag!=0 || c->ab!=0)
2199 msg("<warning> CXForm not SWF-compatible");
2201 cx.a0 = (S16)(c->aa*256);
2202 cx.r0 = (S16)(c->rr*256);
2203 cx.g0 = (S16)(c->gg*256);
2204 cx.b0 = (S16)(c->bb*256);
2213 static int imageInCache(gfxdevice_t*dev, void*data, int width, int height)
2217 static void addImageToCache(gfxdevice_t*dev, void*data, int width, int height)
2221 static int add_image(swfoutput_internal*i, gfximage_t*img, int targetwidth, int targetheight, int* newwidth, int* newheight)
2223 gfxdevice_t*dev = i->dev;
2225 RGBA*mem = (RGBA*)img->data;
2227 int sizex = img->width;
2228 int sizey = img->height;
2229 int is_jpeg = i->jpeg;
2232 int newsizex=sizex, newsizey=sizey;
2235 if(is_jpeg && i->config_jpegsubpixels) {
2236 newsizex = (int)(targetwidth*i->config_jpegsubpixels + 0.5);
2237 newsizey = (int)(targetheight*i->config_jpegsubpixels + 0.5);
2238 } else if(!is_jpeg && i->config_ppmsubpixels) {
2239 newsizex = (int)(targetwidth*i->config_ppmsubpixels + 0.5);
2240 newsizey = (int)(targetheight*i->config_ppmsubpixels + 0.5);
2244 if(sizex<=0 || sizey<=0)
2251 /* TODO: cache images */
2253 if(newsizex<sizex || newsizey<sizey) {
2254 msg("<verbose> Scaling %dx%d image to %dx%d", sizex, sizey, newsizex, newsizey);
2255 newpic = swf_ImageScale(mem, sizex, sizey, newsizex, newsizey);
2256 *newwidth = sizex = newsizex;
2257 *newheight = sizey = newsizey;
2260 *newwidth = newsizex = sizex;
2261 *newheight = newsizey = sizey;
2264 int num_colors = swf_ImageGetNumberOfPaletteEntries(mem,sizex,sizey,0);
2265 int has_alpha = swf_ImageHasAlpha(mem,sizex,sizey);
2267 msg("<verbose> Drawing %dx%d %s%simage (id %d) at size %dx%d (%dx%d), %s%d colors",
2269 has_alpha?(has_alpha==2?"semi-transparent ":"transparent "):"",
2270 is_jpeg?"jpeg-":"", i->currentswfid+1,
2272 targetwidth, targetheight,
2273 /*newsizex, newsizey,*/
2274 num_colors>256?">":"", num_colors>256?256:num_colors);
2276 /*RGBA* pal = (RGBA*)rfx_alloc(sizeof(RGBA)*num_colors);
2277 swf_ImageGetNumberOfPaletteEntries(mem,sizex,sizey,pal);
2279 for(t=0;t<num_colors;t++) {
2280 printf("%02x%02x%02x%02x ",
2281 pal[t].r, pal[t].g, pal[t].b, pal[t].a);
2288 int cacheid = imageInCache(dev, mem, sizex, sizey);
2291 bitid = getNewID(dev);
2293 i->tag = swf_AddImage(i->tag, bitid, mem, sizex, sizey, i->config_jpegquality);
2294 addImageToCache(dev, mem, sizex, sizey);
2304 static SRECT gfxline_getSWFbbox(gfxline_t*line)
2306 gfxbbox_t bbox = gfxline_getbbox(line);
2308 r.xmin = (int)(bbox.xmin*20);
2309 r.ymin = (int)(bbox.ymin*20);
2310 r.xmax = (int)(bbox.xmax*20);
2311 r.ymax = (int)(bbox.ymax*20);
2315 int line_is_empty(gfxline_t*line)
2318 if(line->type != gfx_moveTo)
2325 static void swf_fillbitmap(gfxdevice_t*dev, gfxline_t*line, gfximage_t*img, gfxmatrix_t*matrix, gfxcxform_t*cxform)
2327 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
2329 if(line_is_empty(line))
2335 int targetx = (int)(sqrt(matrix->m00*matrix->m00 + matrix->m01*matrix->m01)*img->width);
2336 int targety = (int)(sqrt(matrix->m10*matrix->m10 + matrix->m11*matrix->m11)*img->height);
2338 int newwidth=0,newheight=0;
2339 int bitid = add_image(i, img, targetx, targety, &newwidth, &newheight);
2342 double fx = (double)img->width / (double)newwidth;
2343 double fy = (double)img->height / (double)newheight;
2346 m.sx = (int)(65536*20*matrix->m00*fx); m.r1 = (int)(65536*20*matrix->m10*fy);
2347 m.r0 = (int)(65536*20*matrix->m01*fx); m.sy = (int)(65536*20*matrix->m11*fy);
2348 m.tx = (int)(matrix->tx*20);
2349 m.ty = (int)(matrix->ty*20);
2352 int myshapeid = getNewID(dev);
2353 i->tag = swf_InsertTag(i->tag,ST_DEFINESHAPE);
2355 swf_ShapeNew(&shape);
2356 int fsid = swf_ShapeAddBitmapFillStyle(shape,&m,bitid,1);
2357 swf_SetU16(i->tag, myshapeid);
2358 SRECT r = gfxline_getSWFbbox(line);
2359 r = swf_ClipRect(i->pagebbox, r);
2360 swf_SetRect(i->tag,&r);
2361 swf_SetShapeStyles(i->tag,shape);
2362 swf_ShapeCountBits(shape,NULL,NULL);
2363 swf_SetShapeBits(i->tag,shape);
2364 swf_ShapeSetAll(i->tag,shape,UNDEFINED_COORD,UNDEFINED_COORD,0,fsid,0);
2365 i->swflastx = i->swflasty = UNDEFINED_COORD;
2366 drawgfxline(dev, line, 1);
2367 swf_ShapeSetEnd(i->tag);
2368 swf_ShapeFree(shape);
2370 msg("<trace> Placing image, shape ID %d, bitmap ID %d", myshapeid, bitid);
2371 i->tag = swf_InsertTag(i->tag,ST_PLACEOBJECT2);
2372 CXFORM cxform2 = gfxcxform_to_cxform(cxform);
2373 swf_ObjectPlace(i->tag,myshapeid,getNewDepth(dev),&i->page_matrix,&cxform2,NULL);
2376 static RGBA col_black = {255,0,0,0};
2378 static void drawoutline(gfxdevice_t*dev, gfxline_t*line)
2380 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
2382 int myshapeid = getNewID(dev);
2383 i->tag = swf_InsertTag(i->tag,ST_DEFINESHAPE3);
2386 swf_ShapeNew(&shape);
2387 int lsid = swf_ShapeAddLineStyle(shape,1,&col_black);
2389 swf_SetU16(i->tag,myshapeid);
2390 SRECT r = gfxline_getSWFbbox(line);
2391 r = swf_ClipRect(i->pagebbox, r);
2392 swf_SetRect(i->tag,&r);
2393 swf_SetShapeStyles(i->tag,shape);
2394 swf_ShapeCountBits(shape,NULL,NULL);
2395 swf_SetShapeBits(i->tag,shape);
2396 swf_ShapeSetAll(i->tag,shape,UNDEFINED_COORD,UNDEFINED_COORD,lsid,0,0);
2397 drawgfxline(dev, line, 1);
2398 swf_ShapeSetEnd(i->tag);
2399 swf_ShapeFree(shape);
2401 i->tag = swf_InsertTag(i->tag,ST_PLACEOBJECT2);
2402 swf_ObjectPlace(i->tag, myshapeid, getNewDepth(dev), 0,0,0);
2405 static void swf_startclip(gfxdevice_t*dev, gfxline_t*line)
2407 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
2408 if(i->config_noclips)
2414 if(i->clippos >= 127)
2416 msg("<warning> Too many clip levels.");
2420 if(i->config_showclipshapes)
2421 drawoutline(dev, line);
2423 int myshapeid = getNewID(dev);
2424 i->tag = swf_InsertTag(i->tag,ST_DEFINESHAPE3);
2426 memset(&col, 0, sizeof(RGBA));
2429 swf_ShapeNew(&shape);
2430 int fsid = swf_ShapeAddSolidFillStyle(shape,&col);
2432 RGBA markcol = {0,i->mark[0],i->mark[1],i->mark[2]};
2433 swf_ShapeAddSolidFillStyle(shape,&markcol);
2435 swf_SetU16(i->tag,myshapeid);
2436 SRECT r = gfxline_getSWFbbox(line);
2437 r = swf_ClipRect(i->pagebbox, r);
2438 swf_SetRect(i->tag,&r);
2439 swf_SetShapeStyles(i->tag,shape);
2440 swf_ShapeCountBits(shape,NULL,NULL);
2441 swf_SetShapeBits(i->tag,shape);
2442 swf_ShapeSetAll(i->tag,shape,UNDEFINED_COORD,UNDEFINED_COORD,0,fsid,0);
2443 i->swflastx = i->swflasty = UNDEFINED_COORD;
2444 i->shapeisempty = 1;
2445 drawgfxline(dev, line, 1);
2446 if(i->shapeisempty) {
2447 /* an empty clip shape is equivalent to a shape with no area */
2448 int x = line?line->x:0;
2449 int y = line?line->y:0;
2450 moveto(dev, i->tag, x,y);
2451 lineto(dev, i->tag, x,y);
2452 lineto(dev, i->tag, x,y);
2454 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)) {
2455 if(i->config_watermark) {
2456 gfxbbox_t r; r.xmin = r.ymin = 0;r.xmax = i->max_x;r.ymax = i->max_y;
2457 draw_watermark(dev, r, 1);
2460 swf_ShapeSetEnd(i->tag);
2461 swf_ShapeFree(shape);
2463 /* TODO: remember the bbox, and check all shapes against it */
2465 msg("<trace> Placing clip ID %d", myshapeid);
2466 i->tag = swf_InsertTag(i->tag,ST_PLACEOBJECT2);
2467 i->cliptags[i->clippos] = i->tag;
2468 i->clipshapes[i->clippos] = myshapeid;
2469 i->clipdepths[i->clippos] = getNewDepth(dev);
2473 static void swf_endclip(gfxdevice_t*dev)
2475 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
2476 if(i->config_noclips)
2484 msg("<error> Invalid end of clipping region");
2488 /*swf_ObjectPlaceClip(i->cliptags[i->clippos],i->clipshapes[i->clippos],i->clipdepths[i->clippos],&i->page_matrix,NULL,NULL,
2489 / * clip to depth: * / i->depth <= i->clipdepths[i->clippos]? i->depth : i->depth - 1);
2491 swf_ObjectPlaceClip(i->cliptags[i->clippos],i->clipshapes[i->clippos],i->clipdepths[i->clippos],&i->page_matrix,NULL,NULL,i->depth);
2493 static int gfxline_type(gfxline_t*line)
2499 int haszerosegments=0;
2502 if(line->type == gfx_moveTo) {
2505 } else if(line->type == gfx_lineTo) {
2509 } else if(line->type == gfx_splineTo) {
2511 if(tmpsplines>lines)
2519 if(lines==0 && splines==0) return 0;
2520 else if(lines==1 && splines==0) return 1;
2521 else if(lines==0 && splines==1) return 2;
2522 else if(splines==0) return 3;
2526 static int gfxline_has_dots(gfxline_t*line)
2534 if(line->type == gfx_moveTo) {
2535 /* test the length of the preceding line, and assume it is a dot if
2536 it's length is less than 1.0. But *only* if there's a noticable
2537 gap between the previous line and the next moveTo. (I've come
2538 across a PDF where thousands of "dots" were stringed together,
2540 int last_short_gap = short_gap;
2541 if((fabs(line->x - x) + fabs(line->y - y)) < 1.0) {
2546 if(isline && dist < 1 && !short_gap && !last_short_gap) {
2551 } else if(line->type == gfx_lineTo) {
2552 dist += fabs(line->x - x) + fabs(line->y - y);
2554 } else if(line->type == gfx_splineTo) {
2555 dist += fabs(line->sx - x) + fabs(line->sy - y) +
2556 fabs(line->x - line->sx) + fabs(line->y - line->sy);
2563 if(isline && dist < 1 && !short_gap) {
2569 static int gfxline_fix_short_edges(gfxline_t*line)
2573 if(line->type == gfx_lineTo) {
2574 if(fabs(line->x - x) + fabs(line->y - y) < 0.01) {
2577 } else if(line->type == gfx_splineTo) {
2578 if(fabs(line->sx - x) + fabs(line->sy - y) +
2579 fabs(line->x - line->sx) + fabs(line->y - line->sy) < 0.01) {
2590 static char is_inside_page(gfxdevice_t*dev, gfxcoord_t x, gfxcoord_t y)
2592 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
2593 if(x<i->min_x || x>i->max_x) return 0;
2594 if(y<i->min_y || y>i->max_y) return 0;
2598 gfxline_t* gfxline_move(gfxline_t*line, double x, double y)
2600 gfxline_t*l = line = gfxline_clone(line);
2612 //#define NORMALIZE_POLYGON_POSITIONS
2614 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)
2616 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
2617 if(line_is_empty(line))
2619 int type = gfxline_type(line);
2620 int has_dots = gfxline_has_dots(line);
2621 gfxbbox_t r = gfxline_getbbox(line);
2622 int is_outside_page = !is_inside_page(dev, r.xmin, r.ymin) || !is_inside_page(dev, r.xmax, r.ymax);
2624 /* TODO: * split line into segments, and perform this check for all segments */
2626 if(i->config_disable_polygon_conversion || /*type>=5 ||*/
2628 (width <= i->config_caplinewidth
2629 || (cap_style == gfx_capRound && joint_style == gfx_joinRound)
2630 || (cap_style == gfx_capRound && type<=2))))
2634 /* convert line to polygon */
2635 msg("<trace> draw as polygon, type=%d dots=%d", type, has_dots);
2637 gfxline_fix_short_edges(line);
2638 /* we need to convert the line into a polygon */
2639 gfxpoly_t* poly = gfxpoly_from_stroke(line, width, cap_style, joint_style, miterLimit, DEFAULT_GRID);
2640 gfxline_t*gfxline = gfxline_from_gfxpoly(poly);
2641 dev->fill(dev, gfxline, color);
2642 gfxline_free(gfxline);
2643 gfxpoly_destroy(poly);
2647 msg("<trace> draw as stroke, type=%d dots=%d", type, has_dots);
2650 if(i->config_normalize_polygon_positions) {
2652 double startx = 0, starty = 0;
2653 if(line && line->type == gfx_moveTo) {
2657 line = gfxline_move(line, -startx, -starty);
2658 i->shapeposx = (int)(startx*20);
2659 i->shapeposy = (int)(starty*20);
2662 swfoutput_setstrokecolor(dev, color->r, color->g, color->b, color->a);
2663 swfoutput_setlinewidth(dev, width);
2666 drawgfxline(dev, line, 0);
2668 if(i->config_normalize_polygon_positions) {
2669 free(line); //account for _move
2674 static void swf_fill(gfxdevice_t*dev, gfxline_t*line, gfxcolor_t*color)
2676 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
2677 if(line_is_empty(line))
2681 gfxbbox_t r = gfxline_getbbox(line);
2682 int is_outside_page = !is_inside_page(dev, r.xmin, r.ymin) || !is_inside_page(dev, r.xmax, r.ymax);
2686 if(!i->config_ignoredraworder)
2689 if(i->config_normalize_polygon_positions) {
2691 double startx = 0, starty = 0;
2692 if(line && line->type == gfx_moveTo) {
2696 line = gfxline_move(line, -startx, -starty);
2697 i->shapeposx = (int)(startx*20);
2698 i->shapeposy = (int)(starty*20);
2701 swfoutput_setfillcolor(dev, color->r, color->g, color->b, color->a);
2704 drawgfxline(dev, line, 1);
2706 if(i->currentswfid==2 && r.xmin==0 && r.ymin==0 && r.xmax==i->max_x && r.ymax==i->max_y) {
2707 if(i->config_watermark) {
2708 draw_watermark(dev, r, 1);
2712 msg("<trace> end of swf_fill (shapeid=%d)", i->shapeid);
2714 if(i->config_normalize_polygon_positions) {
2715 free(line); //account for _move
2719 static GRADIENT* gfxgradient_to_GRADIENT(gfxgradient_t*gradient)
2722 gfxgradient_t*g = gradient;
2727 GRADIENT* swfgradient = malloc(sizeof(GRADIENT));
2728 swfgradient->num = num;
2729 swfgradient->rgba = malloc(sizeof(swfgradient->rgba[0])*num);
2730 swfgradient->ratios = malloc(sizeof(swfgradient->ratios[0])*num);
2735 swfgradient->ratios[num] = g->pos*255;
2736 swfgradient->rgba[num] = *(RGBA*)&g->color;
2743 static void swf_fillgradient(gfxdevice_t*dev, gfxline_t*line, gfxgradient_t*gradient, gfxgradienttype_t type, gfxmatrix_t*matrix)
2745 if(line_is_empty(line))
2747 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
2749 if(line_is_empty(line))
2752 GRADIENT* swfgradient = gfxgradient_to_GRADIENT(gradient);
2759 double f = type==gfxgradient_radial?4:4;
2761 m.sx = (int)(matrix->m00*20*f); m.r1 = (int)(matrix->m10*20*f);
2762 m.r0 = (int)(matrix->m01*20*f); m.sy = (int)(matrix->m11*20*f);
2763 m.tx = (int)(matrix->tx*20);
2764 m.ty = (int)(matrix->ty*20);
2767 int myshapeid = getNewID(dev);
2768 i->tag = swf_InsertTag(i->tag, ST_DEFINESHAPE2);
2770 swf_ShapeNew(&shape);
2771 int fsid = swf_ShapeAddGradientFillStyle(shape,&m,swfgradient,type==gfxgradient_radial);
2772 swf_SetU16(i->tag, myshapeid);
2773 SRECT r = gfxline_getSWFbbox(line);
2774 r = swf_ClipRect(i->pagebbox, r);
2775 swf_SetRect(i->tag,&r);
2776 swf_SetShapeStyles(i->tag,shape);
2777 swf_ShapeCountBits(shape,NULL,NULL);
2778 swf_SetShapeBits(i->tag,shape);
2779 swf_ShapeSetAll(i->tag,shape,UNDEFINED_COORD,UNDEFINED_COORD,0,fsid,0);
2780 i->swflastx = i->swflasty = UNDEFINED_COORD;
2781 drawgfxline(dev, line, 1);
2782 swf_ShapeSetEnd(i->tag);
2783 swf_ShapeFree(shape);
2785 int depth = getNewDepth(dev);
2786 msg("<trace> Placing gradient, shape ID %d, depth %d", myshapeid, depth);
2787 i->tag = swf_InsertTag(i->tag,ST_PLACEOBJECT2);
2788 swf_ObjectPlace(i->tag,myshapeid,depth,&i->page_matrix,NULL,NULL);
2790 swf_FreeGradient(swfgradient);free(swfgradient);
2793 static SWFFONT* gfxfont_to_swffont(gfxfont_t*font, const char* id, int version)
2795 SWFFONT*swffont = (SWFFONT*)rfx_calloc(sizeof(SWFFONT));
2797 SRECT bounds = {0,0,0,0};
2799 swffont->version = version;
2800 swffont->name = (U8*)strdup(id);
2801 swffont->layout = (SWFLAYOUT*)rfx_calloc(sizeof(SWFLAYOUT));
2802 swffont->layout->ascent = 0;
2803 swffont->layout->descent = 0;
2804 swffont->layout->leading = 0;
2805 swffont->layout->bounds = (SRECT*)rfx_calloc(sizeof(SRECT)*font->num_glyphs);
2806 swffont->encoding = FONT_ENCODING_UNICODE;
2807 swffont->numchars = font->num_glyphs;
2808 swffont->maxascii = font->max_unicode;
2809 swffont->ascii2glyph = (int*)rfx_calloc(sizeof(int)*swffont->maxascii);
2810 swffont->glyph2ascii = (U16*)rfx_calloc(sizeof(U16)*swffont->numchars);
2811 swffont->glyph = (SWFGLYPH*)rfx_calloc(sizeof(SWFGLYPH)*swffont->numchars);
2812 swffont->glyphnames = (char**)rfx_calloc(sizeof(char*)*swffont->numchars);
2813 for(t=0;t<font->max_unicode;t++) {
2814 swffont->ascii2glyph[t] = font->unicode2glyph[t];
2816 SRECT max = {0,0,0,0};
2817 for(t=0;t<font->num_glyphs;t++) {
2821 swffont->glyph2ascii[t] = font->glyphs[t].unicode;
2822 if(swffont->glyph2ascii[t] == 0xffff || swffont->glyph2ascii[t] == 0x0000) {
2823 /* flash 8 flashtype requires unique unicode IDs for each character.
2824 We use the Unicode private user area to assign characters, hoping that
2825 the font doesn't contain more than 2048 glyphs */
2826 swffont->glyph2ascii[t] = 0xe000 + (t&0x1fff);
2829 if(font->glyphs[t].name) {
2830 swffont->glyphnames[t] = strdup(font->glyphs[t].name);
2832 swffont->glyphnames[t] = 0;
2834 advance = font->glyphs[t].advance;
2836 swf_Shape01DrawerInit(&draw, 0);
2837 line = font->glyphs[t].line;
2839 const double scale = GLYPH_SCALE;
2842 c.x = line->sx * scale; c.y = -line->sy * scale;
2843 //to.x = floor(line->x * scale); to.y = floor(-line->y * scale);
2844 to.x = line->x * scale; to.y = -line->y * scale;
2845 if(line->type == gfx_moveTo) {
2846 draw.moveTo(&draw, &to);
2847 } else if(line->type == gfx_lineTo) {
2848 draw.lineTo(&draw, &to);
2849 } else if(line->type == gfx_splineTo) {
2850 draw.splineTo(&draw, &c, &to);
2855 swffont->glyph[t].shape = swf_ShapeDrawerToShape(&draw);
2857 SRECT bbox = swf_ShapeDrawerGetBBox(&draw);
2858 swf_ExpandRect2(&max, &bbox);
2860 swffont->layout->bounds[t] = bbox;
2862 if(advance<32768.0/20) {
2863 swffont->glyph[t].advance = (int)(advance*20);
2865 //msg("<warning> Advance value overflow in glyph %d", t);
2866 swffont->glyph[t].advance = 32767;
2869 draw.dealloc(&draw);
2871 swf_ExpandRect2(&bounds, &swffont->layout->bounds[t]);
2873 for(t=0;t<font->num_glyphs;t++) {
2874 SRECT bbox = swffont->layout->bounds[t];
2876 /* if the glyph doesn't have a bounding box, use the
2877 combined bounding box (necessary e.g. for space characters) */
2878 if(!(bbox.xmin|bbox.ymin|bbox.xmax|bbox.ymax)) {
2879 swffont->layout->bounds[t] = bbox = max;
2882 /* check that the advance value is reasonable, by comparing it
2883 with the bounding box */
2884 if(bbox.xmax>0 && (bbox.xmax*10 < swffont->glyph[t].advance || !swffont->glyph[t].advance)) {
2885 if(swffont->glyph[t].advance)
2886 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);
2887 swffont->glyph[t].advance = bbox.xmax;
2889 //swffont->glyph[t].advance = bbox.xmax - bbox.xmin;
2893 /* Flash player will use the advance value from the char, and the ascent/descent values
2894 from the layout for text selection.
2895 ascent will extend the char into negative y direction, from the baseline, while descent
2896 will extend in positive y direction, also from the baseline.
2897 The baseline is defined as the y-position zero
2900 swffont->layout->ascent = bounds.ymin<0?-bounds.ymin:0;
2901 swffont->layout->descent = bounds.ymax>0?bounds.ymax:0;
2902 swffont->layout->leading = bounds.ymax - bounds.ymin;
2904 /* if the font has proper ascent/descent values (>0) and those define
2905 greater line spacing that what we estimated from the bounding boxes,
2906 use the font's parameters */
2907 if(font->ascent*20 > swffont->layout->ascent)
2908 swffont->layout->ascent = font->ascent*20;
2909 if(font->descent*20 > swffont->layout->descent)
2910 swffont->layout->descent = font->descent*20;
2915 static void swf_addfont(gfxdevice_t*dev, gfxfont_t*font)
2917 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
2919 if(i->swffont && i->swffont->name && !strcmp((char*)i->swffont->name,font->id))
2920 return; // the requested font is the current font
2922 fontlist_t*last=0,*l = i->fontlist;
2925 if(!strcmp((char*)l->swffont->name, font->id)) {
2926 return; // we already know this font
2930 l = (fontlist_t*)rfx_calloc(sizeof(fontlist_t));
2931 l->swffont = gfxfont_to_swffont(font, font->id, (i->config_flashversion>=8 && !NO_FONT3)?3:2);
2938 swf_FontSetID(l->swffont, getNewID(i->dev));
2940 if(getScreenLogLevel() >= LOGLEVEL_DEBUG) {
2942 // print font information
2943 msg("<debug> Font %s",font->id);
2944 msg("<debug> | ID: %d", l->swffont->id);
2945 msg("<debug> | Version: %d", l->swffont->version);
2946 msg("<debug> | Name: %s", l->swffont->name);
2947 msg("<debug> | Numchars: %d", l->swffont->numchars);
2948 msg("<debug> | Maxascii: %d", l->swffont->maxascii);
2949 msg("<debug> | Style: %d", l->swffont->style);
2950 msg("<debug> | Encoding: %d", l->swffont->encoding);
2951 for(iii=0; iii<l->swffont->numchars;iii++) {
2952 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,
2953 l->swffont->layout->bounds[iii].xmin/20.0,
2954 l->swffont->layout->bounds[iii].ymin/20.0,
2955 l->swffont->layout->bounds[iii].xmax/20.0,
2956 l->swffont->layout->bounds[iii].ymax/20.0
2959 for(t=0;t<l->swffont->maxascii;t++) {
2960 if(l->swffont->ascii2glyph[t] == iii)
2961 msg("<debug> | - maps to %d",t);
2967 static void swf_switchfont(gfxdevice_t*dev, const char*fontid)
2969 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
2971 if(i->swffont && i->swffont->name && !strcmp((char*)i->swffont->name,fontid))
2972 return; // the requested font is the current font
2974 fontlist_t*l = i->fontlist;
2976 if(!strcmp((char*)l->swffont->name, fontid)) {
2977 i->swffont = l->swffont;
2982 msg("<error> Unknown font id: %s", fontid);
2986 /* sets the matrix which is to be applied to characters drawn by swfoutput_drawchar() */
2987 static void setfontscale(gfxdevice_t*dev,double m11,double m12, double m21,double m22,double x, double y, char force)
2993 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
2994 if(i->lastfontm11 == m11 &&
2995 i->lastfontm12 == m12 &&
2996 i->lastfontm21 == m21 &&
2997 i->lastfontm22 == m22 && !force)
3002 i->lastfontm11 = m11;
3003 i->lastfontm12 = m12;
3004 i->lastfontm21 = m21;
3005 i->lastfontm22 = m22;
3007 double xsize = sqrt(m11*m11 + m12*m12);
3008 double ysize = sqrt(m21*m21 + m22*m22);
3011 if(i->config_flashversion>=8 && !NO_FONT3)
3014 i->current_font_size = (xsize>ysize?xsize:ysize)*extrazoom;
3015 if(i->current_font_size < 1)
3016 i->current_font_size = 1;
3019 swf_GetMatrix(0, &m);
3021 if(m21 || m12 || fabs(m11+m22)>0.001) {
3022 double ifs = (double)extrazoom/(i->current_font_size);
3023 m.sx = (S32)((m11*ifs)*65536); m.r1 = -(S32)((m21*ifs)*65536);
3024 m.r0 = (S32)((m12*ifs)*65536); m.sy = -(S32)((m22*ifs)*65536);
3027 /* this is the position of the first char to set a new fontmatrix-
3028 we hope that it's close enough to all other characters using the
3029 font, so we use its position as origin for the matrix */
3036 static void swf_drawchar(gfxdevice_t*dev, gfxfont_t*font, int glyph, gfxcolor_t*color, gfxmatrix_t*matrix)
3038 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
3040 msg("<error> swf_drawchar called (glyph %d) without font", glyph);
3044 if(i->config_drawonlyshapes) {
3045 gfxglyph_t*g = &font->glyphs[glyph];
3046 gfxline_t*line2 = gfxline_clone(g->line);
3047 gfxline_transform(line2, matrix);
3048 dev->fill(dev, line2, color);
3049 gfxline_free(line2);
3053 if(!i->swffont || !i->swffont->name || strcmp((char*)i->swffont->name,font->id)) // not equal to current font
3055 swf_switchfont(dev, font->id); // set the current font
3059 msg("<warning> swf_drawchar: Font is NULL");
3062 if(glyph<0 || glyph>=i->swffont->numchars) {
3063 msg("<warning> No character %d in font %s (%d chars)", glyph, FIXNULL((char*)i->swffont->name), i->swffont->numchars);
3067 setfontscale(dev, matrix->m00, matrix->m01, matrix->m10, matrix->m11, matrix->tx, matrix->ty, 0);
3069 double det = i->fontmatrix.sx/65536.0 * i->fontmatrix.sy/65536.0 -
3070 i->fontmatrix.r0/65536.0 * i->fontmatrix.r1/65536.0;
3071 if(fabs(det) < 0.0005) {
3072 /* x direction equals y direction- the text is invisible */
3073 msg("<verbose> Not drawing invisible character %d (det=%f, m=[%f %f;%f %f]\n", glyph,
3075 i->fontmatrix.sx/65536.0, i->fontmatrix.r1/65536.0,
3076 i->fontmatrix.r0/65536.0, i->fontmatrix.sy/65536.0);
3080 /*if(i->swffont->glyph[glyph].shape->bitlen <= 16) {
3081 msg("<warning> Glyph %d in current charset (%s, %d characters) is empty",
3082 glyph, FIXNULL((char*)i->swffont->name), i->swffont->numchars);
3086 /* calculate character position with respect to the current font matrix */
3087 double s = 20 * GLYPH_SCALE / det;
3088 double px = matrix->tx - i->fontmatrix.tx/20.0;
3089 double py = matrix->ty - i->fontmatrix.ty/20.0;
3090 int x = (SCOORD)(( px * i->fontmatrix.sy/65536.0 - py * i->fontmatrix.r1/65536.0)*s);
3091 int y = (SCOORD)((- px * i->fontmatrix.r0/65536.0 + py * i->fontmatrix.sx/65536.0)*s);
3092 if(x>32767 || x<-32768 || y>32767 || y<-32768) {
3093 msg("<verbose> Moving character origin to %f %f\n", matrix->tx, matrix->ty);
3095 setfontscale(dev, matrix->m00, matrix->m01, matrix->m10, matrix->m11, matrix->tx, matrix->ty, 1);
3096 /* since we just moved the char origin to the current char's position,
3097 it now has the relative position (0,0) */
3106 msg("<trace> Drawing char %d in font %d at %d,%d in color %02x%02x%02x%02x",
3107 glyph, i->swffont->id, x, y, color->r, color->g, color->b, color->a);
3109 if(color->a == 0 && i->config_invisibletexttofront) {
3110 RGBA color2 = *(RGBA*)color;
3111 if(i->config_flashversion>=8) {
3112 // use "multiply" blend mode
3113 color2.a = color2.r = color2.g = color2.b = 255;
3115 i->topchardata = charbuffer_append(i->topchardata, i->swffont, glyph, x, y, i->current_font_size, color2, &i->fontmatrix);
3117 i->chardata = charbuffer_append(i->chardata, i->swffont, glyph, x, y, i->current_font_size, *(RGBA*)color, &i->fontmatrix);
3119 swf_FontUseGlyph(i->swffont, glyph);