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;
1468 char use_font3 = i->config_flashversion>=8 && !NO_FONT3;
1470 TAG*mtag = i->swf->firstTag;
1471 if(iterator->swffont) {
1473 // needs to be done before the reduce
1474 swf_FontCreateAlignZones(iterator->swffont);
1476 if(!i->config_storeallcharacters) {
1477 msg("<debug> Reducing font %s", iterator->swffont->name);
1478 swf_FontReduce(iterator->swffont);
1480 int used = iterator->swffont->use && iterator->swffont->use->used_glyphs;
1483 mtag = swf_InsertTag(mtag, ST_DEFINEFONT2);
1484 swf_FontSetDefine2(mtag, iterator->swffont);
1486 mtag = swf_InsertTag(mtag, ST_DEFINEFONT3);
1487 swf_FontSetDefine2(mtag, iterator->swffont);
1488 if(iterator->swffont->alignzones) {
1489 mtag = swf_InsertTag(mtag, ST_DEFINEFONTALIGNZONES);
1490 swf_FontSetAlignZones(mtag, iterator->swffont);
1496 iterator = iterator->next;
1499 i->tag = swf_InsertTag(i->tag,ST_END);
1500 TAG* tag = i->tag->prev;
1502 /* remove the removeobject2 tags between the last ST_SHOWFRAME
1503 and the ST_END- they confuse the flash player */
1504 while(tag->id == ST_REMOVEOBJECT2) {
1505 TAG* prev = tag->prev;
1506 swf_DeleteTag(i->swf, tag);
1513 if(i->config_enablezlib || i->config_flashversion>=6) {
1514 i->swf->compressed = 1;
1517 /* Add AVM2 actionscript */
1518 if(i->config_flashversion>=9 &&
1519 (i->config_insertstoptag || i->hasbuttons) && !i->config_linknameurl) {
1520 swf_AddButtonLinks(i->swf, i->config_insertstoptag,
1521 i->config_internallinkfunction||i->config_externallinkfunction);
1523 // if(i->config_reordertags)
1524 // swf_Optimize(i->swf);
1527 int swfresult_save(gfxresult_t*gfx, const char*filename)
1529 SWF*swf = (SWF*)gfx->internal;
1532 fi = open(filename, O_BINARY|O_CREAT|O_TRUNC|O_WRONLY, 0777);
1537 msg("<fatal> Could not create \"%s\". ", FIXNULL(filename));
1541 if FAILED(swf_WriteSWF(fi,swf))
1542 msg("<error> WriteSWF() failed.\n");
1548 void* swfresult_get(gfxresult_t*gfx, const char*name)
1550 SWF*swf = (SWF*)gfx->internal;
1551 if(!strcmp(name, "swf")) {
1552 return (void*)swf_CopySWF(swf);
1553 } else if(!strcmp(name, "xmin")) {
1554 return (void*)(ptroff_t)(swf->movieSize.xmin/20);
1555 } else if(!strcmp(name, "ymin")) {
1556 return (void*)(ptroff_t)(swf->movieSize.ymin/20);
1557 } else if(!strcmp(name, "xmax")) {
1558 return (void*)(ptroff_t)(swf->movieSize.xmax/20);
1559 } else if(!strcmp(name, "ymax")) {
1560 return (void*)(ptroff_t)(swf->movieSize.ymax/20);
1561 } else if(!strcmp(name, "width")) {
1562 return (void*)(ptroff_t)((swf->movieSize.xmax - swf->movieSize.xmin)/20);
1563 } else if(!strcmp(name, "height")) {
1564 return (void*)(ptroff_t)((swf->movieSize.ymax - swf->movieSize.ymin)/20);
1568 void swfresult_destroy(gfxresult_t*gfx)
1571 swf_FreeTags((SWF*)gfx->internal);
1572 free(gfx->internal);
1575 memset(gfx, 0, sizeof(gfxresult_t));
1579 static void swfoutput_destroy(gfxdevice_t* dev);
1581 gfxresult_t* swf_finish(gfxdevice_t* dev)
1583 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1586 if(i->config_linktarget) {
1587 free(i->config_linktarget);
1588 i->config_linktarget = 0;
1591 swfoutput_finalize(dev);
1592 SWF* swf = i->swf;i->swf = 0;
1593 swfoutput_destroy(dev);
1595 result = (gfxresult_t*)rfx_calloc(sizeof(gfxresult_t));
1596 result->internal = swf;
1597 result->save = swfresult_save;
1599 result->get = swfresult_get;
1600 result->destroy = swfresult_destroy;
1604 /* Perform cleaning up */
1605 static void swfoutput_destroy(gfxdevice_t* dev)
1607 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1609 /* not initialized yet- nothing to destroy */
1613 fontlist_t *tmp,*iterator = i->fontlist;
1615 if(iterator->swffont) {
1616 swf_FontFree(iterator->swffont);iterator->swffont=0;
1619 iterator = iterator->next;
1622 if(i->swf) {swf_FreeTags(i->swf);free(i->swf);i->swf = 0;}
1625 memset(dev, 0, sizeof(gfxdevice_t));
1628 static void swfoutput_setstrokecolor(gfxdevice_t* dev, U8 r, U8 g, U8 b, U8 a)
1630 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1631 if(i->strokergb.r == r &&
1632 i->strokergb.g == g &&
1633 i->strokergb.b == b &&
1634 i->strokergb.a == a) return;
1644 //#define ROUND_UP 19
1645 //#define ROUND_UP 10
1647 static void swfoutput_setlinewidth(gfxdevice_t*dev, double _linewidth)
1649 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1650 if(i->linewidth == (U16)(_linewidth*20+19.0/20.0))
1654 i->linewidth = (U16)(_linewidth*20+19.0/20.0);
1658 static void drawlink(gfxdevice_t*dev, ActionTAG*,ActionTAG*, gfxline_t*points, char mouseover, char*type, const char*url);
1659 static void swfoutput_namedlink(gfxdevice_t*dev, char*name, gfxline_t*points);
1660 static void swfoutput_linktopage(gfxdevice_t*dev, int page, gfxline_t*points);
1661 static void swfoutput_linktourl(gfxdevice_t*dev, const char*url, gfxline_t*points);
1663 /*void swfoutput_drawlink(gfxdevice_t*dev, char*url, gfxline_t*points)
1665 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1666 dev->drawlink(dev, points, url);
1669 void swf_drawlink(gfxdevice_t*dev, gfxline_t*points, const char*url)
1671 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1673 if(i->config_disablelinks)
1676 if(!strncmp("http://pdf2swf:", url, 15)) {
1677 char*tmp = strdup(url);
1678 int l = strlen(tmp);
1681 swfoutput_namedlink(dev, tmp+15, points);
1684 } else if(!strncmp("page", url, 4)) {
1687 if(url[t]<'0' || url[t]>'9')
1690 int page = atoi(&url[4]);
1691 if(page<0) page = 0;
1692 swfoutput_linktopage(dev, page, points);
1695 swfoutput_linktourl(dev, url, points);
1698 void swfoutput_linktourl(gfxdevice_t*dev, const char*url, gfxline_t*points)
1700 ActionTAG* actions = 0;
1701 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1707 /* TODO: escape special characters in url */
1709 if(i->config_externallinkfunction && i->config_flashversion<=8) {
1710 actions = action_PushString(actions, url); //parameter
1711 actions = action_PushInt(actions, 1); //number of parameters (1)
1712 actions = action_PushString(actions, i->config_externallinkfunction); //function name
1713 actions = action_CallFunction(actions);
1714 } else if(!i->config_linktarget) {
1715 if(!i->config_opennewwindow)
1716 actions = action_GetUrl(actions, url, "_parent");
1718 actions = action_GetUrl(actions, url, "_this");
1720 actions = action_GetUrl(actions, url, i->config_linktarget);
1722 actions = action_End(actions);
1724 drawlink(dev, actions, 0, points, 0, "url", url);
1726 swf_ActionFree(actions);
1728 void swfoutput_linktopage(gfxdevice_t*dev, int page, gfxline_t*points)
1730 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1731 ActionTAG* actions = 0;
1738 if(!i->config_internallinkfunction || i->config_flashversion>=9) {
1739 actions = action_GotoFrame(actions, page-1);
1740 actions = action_End(actions);
1742 actions = action_PushInt(actions, page); //parameter
1743 actions = action_PushInt(actions, 1); //number of parameters (1)
1744 actions = action_PushString(actions, i->config_internallinkfunction); //function name
1745 actions = action_CallFunction(actions);
1746 actions = action_End(actions);
1750 sprintf(name, "page%d", page);
1752 drawlink(dev, actions, 0, points, 0, "page", name);
1754 swf_ActionFree(actions);
1757 /* Named Links (a.k.a. Acrobatmenu) are used to implement various gadgets
1758 of the viewer objects, like subtitles, index elements etc.
1760 void swfoutput_namedlink(gfxdevice_t*dev, char*name, gfxline_t*points)
1762 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1763 ActionTAG *actions1,*actions2;
1764 char*tmp = strdup(name);
1773 if(!strncmp(tmp, "call:", 5))
1775 char*x = strchr(&tmp[5], ':');
1777 actions1 = action_PushInt(0, 0); //number of parameters (0)
1778 actions1 = action_PushString(actions1, &tmp[5]); //function name
1779 actions1 = action_CallFunction(actions1);
1780 actions1 = action_End(actions1);
1783 actions1 = action_PushString(0, x+1); //parameter
1784 actions1 = action_PushInt(actions1, 1); //number of parameters (1)
1785 actions1 = action_PushString(actions1, &tmp[5]); //function name
1786 actions1 = action_CallFunction(actions1);
1787 actions1 = action_End(actions1);
1789 actions2 = action_End(0);
1795 actions1 = action_PushString(0, "/:subtitle");
1796 actions1 = action_PushString(actions1, name);
1797 actions1 = action_SetVariable(actions1);
1798 actions1 = action_End(actions1);
1800 actions2 = action_PushString(0, "/:subtitle");
1801 actions2 = action_PushString(actions2, "");
1802 actions2 = action_SetVariable(actions2);
1803 actions2 = action_End(actions2);
1807 drawlink(dev, actions1, actions2, points, mouseover, type, name);
1809 swf_ActionFree(actions1);
1810 swf_ActionFree(actions2);
1814 static void drawgfxline(gfxdevice_t*dev, gfxline_t*line, int fill)
1816 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1817 gfxcoord_t lastx=0,lasty=0,px=0,py=0;
1819 int lines= 0, splines=0;
1826 /* check whether the next segment is zero */
1827 if(line->type == gfx_moveTo) {
1828 moveto(dev, i->tag, line->x, line->y);
1829 px = lastx = line->x;
1830 py = lasty = line->y;
1832 } if(line->type == gfx_lineTo) {
1833 lineto(dev, i->tag, line->x, line->y);
1838 } else if(line->type == gfx_splineTo) {
1840 s.x = line->sx;p.x = line->x;
1841 s.y = line->sy;p.y = line->y;
1842 splineto(dev, i->tag, s, p);
1850 msg("<trace> drawgfxline, %d lines, %d splines", lines, splines);
1854 static void drawlink(gfxdevice_t*dev, ActionTAG*actions1, ActionTAG*actions2, gfxline_t*points, char mouseover, char*type, const char*url)
1856 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1865 int buttonid = getNewID(dev);
1866 gfxbbox_t bbox = gfxline_getbbox(points);
1868 if(i->config_linknameurl) {
1876 myshapeid = getNewID(dev);
1877 i->tag = swf_InsertTag(i->tag,ST_DEFINESHAPE3);
1878 swf_ShapeNew(&i->shape);
1879 rgb.r = rgb.b = rgb.a = rgb.g = 0;
1880 fsid = swf_ShapeAddSolidFillStyle(i->shape,&rgb);
1881 swf_SetU16(i->tag, myshapeid);
1882 r.xmin = (int)(bbox.xmin*20);
1883 r.ymin = (int)(bbox.ymin*20);
1884 r.xmax = (int)(bbox.xmax*20);
1885 r.ymax = (int)(bbox.ymax*20);
1886 r = swf_ClipRect(i->pagebbox, r);
1887 swf_SetRect(i->tag,&r);
1888 swf_SetShapeStyles(i->tag,i->shape);
1889 swf_ShapeCountBits(i->shape,NULL,NULL);
1890 swf_SetShapeBits(i->tag,i->shape);
1891 swf_ShapeSetAll(i->tag,i->shape,/*x*/0,/*y*/0,0,fsid,0);
1892 i->swflastx = i->swflasty = 0;
1893 drawgfxline(dev, points, 1);
1894 swf_ShapeSetEnd(i->tag);
1895 swf_ShapeFree(i->shape);
1898 myshapeid2 = getNewID(dev);
1899 i->tag = swf_InsertTag(i->tag,ST_DEFINESHAPE3);
1900 swf_ShapeNew(&i->shape);
1902 rgb = i->config_linkcolor;
1904 fsid = swf_ShapeAddSolidFillStyle(i->shape,&rgb);
1905 swf_SetU16(i->tag, myshapeid2);
1906 r.xmin = (int)(bbox.xmin*20);
1907 r.ymin = (int)(bbox.ymin*20);
1908 r.xmax = (int)(bbox.xmax*20);
1909 r.ymax = (int)(bbox.ymax*20);
1910 r = swf_ClipRect(i->pagebbox, r);
1911 swf_SetRect(i->tag,&r);
1912 swf_SetShapeStyles(i->tag,i->shape);
1913 swf_ShapeCountBits(i->shape,NULL,NULL);
1914 swf_SetShapeBits(i->tag,i->shape);
1915 swf_ShapeSetAll(i->tag,i->shape,/*x*/0,/*y*/0,0,fsid,0);
1916 i->swflastx = i->swflasty = 0;
1917 drawgfxline(dev, points, 1);
1918 swf_ShapeSetEnd(i->tag);
1919 swf_ShapeFree(i->shape);
1923 i->tag = swf_InsertTag(i->tag,ST_DEFINEBUTTON);
1924 swf_SetU16(i->tag,buttonid); //id
1925 swf_ButtonSetFlags(i->tag, 0); //menu=no
1926 swf_ButtonSetRecord(i->tag,0x01,myshapeid,i->depth,0,0);
1927 swf_ButtonSetRecord(i->tag,0x02,myshapeid2,i->depth,0,0);
1928 swf_ButtonSetRecord(i->tag,0x04,myshapeid2,i->depth,0,0);
1929 swf_ButtonSetRecord(i->tag,0x08,myshapeid,i->depth,0,0);
1930 swf_SetU8(i->tag,0);
1931 swf_ActionSet(i->tag,actions1);
1932 swf_SetU8(i->tag,0);
1936 i->tag = swf_InsertTag(i->tag,ST_DEFINEBUTTON2);
1937 swf_SetU16(i->tag,buttonid); //id
1938 swf_ButtonSetFlags(i->tag, 0); //menu=no
1939 swf_ButtonSetRecord(i->tag,0x01,myshapeid,i->depth,0,0);
1940 swf_ButtonSetRecord(i->tag,0x02,myshapeid2,i->depth,0,0);
1941 swf_ButtonSetRecord(i->tag,0x04,myshapeid2,i->depth,0,0);
1942 swf_ButtonSetRecord(i->tag,0x08,myshapeid,i->depth,0,0);
1943 swf_SetU8(i->tag,0); // end of button records
1944 swf_ButtonSetCondition(i->tag, BC_IDLE_OVERUP);
1945 swf_ActionSet(i->tag,actions1);
1947 swf_ButtonSetCondition(i->tag, BC_OVERUP_IDLE);
1948 swf_ActionSet(i->tag,actions2);
1949 swf_SetU8(i->tag,0);
1950 swf_ButtonPostProcess(i->tag, 2);
1952 swf_SetU8(i->tag,0);
1953 swf_ButtonPostProcess(i->tag, 1);
1959 const char* name = 0;
1960 if(i->config_linknameurl) {
1961 buf2 = malloc(strlen(type)+strlen(url)+2);
1962 sprintf(buf2, "%s:%s", type, url);
1966 sprintf(buf, "button%d", buttonid);
1969 msg("<trace> Placing link ID %d", buttonid);
1970 i->tag = swf_InsertTag(i->tag,ST_PLACEOBJECT2);
1972 if(posx!=0 || posy!=0) {
1974 p.x = (int)(posx*20);
1975 p.y = (int)(posy*20);
1976 p = swf_TurnPoint(p, &i->page_matrix);
1981 swf_ObjectPlace(i->tag, buttonid, getNewDepth(dev),&m,0,(U8*)name);
1983 swf_ObjectPlace(i->tag, buttonid, getNewDepth(dev),&i->page_matrix,0,(U8*)name);
1993 for(t=0;t<picpos;t++)
1995 if(pic_xids[t] == xid &&
1996 pic_yids[t] == yid) {
1997 width = pic_width[t];
1998 height = pic_height[t];
2002 pic_ids[picpos] = swfoutput_drawimagelosslessN(&output, pic, pal, width, height, x1,y1,x2,y2,x3,y3,x4,y4, numpalette);
2003 pic_xids[picpos] = xid;
2004 pic_yids[picpos] = yid;
2005 pic_width[picpos] = width;
2006 pic_height[picpos] = height;
2009 pic[width*y+x] = buf[0];
2013 xid += pal[1].r*3 + pal[1].g*11 + pal[1].b*17;
2014 yid += pal[1].r*7 + pal[1].g*5 + pal[1].b*23;
2018 xid += x*r+x*b*3+x*g*7+x*a*11;
2019 yid += y*r*3+y*b*17+y*g*19+y*a*11;
2021 for(t=0;t<picpos;t++)
2023 if(pic_xids[t] == xid &&
2024 pic_yids[t] == yid) {
2033 int swf_setparameter(gfxdevice_t*dev, const char*name, const char*value)
2035 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
2037 msg("<trace> swfdevice: %s=%s", name, value);
2038 if(!strcmp(name, "jpegsubpixels")) {
2039 i->config_jpegsubpixels = atof(value);
2040 } else if(!strcmp(name, "ppmsubpixels")) {
2041 i->config_ppmsubpixels = atof(value);
2042 } else if(!strcmp(name, "subpixels")) {
2043 i->config_ppmsubpixels = i->config_jpegsubpixels = atof(value);
2044 } else if(!strcmp(name, "drawonlyshapes")) {
2045 i->config_drawonlyshapes = atoi(value);
2046 } else if(!strcmp(name, "ignoredraworder")) {
2047 i->config_ignoredraworder = atoi(value);
2048 } else if(!strcmp(name, "mark")) {
2049 if(!value || !value[0]) {
2050 if(i->mark) free(i->mark);
2054 i->mark = strdup("...");
2055 for(t=0;t<3;t++) if(value[t]) i->mark[t] = value[t];
2057 } else if(!strcmp(name, "filloverlap")) {
2058 i->config_filloverlap = atoi(value);
2059 } else if(!strcmp(name, "linksopennewwindow")) {
2060 i->config_opennewwindow = atoi(value);
2061 } else if(!strcmp(name, "opennewwindow")) {
2062 i->config_opennewwindow = atoi(value);
2063 } else if(!strcmp(name, "storeallcharacters")) {
2064 i->config_storeallcharacters = atoi(value);
2065 } else if(!strcmp(name, "enablezlib")) {
2066 i->config_enablezlib = atoi(value);
2067 } else if(!strcmp(name, "bboxvars")) {
2068 i->config_bboxvars = atoi(value);
2069 } else if(!strcmp(name, "dots")) {
2070 i->config_dots = atoi(value);
2071 } else if(!strcmp(name, "frameresets")) {
2072 i->config_frameresets = atoi(value);
2073 } else if(!strcmp(name, "showclipshapes")) {
2074 i->config_showclipshapes = atoi(value);
2075 } else if(!strcmp(name, "reordertags")) {
2076 i->config_reordertags = atoi(value);
2077 } else if(!strcmp(name, "internallinkfunction")) {
2078 i->config_internallinkfunction = strdup(value);
2079 } else if(!strcmp(name, "externallinkfunction")) {
2080 i->config_externallinkfunction = strdup(value);
2081 } else if(!strcmp(name, "linkfunction")) { //sets both internallinkfunction and externallinkfunction
2082 i->config_internallinkfunction = strdup(value);
2083 i->config_externallinkfunction = strdup(value);
2084 } else if(!strcmp(name, "disable_polygon_conversion")) {
2085 i->config_disable_polygon_conversion = atoi(value);
2086 } else if(!strcmp(name, "normalize_polygon_positions")) {
2087 i->config_normalize_polygon_positions = atoi(value);
2088 } else if(!strcmp(name, "wxwindowparams")) {
2089 i->config_watermark = atoi(value);
2090 } else if(!strcmp(name, "insertstop")) {
2091 i->config_insertstoptag = atoi(value);
2092 } else if(!strcmp(name, "protect")) {
2093 i->config_protect = atoi(value);
2094 if(i->config_protect && i->tag) {
2095 i->tag = swf_InsertTag(i->tag, ST_PROTECT);
2097 } else if(!strcmp(name, "flashversion")) {
2098 i->config_flashversion = atoi(value);
2100 i->swf->fileVersion = i->config_flashversion;
2102 } else if(!strcmp(name, "framerate")) {
2103 i->config_framerate = atof(value);
2105 i->swf->frameRate = i->config_framerate*0x100;
2107 } else if(!strcmp(name, "minlinewidth")) {
2108 i->config_minlinewidth = atof(value);
2109 } else if(!strcmp(name, "caplinewidth")) {
2110 i->config_caplinewidth = atof(value);
2111 } else if(!strcmp(name, "linktarget")) {
2112 i->config_linktarget = strdup(value);
2113 } else if(!strcmp(name, "invisibletexttofront")) {
2114 i->config_invisibletexttofront = atoi(value);
2115 } else if(!strcmp(name, "noclips")) {
2116 i->config_noclips = atoi(value);
2117 } else if(!strcmp(name, "dumpfonts")) {
2118 i->config_dumpfonts = atoi(value);
2119 } else if(!strcmp(name, "animate")) {
2120 i->config_animate = atoi(value);
2121 } else if(!strcmp(name, "disablelinks")) {
2122 i->config_disablelinks = atoi(value);
2123 } else if(!strcmp(name, "simpleviewer")) {
2124 i->config_simpleviewer = atoi(value);
2125 } else if(!strcmp(name, "next_bitmap_is_jpeg")) {
2127 } else if(!strcmp(name, "jpegquality")) {
2128 int val = atoi(value);
2130 if(val>101) val=101;
2131 i->config_jpegquality = val;
2132 } else if(!strcmp(name, "splinequality")) {
2133 int v = atoi(value);
2134 v = 500-(v*5); // 100% = 0.25 pixel, 0% = 25 pixel
2136 i->config_splinemaxerror = v;
2137 } else if(!strcmp(name, "fontquality")) {
2138 int v = atoi(value);
2139 v = 500-(v*5); // 100% = 0.25 pixel, 0% = 25 pixel
2141 i->config_fontsplinemaxerror = v;
2142 } else if(!strcmp(name, "linkcolor")) {
2143 if(strlen(value)!=8) {
2144 fprintf(stderr, "Unknown format for option 'linkcolor'. (%s <-> RRGGBBAA)\n", value);
2147 # define NIBBLE(s) (((s)>='0' && (s)<='9')?((s)-'0'):((s)&0x0f)+9)
2148 i->config_linkcolor.r = NIBBLE(value[0])<<4 | NIBBLE(value[1]);
2149 i->config_linkcolor.g = NIBBLE(value[2])<<4 | NIBBLE(value[3]);
2150 i->config_linkcolor.b = NIBBLE(value[4])<<4 | NIBBLE(value[5]);
2151 i->config_linkcolor.a = NIBBLE(value[6])<<4 | NIBBLE(value[7]);
2152 } else if(!strcmp(name, "help")) {
2153 printf("\nSWF layer options:\n");
2154 printf("jpegsubpixels=<pixels> resolution adjustment for jpeg images (same as jpegdpi, but in pixels)\n");
2155 printf("ppmsubpixels=<pixels resolution adjustment for lossless images (same as ppmdpi, but in pixels)\n");
2156 printf("subpixels=<pixels> shortcut for setting both jpegsubpixels and ppmsubpixels\n");
2157 printf("drawonlyshapes convert everything to shapes (currently broken)\n");
2158 printf("ignoredraworder allow to perform a few optimizations for creating smaller SWFs\n");
2159 printf("linksopennewwindow make links open a new browser window\n");
2160 printf("linktarget target window name of new links\n");
2161 printf("linkcolor=<color) color of links (format: RRGGBBAA)\n");
2162 printf("linknameurl Link buttons will be named like the URL they refer to (handy for iterating through links with actionscript)\n");
2163 printf("storeallcharacters don't reduce the fonts to used characters in the output file\n");
2164 printf("enablezlib switch on zlib compression (also done if flashversion>=6)\n");
2165 printf("bboxvars store the bounding box of the SWF file in actionscript variables\n");
2166 printf("dots Take care to handle dots correctly\n");
2167 printf("reordertags=0/1 (default: 1) perform some tag optimizations\n");
2168 printf("internallinkfunction=<name> when the user clicks a internal link (to a different page) in the converted file, this actionscript function is called\n");
2169 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");
2170 printf("disable_polygon_conversion never convert strokes to polygons (will remove capstyles and joint styles)\n");
2171 printf("caplinewidth=<width> the minimum thichness a line needs to have so that capstyles become visible (and are converted)\n");
2172 printf("insertstop put an ActionScript \"STOP\" tag in every frame\n");
2173 printf("protect add a \"protect\" tag to the file, to prevent loading in the Flash editor\n");
2174 printf("flashversion=<version> the SWF fileversion (6)\n");
2175 printf("framerate=<fps> SWF framerate\n");
2176 printf("minlinewidth=<width> convert horizontal/vertical boxes smaller than this width to lines (0.05) \n");
2177 printf("simpleviewer Add next/previous buttons to the SWF\n");
2178 printf("animate insert a showframe tag after each placeobject (animate draw order of PDF files)\n");
2179 printf("jpegquality=<quality> set compression quality of jpeg images\n");
2180 printf("splinequality=<value> Set the quality of spline convertion to value (0-100, default: 100).\n");
2181 printf("disablelinks Disable links.\n");
2188 // --------------------------------------------------------------------
2190 static CXFORM gfxcxform_to_cxform(gfxcxform_t* c)
2193 swf_GetCXForm(0, &cx, 1);
2196 if(c->rg!=0 || c->rb!=0 || c->ra!=0 ||
2197 c->gr!=0 || c->gb!=0 || c->ga!=0 ||
2198 c->br!=0 || c->bg!=0 || c->ba!=0 ||
2199 c->ar!=0 || c->ag!=0 || c->ab!=0)
2200 msg("<warning> CXForm not SWF-compatible");
2202 cx.a0 = (S16)(c->aa*256);
2203 cx.r0 = (S16)(c->rr*256);
2204 cx.g0 = (S16)(c->gg*256);
2205 cx.b0 = (S16)(c->bb*256);
2214 static int imageInCache(gfxdevice_t*dev, void*data, int width, int height)
2218 static void addImageToCache(gfxdevice_t*dev, void*data, int width, int height)
2222 static int add_image(swfoutput_internal*i, gfximage_t*img, int targetwidth, int targetheight, int* newwidth, int* newheight)
2224 gfxdevice_t*dev = i->dev;
2226 RGBA*mem = (RGBA*)img->data;
2228 int sizex = img->width;
2229 int sizey = img->height;
2230 int is_jpeg = i->jpeg;
2233 int newsizex=sizex, newsizey=sizey;
2236 if(is_jpeg && i->config_jpegsubpixels) {
2237 newsizex = (int)(targetwidth*i->config_jpegsubpixels + 0.5);
2238 newsizey = (int)(targetheight*i->config_jpegsubpixels + 0.5);
2239 } else if(!is_jpeg && i->config_ppmsubpixels) {
2240 newsizex = (int)(targetwidth*i->config_ppmsubpixels + 0.5);
2241 newsizey = (int)(targetheight*i->config_ppmsubpixels + 0.5);
2245 if(sizex<=0 || sizey<=0)
2252 /* TODO: cache images */
2254 if(newsizex<sizex || newsizey<sizey) {
2255 msg("<verbose> Scaling %dx%d image to %dx%d", sizex, sizey, newsizex, newsizey);
2256 newpic = swf_ImageScale(mem, sizex, sizey, newsizex, newsizey);
2257 *newwidth = sizex = newsizex;
2258 *newheight = sizey = newsizey;
2261 *newwidth = newsizex = sizex;
2262 *newheight = newsizey = sizey;
2265 int num_colors = swf_ImageGetNumberOfPaletteEntries(mem,sizex,sizey,0);
2266 int has_alpha = swf_ImageHasAlpha(mem,sizex,sizey);
2268 msg("<verbose> Drawing %dx%d %s%simage (id %d) at size %dx%d (%dx%d), %s%d colors",
2270 has_alpha?(has_alpha==2?"semi-transparent ":"transparent "):"",
2271 is_jpeg?"jpeg-":"", i->currentswfid+1,
2273 targetwidth, targetheight,
2274 /*newsizex, newsizey,*/
2275 num_colors>256?">":"", num_colors>256?256:num_colors);
2277 /*RGBA* pal = (RGBA*)rfx_alloc(sizeof(RGBA)*num_colors);
2278 swf_ImageGetNumberOfPaletteEntries(mem,sizex,sizey,pal);
2280 for(t=0;t<num_colors;t++) {
2281 printf("%02x%02x%02x%02x ",
2282 pal[t].r, pal[t].g, pal[t].b, pal[t].a);
2289 int cacheid = imageInCache(dev, mem, sizex, sizey);
2292 bitid = getNewID(dev);
2294 i->tag = swf_AddImage(i->tag, bitid, mem, sizex, sizey, i->config_jpegquality);
2295 addImageToCache(dev, mem, sizex, sizey);
2305 static SRECT gfxline_getSWFbbox(gfxline_t*line)
2307 gfxbbox_t bbox = gfxline_getbbox(line);
2309 r.xmin = (int)(bbox.xmin*20);
2310 r.ymin = (int)(bbox.ymin*20);
2311 r.xmax = (int)(bbox.xmax*20);
2312 r.ymax = (int)(bbox.ymax*20);
2316 int line_is_empty(gfxline_t*line)
2319 if(line->type != gfx_moveTo)
2326 static void swf_fillbitmap(gfxdevice_t*dev, gfxline_t*line, gfximage_t*img, gfxmatrix_t*matrix, gfxcxform_t*cxform)
2328 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
2330 if(line_is_empty(line))
2336 int targetx = (int)(sqrt(matrix->m00*matrix->m00 + matrix->m01*matrix->m01)*img->width);
2337 int targety = (int)(sqrt(matrix->m10*matrix->m10 + matrix->m11*matrix->m11)*img->height);
2339 int newwidth=0,newheight=0;
2340 int bitid = add_image(i, img, targetx, targety, &newwidth, &newheight);
2343 double fx = (double)img->width / (double)newwidth;
2344 double fy = (double)img->height / (double)newheight;
2347 m.sx = (int)(65536*20*matrix->m00*fx); m.r1 = (int)(65536*20*matrix->m10*fy);
2348 m.r0 = (int)(65536*20*matrix->m01*fx); m.sy = (int)(65536*20*matrix->m11*fy);
2349 m.tx = (int)(matrix->tx*20);
2350 m.ty = (int)(matrix->ty*20);
2353 int myshapeid = getNewID(dev);
2354 i->tag = swf_InsertTag(i->tag,ST_DEFINESHAPE);
2356 swf_ShapeNew(&shape);
2357 int fsid = swf_ShapeAddBitmapFillStyle(shape,&m,bitid,1);
2358 swf_SetU16(i->tag, myshapeid);
2359 SRECT r = gfxline_getSWFbbox(line);
2360 r = swf_ClipRect(i->pagebbox, r);
2361 swf_SetRect(i->tag,&r);
2362 swf_SetShapeStyles(i->tag,shape);
2363 swf_ShapeCountBits(shape,NULL,NULL);
2364 swf_SetShapeBits(i->tag,shape);
2365 swf_ShapeSetAll(i->tag,shape,UNDEFINED_COORD,UNDEFINED_COORD,0,fsid,0);
2366 i->swflastx = i->swflasty = UNDEFINED_COORD;
2367 drawgfxline(dev, line, 1);
2368 swf_ShapeSetEnd(i->tag);
2369 swf_ShapeFree(shape);
2371 msg("<trace> Placing image, shape ID %d, bitmap ID %d", myshapeid, bitid);
2372 i->tag = swf_InsertTag(i->tag,ST_PLACEOBJECT2);
2373 CXFORM cxform2 = gfxcxform_to_cxform(cxform);
2374 swf_ObjectPlace(i->tag,myshapeid,getNewDepth(dev),&i->page_matrix,&cxform2,NULL);
2377 static RGBA col_black = {255,0,0,0};
2379 static void drawoutline(gfxdevice_t*dev, gfxline_t*line)
2381 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
2383 int myshapeid = getNewID(dev);
2384 i->tag = swf_InsertTag(i->tag,ST_DEFINESHAPE3);
2387 swf_ShapeNew(&shape);
2388 int lsid = swf_ShapeAddLineStyle(shape,1,&col_black);
2390 swf_SetU16(i->tag,myshapeid);
2391 SRECT r = gfxline_getSWFbbox(line);
2392 r = swf_ClipRect(i->pagebbox, r);
2393 swf_SetRect(i->tag,&r);
2394 swf_SetShapeStyles(i->tag,shape);
2395 swf_ShapeCountBits(shape,NULL,NULL);
2396 swf_SetShapeBits(i->tag,shape);
2397 swf_ShapeSetAll(i->tag,shape,UNDEFINED_COORD,UNDEFINED_COORD,lsid,0,0);
2398 drawgfxline(dev, line, 1);
2399 swf_ShapeSetEnd(i->tag);
2400 swf_ShapeFree(shape);
2402 i->tag = swf_InsertTag(i->tag,ST_PLACEOBJECT2);
2403 swf_ObjectPlace(i->tag, myshapeid, getNewDepth(dev), 0,0,0);
2406 static void swf_startclip(gfxdevice_t*dev, gfxline_t*line)
2408 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
2409 if(i->config_noclips)
2415 if(i->clippos >= 127)
2417 msg("<warning> Too many clip levels.");
2421 if(i->config_showclipshapes)
2422 drawoutline(dev, line);
2424 int myshapeid = getNewID(dev);
2425 i->tag = swf_InsertTag(i->tag,ST_DEFINESHAPE3);
2427 memset(&col, 0, sizeof(RGBA));
2430 swf_ShapeNew(&shape);
2431 int fsid = swf_ShapeAddSolidFillStyle(shape,&col);
2433 RGBA markcol = {0,i->mark[0],i->mark[1],i->mark[2]};
2434 swf_ShapeAddSolidFillStyle(shape,&markcol);
2436 swf_SetU16(i->tag,myshapeid);
2437 SRECT r = gfxline_getSWFbbox(line);
2438 r = swf_ClipRect(i->pagebbox, r);
2439 swf_SetRect(i->tag,&r);
2440 swf_SetShapeStyles(i->tag,shape);
2441 swf_ShapeCountBits(shape,NULL,NULL);
2442 swf_SetShapeBits(i->tag,shape);
2443 swf_ShapeSetAll(i->tag,shape,UNDEFINED_COORD,UNDEFINED_COORD,0,fsid,0);
2444 i->swflastx = i->swflasty = UNDEFINED_COORD;
2445 i->shapeisempty = 1;
2446 drawgfxline(dev, line, 1);
2447 if(i->shapeisempty) {
2448 /* an empty clip shape is equivalent to a shape with no area */
2449 int x = line?line->x:0;
2450 int y = line?line->y:0;
2451 moveto(dev, i->tag, x,y);
2452 lineto(dev, i->tag, x,y);
2453 lineto(dev, i->tag, x,y);
2455 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)) {
2456 if(i->config_watermark) {
2457 gfxbbox_t r; r.xmin = r.ymin = 0;r.xmax = i->max_x;r.ymax = i->max_y;
2458 draw_watermark(dev, r, 1);
2461 swf_ShapeSetEnd(i->tag);
2462 swf_ShapeFree(shape);
2464 /* TODO: remember the bbox, and check all shapes against it */
2466 msg("<trace> Placing clip ID %d", myshapeid);
2467 i->tag = swf_InsertTag(i->tag,ST_PLACEOBJECT2);
2468 i->cliptags[i->clippos] = i->tag;
2469 i->clipshapes[i->clippos] = myshapeid;
2470 i->clipdepths[i->clippos] = getNewDepth(dev);
2474 static void swf_endclip(gfxdevice_t*dev)
2476 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
2477 if(i->config_noclips)
2485 msg("<error> Invalid end of clipping region");
2489 /*swf_ObjectPlaceClip(i->cliptags[i->clippos],i->clipshapes[i->clippos],i->clipdepths[i->clippos],&i->page_matrix,NULL,NULL,
2490 / * clip to depth: * / i->depth <= i->clipdepths[i->clippos]? i->depth : i->depth - 1);
2492 swf_ObjectPlaceClip(i->cliptags[i->clippos],i->clipshapes[i->clippos],i->clipdepths[i->clippos],&i->page_matrix,NULL,NULL,i->depth);
2494 static int gfxline_type(gfxline_t*line)
2500 int haszerosegments=0;
2503 if(line->type == gfx_moveTo) {
2506 } else if(line->type == gfx_lineTo) {
2510 } else if(line->type == gfx_splineTo) {
2512 if(tmpsplines>lines)
2520 if(lines==0 && splines==0) return 0;
2521 else if(lines==1 && splines==0) return 1;
2522 else if(lines==0 && splines==1) return 2;
2523 else if(splines==0) return 3;
2527 static int gfxline_has_dots(gfxline_t*line)
2535 if(line->type == gfx_moveTo) {
2536 /* test the length of the preceding line, and assume it is a dot if
2537 it's length is less than 1.0. But *only* if there's a noticable
2538 gap between the previous line and the next moveTo. (I've come
2539 across a PDF where thousands of "dots" were stringed together,
2541 int last_short_gap = short_gap;
2542 if((fabs(line->x - x) + fabs(line->y - y)) < 1.0) {
2547 if(isline && dist < 1 && !short_gap && !last_short_gap) {
2552 } else if(line->type == gfx_lineTo) {
2553 dist += fabs(line->x - x) + fabs(line->y - y);
2555 } else if(line->type == gfx_splineTo) {
2556 dist += fabs(line->sx - x) + fabs(line->sy - y) +
2557 fabs(line->x - line->sx) + fabs(line->y - line->sy);
2564 if(isline && dist < 1 && !short_gap) {
2570 static int gfxline_fix_short_edges(gfxline_t*line)
2574 if(line->type == gfx_lineTo) {
2575 if(fabs(line->x - x) + fabs(line->y - y) < 0.01) {
2578 } else if(line->type == gfx_splineTo) {
2579 if(fabs(line->sx - x) + fabs(line->sy - y) +
2580 fabs(line->x - line->sx) + fabs(line->y - line->sy) < 0.01) {
2591 static char is_inside_page(gfxdevice_t*dev, gfxcoord_t x, gfxcoord_t y)
2593 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
2594 if(x<i->min_x || x>i->max_x) return 0;
2595 if(y<i->min_y || y>i->max_y) return 0;
2599 gfxline_t* gfxline_move(gfxline_t*line, double x, double y)
2601 gfxline_t*l = line = gfxline_clone(line);
2613 //#define NORMALIZE_POLYGON_POSITIONS
2615 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)
2617 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
2618 if(line_is_empty(line))
2620 int type = gfxline_type(line);
2621 int has_dots = gfxline_has_dots(line);
2622 gfxbbox_t r = gfxline_getbbox(line);
2623 int is_outside_page = !is_inside_page(dev, r.xmin, r.ymin) || !is_inside_page(dev, r.xmax, r.ymax);
2625 /* TODO: * split line into segments, and perform this check for all segments */
2627 if(i->config_disable_polygon_conversion || /*type>=5 ||*/
2629 (width <= i->config_caplinewidth
2630 || (cap_style == gfx_capRound && joint_style == gfx_joinRound)
2631 || (cap_style == gfx_capRound && type<=2))))
2635 /* convert line to polygon */
2636 msg("<trace> draw as polygon, type=%d dots=%d", type, has_dots);
2638 gfxline_fix_short_edges(line);
2639 /* we need to convert the line into a polygon */
2640 gfxpoly_t* poly = gfxpoly_from_stroke(line, width, cap_style, joint_style, miterLimit, DEFAULT_GRID);
2641 gfxline_t*gfxline = gfxline_from_gfxpoly(poly);
2642 dev->fill(dev, gfxline, color);
2643 gfxline_free(gfxline);
2644 gfxpoly_destroy(poly);
2648 msg("<trace> draw as stroke, type=%d dots=%d", type, has_dots);
2651 if(i->config_normalize_polygon_positions) {
2653 double startx = 0, starty = 0;
2654 if(line && line->type == gfx_moveTo) {
2658 line = gfxline_move(line, -startx, -starty);
2659 i->shapeposx = (int)(startx*20);
2660 i->shapeposy = (int)(starty*20);
2663 swfoutput_setstrokecolor(dev, color->r, color->g, color->b, color->a);
2664 swfoutput_setlinewidth(dev, width);
2667 drawgfxline(dev, line, 0);
2669 if(i->config_normalize_polygon_positions) {
2670 free(line); //account for _move
2675 static void swf_fill(gfxdevice_t*dev, gfxline_t*line, gfxcolor_t*color)
2677 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
2678 if(line_is_empty(line))
2682 gfxbbox_t r = gfxline_getbbox(line);
2683 int is_outside_page = !is_inside_page(dev, r.xmin, r.ymin) || !is_inside_page(dev, r.xmax, r.ymax);
2687 if(!i->config_ignoredraworder)
2690 if(i->config_normalize_polygon_positions) {
2692 double startx = 0, starty = 0;
2693 if(line && line->type == gfx_moveTo) {
2697 line = gfxline_move(line, -startx, -starty);
2698 i->shapeposx = (int)(startx*20);
2699 i->shapeposy = (int)(starty*20);
2702 swfoutput_setfillcolor(dev, color->r, color->g, color->b, color->a);
2705 drawgfxline(dev, line, 1);
2707 if(i->currentswfid==2 && r.xmin==0 && r.ymin==0 && r.xmax==i->max_x && r.ymax==i->max_y) {
2708 if(i->config_watermark) {
2709 draw_watermark(dev, r, 1);
2713 msg("<trace> end of swf_fill (shapeid=%d)", i->shapeid);
2715 if(i->config_normalize_polygon_positions) {
2716 free(line); //account for _move
2720 static GRADIENT* gfxgradient_to_GRADIENT(gfxgradient_t*gradient)
2723 gfxgradient_t*g = gradient;
2728 GRADIENT* swfgradient = malloc(sizeof(GRADIENT));
2729 swfgradient->num = num;
2730 swfgradient->rgba = malloc(sizeof(swfgradient->rgba[0])*num);
2731 swfgradient->ratios = malloc(sizeof(swfgradient->ratios[0])*num);
2736 swfgradient->ratios[num] = g->pos*255;
2737 swfgradient->rgba[num] = *(RGBA*)&g->color;
2744 static void swf_fillgradient(gfxdevice_t*dev, gfxline_t*line, gfxgradient_t*gradient, gfxgradienttype_t type, gfxmatrix_t*matrix)
2746 if(line_is_empty(line))
2748 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
2750 if(line_is_empty(line))
2753 GRADIENT* swfgradient = gfxgradient_to_GRADIENT(gradient);
2760 double f = type==gfxgradient_radial?4:4;
2762 m.sx = (int)(matrix->m00*20*f); m.r1 = (int)(matrix->m10*20*f);
2763 m.r0 = (int)(matrix->m01*20*f); m.sy = (int)(matrix->m11*20*f);
2764 m.tx = (int)(matrix->tx*20);
2765 m.ty = (int)(matrix->ty*20);
2768 int myshapeid = getNewID(dev);
2769 i->tag = swf_InsertTag(i->tag, ST_DEFINESHAPE2);
2771 swf_ShapeNew(&shape);
2772 int fsid = swf_ShapeAddGradientFillStyle(shape,&m,swfgradient,type==gfxgradient_radial);
2773 swf_SetU16(i->tag, myshapeid);
2774 SRECT r = gfxline_getSWFbbox(line);
2775 r = swf_ClipRect(i->pagebbox, r);
2776 swf_SetRect(i->tag,&r);
2777 swf_SetShapeStyles(i->tag,shape);
2778 swf_ShapeCountBits(shape,NULL,NULL);
2779 swf_SetShapeBits(i->tag,shape);
2780 swf_ShapeSetAll(i->tag,shape,UNDEFINED_COORD,UNDEFINED_COORD,0,fsid,0);
2781 i->swflastx = i->swflasty = UNDEFINED_COORD;
2782 drawgfxline(dev, line, 1);
2783 swf_ShapeSetEnd(i->tag);
2784 swf_ShapeFree(shape);
2786 int depth = getNewDepth(dev);
2787 msg("<trace> Placing gradient, shape ID %d, depth %d", myshapeid, depth);
2788 i->tag = swf_InsertTag(i->tag,ST_PLACEOBJECT2);
2789 swf_ObjectPlace(i->tag,myshapeid,depth,&i->page_matrix,NULL,NULL);
2791 swf_FreeGradient(swfgradient);free(swfgradient);
2794 static SWFFONT* gfxfont_to_swffont(gfxfont_t*font, const char* id, int version)
2796 SWFFONT*swffont = (SWFFONT*)rfx_calloc(sizeof(SWFFONT));
2798 SRECT bounds = {0,0,0,0};
2800 swffont->version = version;
2801 swffont->name = (U8*)strdup(id);
2802 swffont->layout = (SWFLAYOUT*)rfx_calloc(sizeof(SWFLAYOUT));
2803 swffont->layout->ascent = 0;
2804 swffont->layout->descent = 0;
2805 swffont->layout->leading = 0;
2806 swffont->layout->bounds = (SRECT*)rfx_calloc(sizeof(SRECT)*font->num_glyphs);
2807 swffont->encoding = FONT_ENCODING_UNICODE;
2808 swffont->numchars = font->num_glyphs;
2809 swffont->maxascii = font->max_unicode;
2810 swffont->ascii2glyph = (int*)rfx_calloc(sizeof(int)*swffont->maxascii);
2811 swffont->glyph2ascii = (U16*)rfx_calloc(sizeof(U16)*swffont->numchars);
2812 swffont->glyph = (SWFGLYPH*)rfx_calloc(sizeof(SWFGLYPH)*swffont->numchars);
2813 swffont->glyphnames = (char**)rfx_calloc(sizeof(char*)*swffont->numchars);
2814 for(t=0;t<font->max_unicode;t++) {
2815 swffont->ascii2glyph[t] = font->unicode2glyph[t];
2817 SRECT max = {0,0,0,0};
2818 for(t=0;t<font->num_glyphs;t++) {
2822 swffont->glyph2ascii[t] = font->glyphs[t].unicode;
2823 if(swffont->glyph2ascii[t] == 0xffff || swffont->glyph2ascii[t] == 0x0000) {
2824 /* flash 8 flashtype requires unique unicode IDs for each character.
2825 We use the Unicode private user area to assign characters, hoping that
2826 the font doesn't contain more than 2048 glyphs */
2827 swffont->glyph2ascii[t] = 0xe000 + (t&0x1fff);
2830 if(font->glyphs[t].name) {
2831 swffont->glyphnames[t] = strdup(font->glyphs[t].name);
2833 swffont->glyphnames[t] = 0;
2835 advance = font->glyphs[t].advance;
2837 swf_Shape01DrawerInit(&draw, 0);
2838 line = font->glyphs[t].line;
2840 const double scale = GLYPH_SCALE;
2843 c.x = line->sx * scale; c.y = -line->sy * scale;
2844 //to.x = floor(line->x * scale); to.y = floor(-line->y * scale);
2845 to.x = line->x * scale; to.y = -line->y * scale;
2846 if(line->type == gfx_moveTo) {
2847 draw.moveTo(&draw, &to);
2848 } else if(line->type == gfx_lineTo) {
2849 draw.lineTo(&draw, &to);
2850 } else if(line->type == gfx_splineTo) {
2851 draw.splineTo(&draw, &c, &to);
2856 swffont->glyph[t].shape = swf_ShapeDrawerToShape(&draw);
2858 SRECT bbox = swf_ShapeDrawerGetBBox(&draw);
2859 swf_ExpandRect2(&max, &bbox);
2861 swffont->layout->bounds[t] = bbox;
2863 if(advance<32768.0/20) {
2864 swffont->glyph[t].advance = (int)(advance*20);
2866 //msg("<warning> Advance value overflow in glyph %d", t);
2867 swffont->glyph[t].advance = 32767;
2870 draw.dealloc(&draw);
2872 swf_ExpandRect2(&bounds, &swffont->layout->bounds[t]);
2874 for(t=0;t<font->num_glyphs;t++) {
2875 SRECT bbox = swffont->layout->bounds[t];
2877 /* if the glyph doesn't have a bounding box, use the
2878 combined bounding box (necessary e.g. for space characters) */
2879 if(!(bbox.xmin|bbox.ymin|bbox.xmax|bbox.ymax)) {
2880 swffont->layout->bounds[t] = bbox = max;
2883 /* check that the advance value is reasonable, by comparing it
2884 with the bounding box */
2885 if(bbox.xmax>0 && (bbox.xmax*10 < swffont->glyph[t].advance || !swffont->glyph[t].advance)) {
2886 if(swffont->glyph[t].advance)
2887 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);
2888 swffont->glyph[t].advance = bbox.xmax;
2890 //swffont->glyph[t].advance = bbox.xmax - bbox.xmin;
2894 /* Flash player will use the advance value from the char, and the ascent/descent values
2895 from the layout for text selection.
2896 ascent will extend the char into negative y direction, from the baseline, while descent
2897 will extend in positive y direction, also from the baseline.
2898 The baseline is defined as the y-position zero
2901 swffont->layout->ascent = bounds.ymin<0?-bounds.ymin:0;
2902 swffont->layout->descent = bounds.ymax>0?bounds.ymax:0;
2903 swffont->layout->leading = bounds.ymax - bounds.ymin;
2905 /* if the font has proper ascent/descent values (>0) and those define
2906 greater line spacing that what we estimated from the bounding boxes,
2907 use the font's parameters */
2908 if(font->ascent*20 > swffont->layout->ascent)
2909 swffont->layout->ascent = font->ascent*20;
2910 if(font->descent*20 > swffont->layout->descent)
2911 swffont->layout->descent = font->descent*20;
2916 static void swf_addfont(gfxdevice_t*dev, gfxfont_t*font)
2918 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
2920 if(i->swffont && i->swffont->name && !strcmp((char*)i->swffont->name,font->id))
2921 return; // the requested font is the current font
2923 fontlist_t*last=0,*l = i->fontlist;
2926 if(!strcmp((char*)l->swffont->name, font->id)) {
2927 return; // we already know this font
2931 l = (fontlist_t*)rfx_calloc(sizeof(fontlist_t));
2932 l->swffont = gfxfont_to_swffont(font, font->id, (i->config_flashversion>=8 && !NO_FONT3)?3:2);
2939 swf_FontSetID(l->swffont, getNewID(i->dev));
2941 if(getScreenLogLevel() >= LOGLEVEL_DEBUG) {
2943 // print font information
2944 msg("<debug> Font %s",font->id);
2945 msg("<debug> | ID: %d", l->swffont->id);
2946 msg("<debug> | Version: %d", l->swffont->version);
2947 msg("<debug> | Name: %s", l->swffont->name);
2948 msg("<debug> | Numchars: %d", l->swffont->numchars);
2949 msg("<debug> | Maxascii: %d", l->swffont->maxascii);
2950 msg("<debug> | Style: %d", l->swffont->style);
2951 msg("<debug> | Encoding: %d", l->swffont->encoding);
2952 for(iii=0; iii<l->swffont->numchars;iii++) {
2953 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,
2954 l->swffont->layout->bounds[iii].xmin/20.0,
2955 l->swffont->layout->bounds[iii].ymin/20.0,
2956 l->swffont->layout->bounds[iii].xmax/20.0,
2957 l->swffont->layout->bounds[iii].ymax/20.0
2960 for(t=0;t<l->swffont->maxascii;t++) {
2961 if(l->swffont->ascii2glyph[t] == iii)
2962 msg("<debug> | - maps to %d",t);
2968 static void swf_switchfont(gfxdevice_t*dev, const char*fontid)
2970 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
2972 if(i->swffont && i->swffont->name && !strcmp((char*)i->swffont->name,fontid))
2973 return; // the requested font is the current font
2975 fontlist_t*l = i->fontlist;
2977 if(!strcmp((char*)l->swffont->name, fontid)) {
2978 i->swffont = l->swffont;
2983 msg("<error> Unknown font id: %s", fontid);
2987 /* sets the matrix which is to be applied to characters drawn by swfoutput_drawchar() */
2988 static void setfontscale(gfxdevice_t*dev,double m11,double m12, double m21,double m22,double x, double y, char force)
2994 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
2995 if(i->lastfontm11 == m11 &&
2996 i->lastfontm12 == m12 &&
2997 i->lastfontm21 == m21 &&
2998 i->lastfontm22 == m22 && !force)
3003 i->lastfontm11 = m11;
3004 i->lastfontm12 = m12;
3005 i->lastfontm21 = m21;
3006 i->lastfontm22 = m22;
3008 double xsize = sqrt(m11*m11 + m12*m12);
3009 double ysize = sqrt(m21*m21 + m22*m22);
3012 if(i->config_flashversion>=8 && !NO_FONT3)
3015 i->current_font_size = (xsize>ysize?xsize:ysize)*extrazoom;
3016 if(i->current_font_size < 1)
3017 i->current_font_size = 1;
3020 swf_GetMatrix(0, &m);
3022 if(m21 || m12 || fabs(m11+m22)>0.001) {
3023 double ifs = (double)extrazoom/(i->current_font_size);
3024 m.sx = (S32)((m11*ifs)*65536); m.r1 = -(S32)((m21*ifs)*65536);
3025 m.r0 = (S32)((m12*ifs)*65536); m.sy = -(S32)((m22*ifs)*65536);
3028 /* this is the position of the first char to set a new fontmatrix-
3029 we hope that it's close enough to all other characters using the
3030 font, so we use its position as origin for the matrix */
3037 static void swf_drawchar(gfxdevice_t*dev, gfxfont_t*font, int glyph, gfxcolor_t*color, gfxmatrix_t*matrix)
3039 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
3041 msg("<error> swf_drawchar called (glyph %d) without font", glyph);
3045 if(i->config_drawonlyshapes) {
3046 gfxglyph_t*g = &font->glyphs[glyph];
3047 gfxline_t*line2 = gfxline_clone(g->line);
3048 gfxline_transform(line2, matrix);
3049 dev->fill(dev, line2, color);
3050 gfxline_free(line2);
3054 if(!i->swffont || !i->swffont->name || strcmp((char*)i->swffont->name,font->id)) // not equal to current font
3056 swf_switchfont(dev, font->id); // set the current font
3060 msg("<warning> swf_drawchar: Font is NULL");
3063 if(glyph<0 || glyph>=i->swffont->numchars) {
3064 msg("<warning> No character %d in font %s (%d chars)", glyph, FIXNULL((char*)i->swffont->name), i->swffont->numchars);
3068 setfontscale(dev, matrix->m00, matrix->m01, matrix->m10, matrix->m11, matrix->tx, matrix->ty, 0);
3070 double det = i->fontmatrix.sx/65536.0 * i->fontmatrix.sy/65536.0 -
3071 i->fontmatrix.r0/65536.0 * i->fontmatrix.r1/65536.0;
3072 if(fabs(det) < 0.0005) {
3073 /* x direction equals y direction- the text is invisible */
3074 msg("<verbose> Not drawing invisible character %d (det=%f, m=[%f %f;%f %f]\n", glyph,
3076 i->fontmatrix.sx/65536.0, i->fontmatrix.r1/65536.0,
3077 i->fontmatrix.r0/65536.0, i->fontmatrix.sy/65536.0);
3081 /*if(i->swffont->glyph[glyph].shape->bitlen <= 16) {
3082 msg("<warning> Glyph %d in current charset (%s, %d characters) is empty",
3083 glyph, FIXNULL((char*)i->swffont->name), i->swffont->numchars);
3087 /* calculate character position with respect to the current font matrix */
3088 double s = 20 * GLYPH_SCALE / det;
3089 double px = matrix->tx - i->fontmatrix.tx/20.0;
3090 double py = matrix->ty - i->fontmatrix.ty/20.0;
3091 int x = (SCOORD)(( px * i->fontmatrix.sy/65536.0 - py * i->fontmatrix.r1/65536.0)*s);
3092 int y = (SCOORD)((- px * i->fontmatrix.r0/65536.0 + py * i->fontmatrix.sx/65536.0)*s);
3093 if(x>32767 || x<-32768 || y>32767 || y<-32768) {
3094 msg("<verbose> Moving character origin to %f %f\n", matrix->tx, matrix->ty);
3096 setfontscale(dev, matrix->m00, matrix->m01, matrix->m10, matrix->m11, matrix->tx, matrix->ty, 1);
3097 /* since we just moved the char origin to the current char's position,
3098 it now has the relative position (0,0) */
3107 msg("<trace> Drawing char %d in font %d at %d,%d in color %02x%02x%02x%02x",
3108 glyph, i->swffont->id, x, y, color->r, color->g, color->b, color->a);
3110 if(color->a == 0 && i->config_invisibletexttofront) {
3111 RGBA color2 = *(RGBA*)color;
3112 if(i->config_flashversion>=8) {
3113 // use "multiply" blend mode
3114 color2.a = color2.r = color2.g = color2.b = 255;
3116 i->topchardata = charbuffer_append(i->topchardata, i->swffont, glyph, x, y, i->current_font_size, color2, &i->fontmatrix);
3118 i->chardata = charbuffer_append(i->chardata, i->swffont, glyph, x, y, i->current_font_size, *(RGBA*)color, &i->fontmatrix);
3120 swf_FontUseGlyph(i->swffont, glyph);