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;
628 int charadvance[128];
631 int glyphbits=1; //TODO: can this be zero?
634 if(tag->id != ST_DEFINETEXT &&
635 tag->id != ST_DEFINETEXT2) {
636 msg("<error> internal error: charbuffer_put needs an text tag, not %d\n",tag->id);
640 msg("<warning> charbuffer_put called with zero characters");
643 for(pass = 0; pass < 2; pass++)
654 advancebits++; // add sign bit
655 swf_SetU8(tag, glyphbits);
656 swf_SetU8(tag, advancebits);
659 chararray_t*chardata = _chardata;
664 assert(!chardata->next || chardata->pos == CHARDATAMAX);
665 assert(chardata->pos);
667 int to = chardata->next?chardata->pos-1:chardata->pos;
671 char islast = t==chardata->pos;
673 charatposition_t*chr = &chardata->chr[t];
675 if(lastfont != chr->font ||
678 !colorcompare(&color, &chardata->chr[t].color) ||
680 lastsize != chardata->chr[t].size ||
683 if(charstorepos && pass==0)
686 for(s=0;s<charstorepos;s++)
688 while(charids[s]>=(1<<glyphbits))
690 while(charadvance[s]>=(1<<advancebits))
694 if(charstorepos && pass==1)
696 tag->writeBit = 0; // Q&D
697 swf_SetBits(tag, 0, 1); // GLYPH Record
698 swf_SetBits(tag, charstorepos, 7); // number of glyphs
700 for(s=0;s<charstorepos;s++)
702 swf_SetBits(tag, charids[s], glyphbits);
703 swf_SetBits(tag, charadvance[s], advancebits);
708 if(pass == 1 && !islast)
714 if(lastx != chr->x ||
724 if(!colorcompare(&color, &chr->color))
729 font.id = chr->font->id;
730 if(lastfont != chr->font || lastsize != chr->size)
733 tag->writeBit = 0; // Q&D
734 swf_TextSetInfoRecord(tag, newfont, chr->size, newcolor, newx, newy);
737 lastfont = chr->font;
740 lastsize = chr->size;
743 if(pass==1 && lastchar!=chr->charid) {
744 swf_FontUsePair(chr->font, lastchar, chr->charid);
745 swf_FontUsePair(chr->font, chr->charid, lastchar);
753 if(t<chardata->pos-1) nextx = chardata->chr[t+1].x;
754 if(t==chardata->pos-1 && chardata->next) nextx = chardata->next->chr[0].x;
755 int dx = nextx-chr->x;
758 if(dx>=0 && (dx<(1<<(advancebits-1)) || pass==0)) {
766 charids[charstorepos] = chr->charid;
767 charadvance[charstorepos] = advance;
768 lastchar = chr->charid;
771 chardata = chardata->next;
776 static void chararray_destroy(chararray_t*chr)
779 chararray_t*next = chr->next;
786 static inline int matrix_diff(MATRIX*m1, MATRIX*m2)
788 return memcmp(m1,m2,sizeof(MATRIX));
790 static charbuffer_t*charbuffer_append(charbuffer_t*buf, SWFFONT*font, int charid, int x,int y, int size, RGBA color, MATRIX*m)
792 if(!buf || matrix_diff(&buf->matrix,m)) {
793 charbuffer_t*n = rfx_calloc(sizeof(charbuffer_t));
798 if(!buf->last || buf->last->pos == CHARDATAMAX) {
799 chararray_t*n = rfx_calloc(sizeof(chararray_t));
801 buf->array = buf->last = n;
807 chararray_t*a = buf->last;
808 a->chr[a->pos].font = font;
809 a->chr[a->pos].charid = charid;
810 a->chr[a->pos].x = x;
811 a->chr[a->pos].y = y;
812 a->chr[a->pos].color = color;
813 a->chr[a->pos].size = size;
818 /* Notice: we can only put chars in the range -1639,1638 (-32768/20,32768/20).
819 So if we set this value to high, the char coordinates will overflow.
820 If we set it to low, however, the char positions will be inaccurate */
821 #define GLYPH_SCALE 1
823 static void chararray_writetodev(gfxdevice_t*dev, chararray_t*array, MATRIX*matrix, char invisible)
825 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
827 int textid = getNewID(dev);
828 i->tag = swf_InsertTag(i->tag,ST_DEFINETEXT2);
829 swf_SetU16(i->tag, textid);
831 r = getcharacterbbox(array, matrix, i->config_flashversion);
832 r = swf_ClipRect(i->pagebbox, r);
833 swf_SetRect(i->tag,&r);
834 swf_SetMatrix(i->tag, matrix);
835 msg("<trace> Placing text as ID %d", textid);
836 chararray_writetotag(array, i->tag);
841 if(i->swf->fileVersion >= 8) {
842 i->tag = swf_InsertTag(i->tag, ST_CSMTEXTSETTINGS);
843 swf_SetU16(i->tag, textid);
845 //swf_SetU8(i->tag, /*subpixel grid*/(2<<3)|/*flashtype*/0x40);
846 swf_SetU8(i->tag, /*grid*/(1<<3)|/*flashtype*/0x40);
847 //swf_SetU8(i->tag, /*no grid*/(0<<3)|/*flashtype*/0x40);
849 swf_SetU32(i->tag, 0);//thickness
850 swf_SetU32(i->tag, 0);//sharpness
851 //swf_SetU32(i->tag, 0x20000);//thickness
852 //swf_SetU32(i->tag, 0x800000);//sharpness
853 swf_SetU8(i->tag, 0);//reserved
855 if(invisible && i->config_flashversion>=8) {
856 i->tag = swf_InsertTag(i->tag,ST_PLACEOBJECT3);
857 swf_ObjectPlaceBlend(i->tag,textid,getNewDepth(dev),&i->page_matrix,NULL,NULL,BLENDMODE_MULTIPLY);
859 i->tag = swf_InsertTag(i->tag,ST_PLACEOBJECT2);
860 swf_ObjectPlace(i->tag,textid,getNewDepth(dev),&i->page_matrix,NULL,NULL);
864 static void charbuffer_writetodevandfree(gfxdevice_t*dev, charbuffer_t*buf, char invisible)
867 charbuffer_t*next = buf->next;buf->next = 0;
868 chararray_writetodev(dev, buf->array, &buf->matrix, invisible);
869 chararray_destroy(buf->array);
875 static void endtext(gfxdevice_t*dev)
877 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
880 charbuffer_writetodevandfree(dev, i->chardata, 0);i->chardata = 0;
884 static int watermark2_width=47;
885 static int watermark2_height=11;
886 static int watermark2[47] = {95,1989,71,0,2015,337,1678,0,2015,5,1921,320,1938,25,2006,1024,
887 1042,21,13,960,1039,976,8,2000,1359,1088,31,1989,321,1728,0,1152,
888 1344,832,0,1984,0,896,1088,1088,896,0,1984,128,256,512,1984};
890 static void draw_watermark(gfxdevice_t*dev, gfxbbox_t r, char drawall)
892 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
893 double wx = r.xmax / 5.0;
894 double tx = r.xmax*4.0 / 5.0;
895 double ty = r.ymax-wx*watermark2_height/watermark2_width;
896 double sx = (r.xmax - tx) / watermark2_width;
897 double sy = (r.ymax - ty) / watermark2_height;
900 if(ty > 0 && px > 1.0 && py > 1.0) {
902 for(y=0;y<watermark2_height;y++)
903 for(x=0;x<watermark2_width;x++) {
904 if(((watermark2[x]>>y)&1)) {
905 if(!drawall && rand()%5)
907 unsigned int b = rand();
908 moveto(dev, i->tag, x*sx+tx+((b>>1)&1)/20.0, y*sy+ty+((b>>3)&1)/20.0);
909 lineto(dev, i->tag, x*sx+px+tx+((b>>2)&1)/20.0, y*sy+ty+((b>>3)&1)/20.0);
910 lineto(dev, i->tag, x*sx+px+tx+((b>>2)&1)/20.0, y*sy+py+ty+((b>>4)&1)/20.0);
911 lineto(dev, i->tag, x*sx+tx+((b>>1)&1)/20.0, y*sy+py+ty+((b>>4)&1)/20.0);
912 lineto(dev, i->tag, x*sx+tx+((b>>1)&1)/20.0, y*sy+ty+((b>>3)&1)/20.0);
918 static void swfoutput_setfillcolor(gfxdevice_t* dev, U8 r, U8 g, U8 b, U8 a)
920 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
921 if(i->fillrgb.r == r &&
924 i->fillrgb.a == a) return;
933 static void insert_watermark(gfxdevice_t*dev, char drawall)
935 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
936 if(!drawall && i->watermarks>20)
942 swfoutput_setfillcolor(dev, 0,0,255,192);
944 swfoutput_setfillcolor(dev, rand(),rand(),rand(),(rand()&127)+128);
949 gfxbbox_t r; r.xmin = r.ymin = 0;
952 draw_watermark(dev, r, drawall);
958 static void endpage(gfxdevice_t*dev)
960 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
968 charbuffer_writetodevandfree(dev, i->topchardata, 1);
975 if(i->config_watermark) {
976 insert_watermark(dev, 1);
982 static void addViewer(gfxdevice_t* dev)
984 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
987 RGBA button_colors[3]= {{0xbf,0x00,0x00,0x80},{0xbf,0x20,0x20,0xc0}, {0xbf,0xc0,0xc0,0xff}};
989 int button_sizex = 20;
990 int button_sizey = 20;
992 RGBA black = {255,0,0,0};
994 i->tag = swf_InsertTag(i->tag,ST_DEFINESHAPE3);
996 int ls1 = swf_ShapeAddLineStyle(s,40,&black);
997 int fs1 = swf_ShapeAddSolidFillStyle(s,&button_colors[t/2]);
998 int shapeid = ids[t] = getNewID(dev);
999 swf_SetU16(i->tag,shapeid);
1001 r.xmin = -20*button_sizex;
1002 r.xmax = 20*button_sizex;
1004 r.ymax = 40*button_sizey;
1005 swf_SetRect(i->tag,&r); // set shape bounds
1006 swf_SetShapeHeader(i->tag,s); // write all styles to tag
1007 swf_ShapeSetAll(i->tag,s,0*button_sizex,0,ls1,fs1,0);
1008 swf_ShapeSetLine(i->tag,s,(1-(t&1)*2)*20*button_sizex,20*button_sizey);
1009 swf_ShapeSetLine(i->tag,s,-(1-(t&1)*2)*20*button_sizex,20*button_sizey);
1010 swf_ShapeSetLine(i->tag,s,0,-40*button_sizey);
1011 swf_ShapeSetEnd(i->tag); // finish drawing
1012 swf_ShapeFree(s); // clean shape structure (which isn't needed anymore after writing the tag)
1014 ActionTAG*a1=0,*a2=0,*a3=0;
1015 a1 = action_NextFrame(a1);
1016 a1 = action_Stop(a1);
1017 a1 = action_End(a1);
1019 a2 = action_PreviousFrame(a2);
1020 a2 = action_Stop(a2);
1021 a2 = action_End(a2);
1023 a3 = action_Stop(a3);
1024 a3 = action_End(a3);
1026 i->tag = swf_InsertTag(i->tag, ST_DOACTION);
1027 swf_ActionSet(i->tag,a3);
1029 i->tag = swf_InsertTag(i->tag,ST_DEFINEBUTTON);
1030 int buttonid1 = getNewID(dev);
1031 swf_SetU16(i->tag, buttonid1);
1032 swf_ButtonSetRecord(i->tag,BS_UP|BS_HIT,ids[0],1,NULL,NULL);
1033 swf_ButtonSetRecord(i->tag,BS_OVER,ids[2],1,NULL,NULL);
1034 swf_ButtonSetRecord(i->tag,BS_DOWN,ids[4],1,NULL,NULL);
1035 swf_SetU8(i->tag,0); // end of button records
1036 swf_ActionSet(i->tag,a1);
1038 i->tag = swf_InsertTag(i->tag,ST_DEFINEBUTTON);
1039 int buttonid2 = getNewID(dev);
1040 swf_SetU16(i->tag, buttonid2);
1041 swf_ButtonSetRecord(i->tag,BS_UP|BS_HIT,ids[1],1,NULL,NULL);
1042 swf_ButtonSetRecord(i->tag,BS_OVER,ids[3],1,NULL,NULL);
1043 swf_ButtonSetRecord(i->tag,BS_DOWN,ids[5],1,NULL,NULL);
1044 swf_SetU8(i->tag,0); // end of button records
1045 swf_ActionSet(i->tag,a2);
1047 i->tag = swf_InsertTag(i->tag,ST_PLACEOBJECT2);
1049 swf_GetMatrix(0, &m);
1050 m.tx = button_sizex*20+200;
1051 swf_ObjectPlace(i->tag, buttonid2, 65534,&m,0,0);
1052 i->tag = swf_InsertTag(i->tag,ST_PLACEOBJECT2);
1053 m.tx = button_sizex*20+200+200;
1054 swf_ObjectPlace(i->tag, buttonid1, 65535,&m,0,0);
1058 void swf_startframe(gfxdevice_t*dev, int width, int height)
1060 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1062 if(i->config_protect) {
1063 i->tag = swf_InsertTag(i->tag, ST_PROTECT);
1064 i->config_protect = 0;
1066 if(i->config_simpleviewer) {
1071 if(!i->firstpage && !i->pagefinished)
1074 msg("<verbose> Starting new SWF page of size %dx%d", width, height);
1076 swf_GetMatrix(0, &i->page_matrix);
1077 i->page_matrix.tx = 0;
1078 i->page_matrix.ty = 0;
1085 /* create a bbox structure with the page size. This is used
1086 for clipping shape and text bounding boxes. As we don't want to
1087 generate bounding boxes which extend beyond the movie size (in
1088 order to not confuse Flash), we clip everything against i->pagebbox */
1089 i->pagebbox.xmin = 0;
1090 i->pagebbox.ymin = 0;
1091 i->pagebbox.xmax = width*20;
1092 i->pagebbox.ymax = height*20;
1094 /* increase SWF's bounding box */
1095 swf_ExpandRect2(&i->swf->movieSize, &i->pagebbox);
1097 i->lastframeno = i->frameno;
1099 i->pagefinished = 0;
1103 void swf_endframe(gfxdevice_t*dev)
1105 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1107 if(!i->pagefinished)
1110 if( (i->swf->fileVersion <= 8) && (i->config_insertstoptag) ) {
1112 atag = action_Stop(atag);
1113 atag = action_End(atag);
1114 i->tag = swf_InsertTag(i->tag,ST_DOACTION);
1115 swf_ActionSet(i->tag,atag);
1117 i->tag = swf_InsertTag(i->tag,ST_SHOWFRAME);
1120 for(i->depth;i->depth>i->startdepth;i->depth--) {
1121 i->tag = swf_InsertTag(i->tag,ST_REMOVEOBJECT2);
1122 swf_SetU16(i->tag,i->depth);
1124 i->depth = i->startdepth;
1126 if(i->config_frameresets) {
1127 for(i->currentswfid;i->currentswfid>i->startids;i->currentswfid--) {
1128 i->tag = swf_InsertTag(i->tag,ST_FREECHARACTER);
1129 swf_SetU16(i->tag,i->currentswfid);
1131 i->currentswfid = i->startids;
1135 static void setBackground(gfxdevice_t*dev, int x1, int y1, int x2, int y2)
1137 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1139 rgb.a = rgb.r = rgb.g = rgb.b = 0xff;
1143 int shapeid = getNewID(dev);
1148 i->tag = swf_InsertTag(i->tag, ST_DEFINESHAPE);
1150 fs1 = swf_ShapeAddSolidFillStyle(s, &rgb);
1151 swf_SetU16(i->tag,shapeid);
1152 swf_SetRect(i->tag,&r);
1153 swf_SetShapeHeader(i->tag,s);
1154 swf_ShapeSetAll(i->tag,s,x1,y1,ls1,fs1,0);
1155 swf_ShapeSetLine(i->tag,s,(x2-x1),0);
1156 swf_ShapeSetLine(i->tag,s,0,(y2-y1));
1157 swf_ShapeSetLine(i->tag,s,(x1-x2),0);
1158 swf_ShapeSetLine(i->tag,s,0,(y1-y2));
1159 swf_ShapeSetEnd(i->tag);
1161 i->tag = swf_InsertTag(i->tag, ST_PLACEOBJECT2);
1162 swf_ObjectPlace(i->tag,shapeid,getNewDepth(dev),0,0,0);
1163 i->tag = swf_InsertTag(i->tag, ST_PLACEOBJECT2);
1164 swf_ObjectPlaceClip(i->tag,shapeid,getNewDepth(dev),0,0,0,65535);
1167 /* initialize the swf writer */
1168 void gfxdevice_swf_init(gfxdevice_t* dev)
1170 memset(dev, 0, sizeof(gfxdevice_t));
1174 dev->internal = init_internal_struct(); // set config to default values
1176 dev->startpage = swf_startframe;
1177 dev->endpage = swf_endframe;
1178 dev->finish = swf_finish;
1179 dev->fillbitmap = swf_fillbitmap;
1180 dev->setparameter = swf_setparameter;
1181 dev->stroke = swf_stroke;
1182 dev->startclip = swf_startclip;
1183 dev->endclip = swf_endclip;
1184 dev->fill = swf_fill;
1185 dev->fillbitmap = swf_fillbitmap;
1186 dev->fillgradient = swf_fillgradient;
1187 dev->addfont = swf_addfont;
1188 dev->drawchar = swf_drawchar;
1189 dev->drawlink = swf_drawlink;
1191 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1194 msg("<verbose> initializing swf output\n", i->max_x,i->max_y);
1198 i->swf = (SWF*)rfx_calloc(sizeof(SWF));
1199 i->swf->fileVersion = 0;
1200 i->swf->frameRate = 0x80;
1201 i->swf->movieSize.xmin = 0;
1202 i->swf->movieSize.ymin = 0;
1203 i->swf->movieSize.xmax = 0;
1204 i->swf->movieSize.ymax = 0;
1205 i->swf->fileAttributes = 9; // as3, local-with-network
1207 i->swf->firstTag = swf_InsertTag(NULL,ST_SETBACKGROUNDCOLOR);
1208 i->tag = i->swf->firstTag;
1210 rgb.a = rgb.r = rgb.g = rgb.b = 0xff;
1212 swf_SetRGB(i->tag,&rgb);
1214 i->startdepth = i->depth = 0;
1215 i->startids = i->currentswfid = 0;
1218 static void startshape(gfxdevice_t*dev)
1220 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1225 //if(i->chardatapos && i->chardata[i->chardatapos-1].color.a)
1228 i->tag = swf_InsertTag(i->tag,ST_DEFINESHAPE3);
1230 swf_ShapeNew(&i->shape);
1231 i->linestyleid = swf_ShapeAddLineStyle(i->shape,i->linewidth,&i->strokergb);
1232 i->fillstyleid = swf_ShapeAddSolidFillStyle(i->shape,&i->fillrgb);
1234 RGBA markcol = {0,i->mark[0],i->mark[1],i->mark[2]};
1235 swf_ShapeAddSolidFillStyle(i->shape,&markcol);
1238 i->shapeid = getNewID(dev);
1240 msg("<debug> Using shape id %d", i->shapeid);
1242 swf_SetU16(i->tag,i->shapeid); // ID
1244 i->bboxrectpos = i->tag->len;
1246 swf_SetRect(i->tag,&i->pagebbox);
1248 memset(&i->bboxrect, 0, sizeof(i->bboxrect));
1250 swf_SetShapeStyles(i->tag,i->shape);
1251 swf_ShapeCountBits(i->shape,NULL,NULL);
1252 swf_SetShapeBits(i->tag,i->shape);
1254 /* TODO: do we really need this? */
1255 //swf_ShapeSetAll(i->tag,i->shape,/*x*/0,/*y*/0,i->linestyleid,0,0);
1256 //swf_ShapeSetAll(i->tag,i->shape,/*x*/UNDEFINED_COORD,/*y*/UNDEFINED_COORD,i->linestyleid,0,0);
1257 i->swflastx=i->swflasty=UNDEFINED_COORD;
1258 i->lastwasfill = -1;
1259 i->shapeisempty = 1;
1262 static void starttext(gfxdevice_t*dev)
1264 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1268 if(i->config_watermark) {
1269 insert_watermark(dev, 0);
1272 i->swflastx=i->swflasty=0;
1276 /* TODO: move to ../lib/rfxswf */
1277 void changeRect(gfxdevice_t*dev, TAG*tag, int pos, SRECT*newrect)
1279 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1280 /* determine length of old rect */
1284 swf_GetRect(tag, &old);
1285 swf_ResetReadBits(tag);
1286 int pos_end = tag->pos;
1288 int len = tag->len - pos_end;
1289 U8*data = (U8*)malloc(len);
1290 memcpy(data, &tag->data[pos_end], len);
1293 swf_SetRect(tag, newrect);
1294 swf_SetBlock(tag, data, len);
1296 tag->pos = tag->readBit = 0;
1299 void cancelshape(gfxdevice_t*dev)
1301 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1302 /* delete old shape tag */
1304 i->tag = i->tag->prev;
1305 swf_DeleteTag(0, todel);
1306 if(i->shape) {swf_ShapeFree(i->shape);i->shape=0;}
1308 i->bboxrectpos = -1;
1310 // i->currentswfid--; // doesn't work, for some reason
1313 void fixAreas(gfxdevice_t*dev)
1315 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1316 if(!i->shapeisempty && i->fill &&
1317 (i->bboxrect.xmin == i->bboxrect.xmax ||
1318 i->bboxrect.ymin == i->bboxrect.ymax) &&
1319 i->config_minlinewidth >= 0.001
1321 msg("<debug> Shape has size 0: width=%.2f height=%.2f",
1322 (i->bboxrect.xmax-i->bboxrect.xmin)/20.0,
1323 (i->bboxrect.ymax-i->bboxrect.ymin)/20.0
1326 SRECT r = i->bboxrect;
1328 if(r.xmin == r.xmax && r.ymin == r.ymax) {
1329 /* this thing comes down to a single dot- nothing to fix here */
1335 RGBA save_col = i->strokergb;
1336 int save_width = i->linewidth;
1338 i->strokergb = i->fillrgb;
1339 i->linewidth = (int)(i->config_minlinewidth*20);
1340 if(i->linewidth==0) i->linewidth = 1;
1345 moveto(dev, i->tag, r.xmin/20.0,r.ymin/20.0);
1346 lineto(dev, i->tag, r.xmax/20.0,r.ymax/20.0);
1348 i->strokergb = save_col;
1349 i->linewidth = save_width;
1354 static void endshape_noput(gfxdevice_t*dev)
1356 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1359 //changeRect(dev, i->tag, i->bboxrectpos, &i->bboxrect);
1362 swf_ShapeFree(i->shape);
1370 static void endshape(gfxdevice_t*dev)
1372 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1378 if(i->shapeisempty ||
1380 (i->bboxrect.xmin == i->bboxrect.xmax &&
1381 i->bboxrect.ymin == i->bboxrect.ymax))
1383 // delete the shape again, we didn't do anything
1384 msg("<debug> cancelling shape: bbox is (%f,%f,%f,%f)",
1385 i->bboxrect.xmin /20.0,
1386 i->bboxrect.ymin /20.0,
1387 i->bboxrect.xmax /20.0,
1388 i->bboxrect.ymax /20.0
1394 swf_ShapeSetEnd(i->tag);
1396 SRECT r = swf_ClipRect(i->pagebbox, i->bboxrect);
1397 changeRect(dev, i->tag, i->bboxrectpos, &r);
1399 msg("<trace> Placing shape ID %d", i->shapeid);
1401 i->tag = swf_InsertTag(i->tag,ST_PLACEOBJECT2);
1402 MATRIX m = i->page_matrix;
1403 m.tx += i->shapeposx;
1404 m.ty += i->shapeposy;
1405 swf_ObjectPlace(i->tag,i->shapeid,getNewDepth(dev),&m,NULL,NULL);
1407 if(i->config_animate) {
1408 i->tag = swf_InsertTag(i->tag,ST_SHOWFRAME);
1411 swf_ShapeFree(i->shape);
1414 i->bboxrectpos = -1;
1421 void wipeSWF(SWF*swf)
1423 TAG*tag = swf->firstTag;
1425 TAG*next = tag->next;
1426 if(tag->id != ST_SETBACKGROUNDCOLOR &&
1427 tag->id != ST_END &&
1428 tag->id != ST_DOACTION &&
1429 tag->id != ST_SHOWFRAME) {
1430 swf_DeleteTag(swf, tag);
1436 void swfoutput_finalize(gfxdevice_t*dev)
1438 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1440 if(i->tag && i->tag->id == ST_END)
1441 return; //already done
1443 i->swf->fileVersion = i->config_flashversion;
1444 i->swf->frameRate = i->config_framerate*0x100;
1446 if(i->config_bboxvars) {
1447 TAG* tag = swf_InsertTag(i->swf->firstTag, ST_DOACTION);
1449 a = action_PushString(a, "xmin");
1450 a = action_PushFloat(a, i->swf->movieSize.xmin / 20.0);
1451 a = action_SetVariable(a);
1452 a = action_PushString(a, "ymin");
1453 a = action_PushFloat(a, i->swf->movieSize.ymin / 20.0);
1454 a = action_SetVariable(a);
1455 a = action_PushString(a, "xmax");
1456 a = action_PushFloat(a, i->swf->movieSize.xmax / 20.0);
1457 a = action_SetVariable(a);
1458 a = action_PushString(a, "ymax");
1459 a = action_PushFloat(a, i->swf->movieSize.ymax / 20.0);
1460 a = action_SetVariable(a);
1461 a = action_PushString(a, "width");
1462 a = action_PushFloat(a, (i->swf->movieSize.xmax - i->swf->movieSize.xmin) / 20.0);
1463 a = action_SetVariable(a);
1464 a = action_PushString(a, "height");
1465 a = action_PushFloat(a, (i->swf->movieSize.ymax - i->swf->movieSize.ymin) / 20.0);
1466 a = action_SetVariable(a);
1468 swf_ActionSet(tag, a);
1473 free(i->mark);i->mark = 0;
1477 fontlist_t *iterator = i->fontlist;
1478 char use_font3 = i->config_flashversion>=8 && !NO_FONT3;
1480 TAG*mtag = i->swf->firstTag;
1481 if(iterator->swffont) {
1483 // needs to be done before the reduce
1484 swf_FontCreateAlignZones(iterator->swffont);
1486 if(!i->config_storeallcharacters) {
1487 msg("<debug> Reducing font %s", iterator->swffont->name);
1488 swf_FontReduce(iterator->swffont);
1490 int used = iterator->swffont->use && iterator->swffont->use->used_glyphs;
1493 mtag = swf_InsertTag(mtag, ST_DEFINEFONT2);
1494 swf_FontSetDefine2(mtag, iterator->swffont);
1496 mtag = swf_InsertTag(mtag, ST_DEFINEFONT3);
1497 swf_FontSetDefine2(mtag, iterator->swffont);
1498 if(iterator->swffont->alignzones) {
1499 mtag = swf_InsertTag(mtag, ST_DEFINEFONTALIGNZONES);
1500 swf_FontSetAlignZones(mtag, iterator->swffont);
1506 iterator = iterator->next;
1509 i->tag = swf_InsertTag(i->tag,ST_END);
1510 TAG* tag = i->tag->prev;
1512 /* remove the removeobject2 tags between the last ST_SHOWFRAME
1513 and the ST_END- they confuse the flash player */
1514 while(tag->id == ST_REMOVEOBJECT2) {
1515 TAG* prev = tag->prev;
1516 swf_DeleteTag(i->swf, tag);
1523 if(i->config_enablezlib || i->config_flashversion>=6) {
1524 i->swf->compressed = 1;
1527 /* Add AVM2 actionscript */
1528 if(i->config_flashversion>=9 &&
1529 (i->config_insertstoptag || i->hasbuttons) && !i->config_linknameurl) {
1530 swf_AddButtonLinks(i->swf, i->config_insertstoptag,
1531 i->config_internallinkfunction||i->config_externallinkfunction);
1533 // if(i->config_reordertags)
1534 // swf_Optimize(i->swf);
1537 int swfresult_save(gfxresult_t*gfx, const char*filename)
1539 SWF*swf = (SWF*)gfx->internal;
1542 fi = open(filename, O_BINARY|O_CREAT|O_TRUNC|O_WRONLY, 0777);
1547 msg("<fatal> Could not create \"%s\". ", FIXNULL(filename));
1551 if FAILED(swf_WriteSWF(fi,swf))
1552 msg("<error> WriteSWF() failed.\n");
1558 void* swfresult_get(gfxresult_t*gfx, const char*name)
1560 SWF*swf = (SWF*)gfx->internal;
1561 if(!strcmp(name, "swf")) {
1562 return (void*)swf_CopySWF(swf);
1563 } else if(!strcmp(name, "xmin")) {
1564 return (void*)(ptroff_t)(swf->movieSize.xmin/20);
1565 } else if(!strcmp(name, "ymin")) {
1566 return (void*)(ptroff_t)(swf->movieSize.ymin/20);
1567 } else if(!strcmp(name, "xmax")) {
1568 return (void*)(ptroff_t)(swf->movieSize.xmax/20);
1569 } else if(!strcmp(name, "ymax")) {
1570 return (void*)(ptroff_t)(swf->movieSize.ymax/20);
1571 } else if(!strcmp(name, "width")) {
1572 return (void*)(ptroff_t)((swf->movieSize.xmax - swf->movieSize.xmin)/20);
1573 } else if(!strcmp(name, "height")) {
1574 return (void*)(ptroff_t)((swf->movieSize.ymax - swf->movieSize.ymin)/20);
1578 void swfresult_destroy(gfxresult_t*gfx)
1581 swf_FreeTags((SWF*)gfx->internal);
1582 free(gfx->internal);
1585 memset(gfx, 0, sizeof(gfxresult_t));
1589 static void swfoutput_destroy(gfxdevice_t* dev);
1591 gfxresult_t* swf_finish(gfxdevice_t* dev)
1593 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1596 if(i->config_linktarget) {
1597 free(i->config_linktarget);
1598 i->config_linktarget = 0;
1601 swfoutput_finalize(dev);
1602 SWF* swf = i->swf;i->swf = 0;
1603 swfoutput_destroy(dev);
1605 result = (gfxresult_t*)rfx_calloc(sizeof(gfxresult_t));
1606 result->internal = swf;
1607 result->save = swfresult_save;
1609 result->get = swfresult_get;
1610 result->destroy = swfresult_destroy;
1614 /* Perform cleaning up */
1615 static void swfoutput_destroy(gfxdevice_t* dev)
1617 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1619 /* not initialized yet- nothing to destroy */
1623 fontlist_t *tmp,*iterator = i->fontlist;
1625 if(iterator->swffont) {
1626 swf_FontFree(iterator->swffont);iterator->swffont=0;
1629 iterator = iterator->next;
1632 if(i->swf) {swf_FreeTags(i->swf);free(i->swf);i->swf = 0;}
1635 memset(dev, 0, sizeof(gfxdevice_t));
1638 static void swfoutput_setstrokecolor(gfxdevice_t* dev, U8 r, U8 g, U8 b, U8 a)
1640 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1641 if(i->strokergb.r == r &&
1642 i->strokergb.g == g &&
1643 i->strokergb.b == b &&
1644 i->strokergb.a == a) return;
1654 //#define ROUND_UP 19
1655 //#define ROUND_UP 10
1657 static void swfoutput_setlinewidth(gfxdevice_t*dev, double _linewidth)
1659 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1660 if(i->linewidth == (U16)(_linewidth*20+19.0/20.0))
1664 i->linewidth = (U16)(_linewidth*20+19.0/20.0);
1668 static void drawlink(gfxdevice_t*dev, ActionTAG*,ActionTAG*, gfxline_t*points, char mouseover, char*type, const char*url);
1669 static void swfoutput_namedlink(gfxdevice_t*dev, char*name, gfxline_t*points);
1670 static void swfoutput_linktopage(gfxdevice_t*dev, int page, gfxline_t*points);
1671 static void swfoutput_linktourl(gfxdevice_t*dev, const char*url, gfxline_t*points);
1673 /*void swfoutput_drawlink(gfxdevice_t*dev, char*url, gfxline_t*points)
1675 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1676 dev->drawlink(dev, points, url);
1679 void swf_drawlink(gfxdevice_t*dev, gfxline_t*points, const char*url)
1681 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1683 if(i->config_disablelinks)
1686 if(!strncmp("http://pdf2swf:", url, 15)) {
1687 char*tmp = strdup(url);
1688 int l = strlen(tmp);
1691 swfoutput_namedlink(dev, tmp+15, points);
1694 } else if(!strncmp("page", url, 4)) {
1697 if(url[t]<'0' || url[t]>'9')
1700 int page = atoi(&url[4]);
1701 if(page<0) page = 0;
1702 swfoutput_linktopage(dev, page, points);
1705 swfoutput_linktourl(dev, url, points);
1708 void swfoutput_linktourl(gfxdevice_t*dev, const char*url, gfxline_t*points)
1710 ActionTAG* actions = 0;
1711 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1717 /* TODO: escape special characters in url */
1719 if(i->config_externallinkfunction && i->config_flashversion<=8) {
1720 actions = action_PushString(actions, url); //parameter
1721 actions = action_PushInt(actions, 1); //number of parameters (1)
1722 actions = action_PushString(actions, i->config_externallinkfunction); //function name
1723 actions = action_CallFunction(actions);
1724 } else if(!i->config_linktarget) {
1725 if(!i->config_opennewwindow)
1726 actions = action_GetUrl(actions, url, "_parent");
1728 actions = action_GetUrl(actions, url, "_this");
1730 actions = action_GetUrl(actions, url, i->config_linktarget);
1732 actions = action_End(actions);
1734 drawlink(dev, actions, 0, points, 0, "url", url);
1736 swf_ActionFree(actions);
1738 void swfoutput_linktopage(gfxdevice_t*dev, int page, gfxline_t*points)
1740 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1741 ActionTAG* actions = 0;
1748 if(!i->config_internallinkfunction || i->config_flashversion>=9) {
1749 actions = action_GotoFrame(actions, page-1);
1750 actions = action_End(actions);
1752 actions = action_PushInt(actions, page); //parameter
1753 actions = action_PushInt(actions, 1); //number of parameters (1)
1754 actions = action_PushString(actions, i->config_internallinkfunction); //function name
1755 actions = action_CallFunction(actions);
1756 actions = action_End(actions);
1760 sprintf(name, "page%d", page);
1762 drawlink(dev, actions, 0, points, 0, "page", name);
1764 swf_ActionFree(actions);
1767 /* Named Links (a.k.a. Acrobatmenu) are used to implement various gadgets
1768 of the viewer objects, like subtitles, index elements etc.
1770 void swfoutput_namedlink(gfxdevice_t*dev, char*name, gfxline_t*points)
1772 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1773 ActionTAG *actions1,*actions2;
1774 char*tmp = strdup(name);
1783 if(!strncmp(tmp, "call:", 5))
1785 char*x = strchr(&tmp[5], ':');
1787 actions1 = action_PushInt(0, 0); //number of parameters (0)
1788 actions1 = action_PushString(actions1, &tmp[5]); //function name
1789 actions1 = action_CallFunction(actions1);
1790 actions1 = action_End(actions1);
1793 actions1 = action_PushString(0, x+1); //parameter
1794 actions1 = action_PushInt(actions1, 1); //number of parameters (1)
1795 actions1 = action_PushString(actions1, &tmp[5]); //function name
1796 actions1 = action_CallFunction(actions1);
1797 actions1 = action_End(actions1);
1799 actions2 = action_End(0);
1805 actions1 = action_PushString(0, "/:subtitle");
1806 actions1 = action_PushString(actions1, name);
1807 actions1 = action_SetVariable(actions1);
1808 actions1 = action_End(actions1);
1810 actions2 = action_PushString(0, "/:subtitle");
1811 actions2 = action_PushString(actions2, "");
1812 actions2 = action_SetVariable(actions2);
1813 actions2 = action_End(actions2);
1817 drawlink(dev, actions1, actions2, points, mouseover, type, name);
1819 swf_ActionFree(actions1);
1820 swf_ActionFree(actions2);
1824 static void drawgfxline(gfxdevice_t*dev, gfxline_t*line, int fill)
1826 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1827 gfxcoord_t lastx=0,lasty=0,px=0,py=0;
1829 int lines= 0, splines=0;
1836 /* check whether the next segment is zero */
1837 if(line->type == gfx_moveTo) {
1838 moveto(dev, i->tag, line->x, line->y);
1839 px = lastx = line->x;
1840 py = lasty = line->y;
1842 } if(line->type == gfx_lineTo) {
1843 lineto(dev, i->tag, line->x, line->y);
1848 } else if(line->type == gfx_splineTo) {
1850 s.x = line->sx;p.x = line->x;
1851 s.y = line->sy;p.y = line->y;
1852 splineto(dev, i->tag, s, p);
1860 msg("<trace> drawgfxline, %d lines, %d splines", lines, splines);
1864 static void drawlink(gfxdevice_t*dev, ActionTAG*actions1, ActionTAG*actions2, gfxline_t*points, char mouseover, char*type, const char*url)
1866 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1875 int buttonid = getNewID(dev);
1876 gfxbbox_t bbox = gfxline_getbbox(points);
1878 if(i->config_linknameurl) {
1886 myshapeid = getNewID(dev);
1887 i->tag = swf_InsertTag(i->tag,ST_DEFINESHAPE3);
1888 swf_ShapeNew(&i->shape);
1889 rgb.r = rgb.b = rgb.a = rgb.g = 0;
1890 fsid = swf_ShapeAddSolidFillStyle(i->shape,&rgb);
1891 swf_SetU16(i->tag, myshapeid);
1892 r.xmin = (int)(bbox.xmin*20);
1893 r.ymin = (int)(bbox.ymin*20);
1894 r.xmax = (int)(bbox.xmax*20);
1895 r.ymax = (int)(bbox.ymax*20);
1896 r = swf_ClipRect(i->pagebbox, r);
1897 swf_SetRect(i->tag,&r);
1898 swf_SetShapeStyles(i->tag,i->shape);
1899 swf_ShapeCountBits(i->shape,NULL,NULL);
1900 swf_SetShapeBits(i->tag,i->shape);
1901 swf_ShapeSetAll(i->tag,i->shape,/*x*/0,/*y*/0,0,fsid,0);
1902 i->swflastx = i->swflasty = 0;
1903 drawgfxline(dev, points, 1);
1904 swf_ShapeSetEnd(i->tag);
1905 swf_ShapeFree(i->shape);
1908 myshapeid2 = getNewID(dev);
1909 i->tag = swf_InsertTag(i->tag,ST_DEFINESHAPE3);
1910 swf_ShapeNew(&i->shape);
1912 rgb = i->config_linkcolor;
1914 fsid = swf_ShapeAddSolidFillStyle(i->shape,&rgb);
1915 swf_SetU16(i->tag, myshapeid2);
1916 r.xmin = (int)(bbox.xmin*20);
1917 r.ymin = (int)(bbox.ymin*20);
1918 r.xmax = (int)(bbox.xmax*20);
1919 r.ymax = (int)(bbox.ymax*20);
1920 r = swf_ClipRect(i->pagebbox, r);
1921 swf_SetRect(i->tag,&r);
1922 swf_SetShapeStyles(i->tag,i->shape);
1923 swf_ShapeCountBits(i->shape,NULL,NULL);
1924 swf_SetShapeBits(i->tag,i->shape);
1925 swf_ShapeSetAll(i->tag,i->shape,/*x*/0,/*y*/0,0,fsid,0);
1926 i->swflastx = i->swflasty = 0;
1927 drawgfxline(dev, points, 1);
1928 swf_ShapeSetEnd(i->tag);
1929 swf_ShapeFree(i->shape);
1933 i->tag = swf_InsertTag(i->tag,ST_DEFINEBUTTON);
1934 swf_SetU16(i->tag,buttonid); //id
1935 swf_ButtonSetFlags(i->tag, 0); //menu=no
1936 swf_ButtonSetRecord(i->tag,0x01,myshapeid,i->depth,0,0);
1937 swf_ButtonSetRecord(i->tag,0x02,myshapeid2,i->depth,0,0);
1938 swf_ButtonSetRecord(i->tag,0x04,myshapeid2,i->depth,0,0);
1939 swf_ButtonSetRecord(i->tag,0x08,myshapeid,i->depth,0,0);
1940 swf_SetU8(i->tag,0);
1941 swf_ActionSet(i->tag,actions1);
1942 swf_SetU8(i->tag,0);
1946 i->tag = swf_InsertTag(i->tag,ST_DEFINEBUTTON2);
1947 swf_SetU16(i->tag,buttonid); //id
1948 swf_ButtonSetFlags(i->tag, 0); //menu=no
1949 swf_ButtonSetRecord(i->tag,0x01,myshapeid,i->depth,0,0);
1950 swf_ButtonSetRecord(i->tag,0x02,myshapeid2,i->depth,0,0);
1951 swf_ButtonSetRecord(i->tag,0x04,myshapeid2,i->depth,0,0);
1952 swf_ButtonSetRecord(i->tag,0x08,myshapeid,i->depth,0,0);
1953 swf_SetU8(i->tag,0); // end of button records
1954 swf_ButtonSetCondition(i->tag, BC_IDLE_OVERUP);
1955 swf_ActionSet(i->tag,actions1);
1957 swf_ButtonSetCondition(i->tag, BC_OVERUP_IDLE);
1958 swf_ActionSet(i->tag,actions2);
1959 swf_SetU8(i->tag,0);
1960 swf_ButtonPostProcess(i->tag, 2);
1962 swf_SetU8(i->tag,0);
1963 swf_ButtonPostProcess(i->tag, 1);
1969 const char* name = 0;
1970 if(i->config_linknameurl) {
1971 buf2 = malloc(strlen(type)+strlen(url)+2);
1972 sprintf(buf2, "%s:%s", type, url);
1976 sprintf(buf, "button%d", buttonid);
1979 msg("<trace> Placing link ID %d", buttonid);
1980 i->tag = swf_InsertTag(i->tag,ST_PLACEOBJECT2);
1982 if(posx!=0 || posy!=0) {
1984 p.x = (int)(posx*20);
1985 p.y = (int)(posy*20);
1986 p = swf_TurnPoint(p, &i->page_matrix);
1991 swf_ObjectPlace(i->tag, buttonid, getNewDepth(dev),&m,0,(U8*)name);
1993 swf_ObjectPlace(i->tag, buttonid, getNewDepth(dev),&i->page_matrix,0,(U8*)name);
2003 for(t=0;t<picpos;t++)
2005 if(pic_xids[t] == xid &&
2006 pic_yids[t] == yid) {
2007 width = pic_width[t];
2008 height = pic_height[t];
2012 pic_ids[picpos] = swfoutput_drawimagelosslessN(&output, pic, pal, width, height, x1,y1,x2,y2,x3,y3,x4,y4, numpalette);
2013 pic_xids[picpos] = xid;
2014 pic_yids[picpos] = yid;
2015 pic_width[picpos] = width;
2016 pic_height[picpos] = height;
2019 pic[width*y+x] = buf[0];
2023 xid += pal[1].r*3 + pal[1].g*11 + pal[1].b*17;
2024 yid += pal[1].r*7 + pal[1].g*5 + pal[1].b*23;
2028 xid += x*r+x*b*3+x*g*7+x*a*11;
2029 yid += y*r*3+y*b*17+y*g*19+y*a*11;
2031 for(t=0;t<picpos;t++)
2033 if(pic_xids[t] == xid &&
2034 pic_yids[t] == yid) {
2043 int swf_setparameter(gfxdevice_t*dev, const char*name, const char*value)
2045 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
2047 msg("<trace> swfdevice: %s=%s", name, value);
2048 if(!strcmp(name, "jpegsubpixels")) {
2049 i->config_jpegsubpixels = atof(value);
2050 } else if(!strcmp(name, "ppmsubpixels")) {
2051 i->config_ppmsubpixels = atof(value);
2052 } else if(!strcmp(name, "subpixels")) {
2053 i->config_ppmsubpixels = i->config_jpegsubpixels = atof(value);
2054 } else if(!strcmp(name, "drawonlyshapes")) {
2055 i->config_drawonlyshapes = atoi(value);
2056 } else if(!strcmp(name, "ignoredraworder")) {
2057 i->config_ignoredraworder = atoi(value);
2058 } else if(!strcmp(name, "mark")) {
2059 if(!value || !value[0]) {
2060 if(i->mark) free(i->mark);
2064 i->mark = strdup("...");
2065 for(t=0;t<3;t++) if(value[t]) i->mark[t] = value[t];
2067 } else if(!strcmp(name, "filloverlap")) {
2068 i->config_filloverlap = atoi(value);
2069 } else if(!strcmp(name, "linksopennewwindow")) {
2070 i->config_opennewwindow = atoi(value);
2071 } else if(!strcmp(name, "opennewwindow")) {
2072 i->config_opennewwindow = atoi(value);
2073 } else if(!strcmp(name, "storeallcharacters")) {
2074 i->config_storeallcharacters = atoi(value);
2075 } else if(!strcmp(name, "enablezlib")) {
2076 i->config_enablezlib = atoi(value);
2077 } else if(!strcmp(name, "bboxvars")) {
2078 i->config_bboxvars = atoi(value);
2079 } else if(!strcmp(name, "dots")) {
2080 i->config_dots = atoi(value);
2081 } else if(!strcmp(name, "frameresets")) {
2082 i->config_frameresets = atoi(value);
2083 } else if(!strcmp(name, "showclipshapes")) {
2084 i->config_showclipshapes = atoi(value);
2085 } else if(!strcmp(name, "reordertags")) {
2086 i->config_reordertags = atoi(value);
2087 } else if(!strcmp(name, "internallinkfunction")) {
2088 i->config_internallinkfunction = strdup(value);
2089 } else if(!strcmp(name, "externallinkfunction")) {
2090 i->config_externallinkfunction = strdup(value);
2091 } else if(!strcmp(name, "linkfunction")) { //sets both internallinkfunction and externallinkfunction
2092 i->config_internallinkfunction = strdup(value);
2093 i->config_externallinkfunction = strdup(value);
2094 } else if(!strcmp(name, "disable_polygon_conversion")) {
2095 i->config_disable_polygon_conversion = atoi(value);
2096 } else if(!strcmp(name, "normalize_polygon_positions")) {
2097 i->config_normalize_polygon_positions = atoi(value);
2098 } else if(!strcmp(name, "wxwindowparams")) {
2099 i->config_watermark = atoi(value);
2100 } else if(!strcmp(name, "insertstop")) {
2101 i->config_insertstoptag = atoi(value);
2102 } else if(!strcmp(name, "protect")) {
2103 i->config_protect = atoi(value);
2104 if(i->config_protect && i->tag) {
2105 i->tag = swf_InsertTag(i->tag, ST_PROTECT);
2107 } else if(!strcmp(name, "flashversion")) {
2108 i->config_flashversion = atoi(value);
2110 i->swf->fileVersion = i->config_flashversion;
2112 } else if(!strcmp(name, "framerate")) {
2113 i->config_framerate = atof(value);
2115 i->swf->frameRate = i->config_framerate*0x100;
2117 } else if(!strcmp(name, "minlinewidth")) {
2118 i->config_minlinewidth = atof(value);
2119 } else if(!strcmp(name, "caplinewidth")) {
2120 i->config_caplinewidth = atof(value);
2121 } else if(!strcmp(name, "linktarget")) {
2122 i->config_linktarget = strdup(value);
2123 } else if(!strcmp(name, "invisibletexttofront")) {
2124 i->config_invisibletexttofront = atoi(value);
2125 } else if(!strcmp(name, "noclips")) {
2126 i->config_noclips = atoi(value);
2127 } else if(!strcmp(name, "dumpfonts")) {
2128 i->config_dumpfonts = atoi(value);
2129 } else if(!strcmp(name, "animate")) {
2130 i->config_animate = atoi(value);
2131 } else if(!strcmp(name, "disablelinks")) {
2132 i->config_disablelinks = atoi(value);
2133 } else if(!strcmp(name, "simpleviewer")) {
2134 i->config_simpleviewer = atoi(value);
2135 } else if(!strcmp(name, "next_bitmap_is_jpeg")) {
2137 } else if(!strcmp(name, "jpegquality")) {
2138 int val = atoi(value);
2140 if(val>101) val=101;
2141 i->config_jpegquality = val;
2142 } else if(!strcmp(name, "splinequality")) {
2143 int v = atoi(value);
2144 v = 500-(v*5); // 100% = 0.25 pixel, 0% = 25 pixel
2146 i->config_splinemaxerror = v;
2147 } else if(!strcmp(name, "fontquality")) {
2148 int v = atoi(value);
2149 v = 500-(v*5); // 100% = 0.25 pixel, 0% = 25 pixel
2151 i->config_fontsplinemaxerror = v;
2152 } else if(!strcmp(name, "linkcolor")) {
2153 if(strlen(value)!=8) {
2154 fprintf(stderr, "Unknown format for option 'linkcolor'. (%s <-> RRGGBBAA)\n", value);
2157 # define NIBBLE(s) (((s)>='0' && (s)<='9')?((s)-'0'):((s)&0x0f)+9)
2158 i->config_linkcolor.r = NIBBLE(value[0])<<4 | NIBBLE(value[1]);
2159 i->config_linkcolor.g = NIBBLE(value[2])<<4 | NIBBLE(value[3]);
2160 i->config_linkcolor.b = NIBBLE(value[4])<<4 | NIBBLE(value[5]);
2161 i->config_linkcolor.a = NIBBLE(value[6])<<4 | NIBBLE(value[7]);
2162 } else if(!strcmp(name, "help")) {
2163 printf("\nSWF layer options:\n");
2164 printf("jpegsubpixels=<pixels> resolution adjustment for jpeg images (same as jpegdpi, but in pixels)\n");
2165 printf("ppmsubpixels=<pixels resolution adjustment for lossless images (same as ppmdpi, but in pixels)\n");
2166 printf("subpixels=<pixels> shortcut for setting both jpegsubpixels and ppmsubpixels\n");
2167 printf("drawonlyshapes convert everything to shapes (currently broken)\n");
2168 printf("ignoredraworder allow to perform a few optimizations for creating smaller SWFs\n");
2169 printf("linksopennewwindow make links open a new browser window\n");
2170 printf("linktarget target window name of new links\n");
2171 printf("linkcolor=<color) color of links (format: RRGGBBAA)\n");
2172 printf("linknameurl Link buttons will be named like the URL they refer to (handy for iterating through links with actionscript)\n");
2173 printf("storeallcharacters don't reduce the fonts to used characters in the output file\n");
2174 printf("enablezlib switch on zlib compression (also done if flashversion>=6)\n");
2175 printf("bboxvars store the bounding box of the SWF file in actionscript variables\n");
2176 printf("dots Take care to handle dots correctly\n");
2177 printf("reordertags=0/1 (default: 1) perform some tag optimizations\n");
2178 printf("internallinkfunction=<name> when the user clicks a internal link (to a different page) in the converted file, this actionscript function is called\n");
2179 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");
2180 printf("disable_polygon_conversion never convert strokes to polygons (will remove capstyles and joint styles)\n");
2181 printf("caplinewidth=<width> the minimum thichness a line needs to have so that capstyles become visible (and are converted)\n");
2182 printf("insertstop put an ActionScript \"STOP\" tag in every frame\n");
2183 printf("protect add a \"protect\" tag to the file, to prevent loading in the Flash editor\n");
2184 printf("flashversion=<version> the SWF fileversion (6)\n");
2185 printf("framerate=<fps> SWF framerate\n");
2186 printf("minlinewidth=<width> convert horizontal/vertical boxes smaller than this width to lines (0.05) \n");
2187 printf("simpleviewer Add next/previous buttons to the SWF\n");
2188 printf("animate insert a showframe tag after each placeobject (animate draw order of PDF files)\n");
2189 printf("jpegquality=<quality> set compression quality of jpeg images\n");
2190 printf("splinequality=<value> Set the quality of spline convertion to value (0-100, default: 100).\n");
2191 printf("disablelinks Disable links.\n");
2198 // --------------------------------------------------------------------
2200 static CXFORM gfxcxform_to_cxform(gfxcxform_t* c)
2203 swf_GetCXForm(0, &cx, 1);
2206 if(c->rg!=0 || c->rb!=0 || c->ra!=0 ||
2207 c->gr!=0 || c->gb!=0 || c->ga!=0 ||
2208 c->br!=0 || c->bg!=0 || c->ba!=0 ||
2209 c->ar!=0 || c->ag!=0 || c->ab!=0)
2210 msg("<warning> CXForm not SWF-compatible");
2212 cx.a0 = (S16)(c->aa*256);
2213 cx.r0 = (S16)(c->rr*256);
2214 cx.g0 = (S16)(c->gg*256);
2215 cx.b0 = (S16)(c->bb*256);
2224 static int imageInCache(gfxdevice_t*dev, void*data, int width, int height)
2228 static void addImageToCache(gfxdevice_t*dev, void*data, int width, int height)
2232 static int add_image(swfoutput_internal*i, gfximage_t*img, int targetwidth, int targetheight, int* newwidth, int* newheight)
2234 gfxdevice_t*dev = i->dev;
2236 RGBA*mem = (RGBA*)img->data;
2238 int sizex = img->width;
2239 int sizey = img->height;
2240 int is_jpeg = i->jpeg;
2243 int newsizex=sizex, newsizey=sizey;
2246 if(is_jpeg && i->config_jpegsubpixels) {
2247 newsizex = (int)(targetwidth*i->config_jpegsubpixels + 0.5);
2248 newsizey = (int)(targetheight*i->config_jpegsubpixels + 0.5);
2249 } else if(!is_jpeg && i->config_ppmsubpixels) {
2250 newsizex = (int)(targetwidth*i->config_ppmsubpixels + 0.5);
2251 newsizey = (int)(targetheight*i->config_ppmsubpixels + 0.5);
2255 if(sizex<=0 || sizey<=0)
2262 /* TODO: cache images */
2264 if(newsizex<sizex || newsizey<sizey) {
2265 msg("<verbose> Scaling %dx%d image to %dx%d", sizex, sizey, newsizex, newsizey);
2266 newpic = swf_ImageScale(mem, sizex, sizey, newsizex, newsizey);
2267 *newwidth = sizex = newsizex;
2268 *newheight = sizey = newsizey;
2271 *newwidth = newsizex = sizex;
2272 *newheight = newsizey = sizey;
2275 int num_colors = swf_ImageGetNumberOfPaletteEntries(mem,sizex,sizey,0);
2276 int has_alpha = swf_ImageHasAlpha(mem,sizex,sizey);
2278 msg("<verbose> Drawing %dx%d %s%simage (id %d) at size %dx%d (%dx%d), %s%d colors",
2280 has_alpha?(has_alpha==2?"semi-transparent ":"transparent "):"",
2281 is_jpeg?"jpeg-":"", i->currentswfid+1,
2283 targetwidth, targetheight,
2284 /*newsizex, newsizey,*/
2285 num_colors>256?">":"", num_colors>256?256:num_colors);
2287 /*RGBA* pal = (RGBA*)rfx_alloc(sizeof(RGBA)*num_colors);
2288 swf_ImageGetNumberOfPaletteEntries(mem,sizex,sizey,pal);
2290 for(t=0;t<num_colors;t++) {
2291 printf("%02x%02x%02x%02x ",
2292 pal[t].r, pal[t].g, pal[t].b, pal[t].a);
2299 int cacheid = imageInCache(dev, mem, sizex, sizey);
2302 bitid = getNewID(dev);
2304 i->tag = swf_AddImage(i->tag, bitid, mem, sizex, sizey, i->config_jpegquality);
2305 addImageToCache(dev, mem, sizex, sizey);
2315 static SRECT gfxline_getSWFbbox(gfxline_t*line)
2317 gfxbbox_t bbox = gfxline_getbbox(line);
2319 r.xmin = (int)(bbox.xmin*20);
2320 r.ymin = (int)(bbox.ymin*20);
2321 r.xmax = (int)(bbox.xmax*20);
2322 r.ymax = (int)(bbox.ymax*20);
2326 int line_is_empty(gfxline_t*line)
2329 if(line->type != gfx_moveTo)
2336 static void swf_fillbitmap(gfxdevice_t*dev, gfxline_t*line, gfximage_t*img, gfxmatrix_t*matrix, gfxcxform_t*cxform)
2338 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
2340 if(line_is_empty(line))
2346 int targetx = (int)(sqrt(matrix->m00*matrix->m00 + matrix->m01*matrix->m01)*img->width);
2347 int targety = (int)(sqrt(matrix->m10*matrix->m10 + matrix->m11*matrix->m11)*img->height);
2349 int newwidth=0,newheight=0;
2350 int bitid = add_image(i, img, targetx, targety, &newwidth, &newheight);
2353 double fx = (double)img->width / (double)newwidth;
2354 double fy = (double)img->height / (double)newheight;
2357 m.sx = (int)(65536*20*matrix->m00*fx); m.r1 = (int)(65536*20*matrix->m10*fy);
2358 m.r0 = (int)(65536*20*matrix->m01*fx); m.sy = (int)(65536*20*matrix->m11*fy);
2359 m.tx = (int)(matrix->tx*20);
2360 m.ty = (int)(matrix->ty*20);
2363 int myshapeid = getNewID(dev);
2364 i->tag = swf_InsertTag(i->tag,ST_DEFINESHAPE);
2366 swf_ShapeNew(&shape);
2367 int fsid = swf_ShapeAddBitmapFillStyle(shape,&m,bitid,1);
2368 swf_SetU16(i->tag, myshapeid);
2369 SRECT r = gfxline_getSWFbbox(line);
2370 r = swf_ClipRect(i->pagebbox, r);
2371 swf_SetRect(i->tag,&r);
2372 swf_SetShapeStyles(i->tag,shape);
2373 swf_ShapeCountBits(shape,NULL,NULL);
2374 swf_SetShapeBits(i->tag,shape);
2375 swf_ShapeSetAll(i->tag,shape,UNDEFINED_COORD,UNDEFINED_COORD,0,fsid,0);
2376 i->swflastx = i->swflasty = UNDEFINED_COORD;
2377 drawgfxline(dev, line, 1);
2378 swf_ShapeSetEnd(i->tag);
2379 swf_ShapeFree(shape);
2381 msg("<trace> Placing image, shape ID %d, bitmap ID %d", myshapeid, bitid);
2382 i->tag = swf_InsertTag(i->tag,ST_PLACEOBJECT2);
2383 CXFORM cxform2 = gfxcxform_to_cxform(cxform);
2384 swf_ObjectPlace(i->tag,myshapeid,getNewDepth(dev),&i->page_matrix,&cxform2,NULL);
2387 static RGBA col_black = {255,0,0,0};
2389 static void drawoutline(gfxdevice_t*dev, gfxline_t*line)
2391 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
2393 int myshapeid = getNewID(dev);
2394 i->tag = swf_InsertTag(i->tag,ST_DEFINESHAPE3);
2397 swf_ShapeNew(&shape);
2398 int lsid = swf_ShapeAddLineStyle(shape,1,&col_black);
2400 swf_SetU16(i->tag,myshapeid);
2401 SRECT r = gfxline_getSWFbbox(line);
2402 r = swf_ClipRect(i->pagebbox, r);
2403 swf_SetRect(i->tag,&r);
2404 swf_SetShapeStyles(i->tag,shape);
2405 swf_ShapeCountBits(shape,NULL,NULL);
2406 swf_SetShapeBits(i->tag,shape);
2407 swf_ShapeSetAll(i->tag,shape,UNDEFINED_COORD,UNDEFINED_COORD,lsid,0,0);
2408 drawgfxline(dev, line, 1);
2409 swf_ShapeSetEnd(i->tag);
2410 swf_ShapeFree(shape);
2412 i->tag = swf_InsertTag(i->tag,ST_PLACEOBJECT2);
2413 swf_ObjectPlace(i->tag, myshapeid, getNewDepth(dev), 0,0,0);
2416 static void swf_startclip(gfxdevice_t*dev, gfxline_t*line)
2418 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
2419 if(i->config_noclips)
2425 if(i->clippos >= 127)
2427 msg("<warning> Too many clip levels.");
2431 if(i->config_showclipshapes)
2432 drawoutline(dev, line);
2434 int myshapeid = getNewID(dev);
2435 i->tag = swf_InsertTag(i->tag,ST_DEFINESHAPE3);
2437 memset(&col, 0, sizeof(RGBA));
2440 swf_ShapeNew(&shape);
2441 int fsid = swf_ShapeAddSolidFillStyle(shape,&col);
2443 RGBA markcol = {0,i->mark[0],i->mark[1],i->mark[2]};
2444 swf_ShapeAddSolidFillStyle(shape,&markcol);
2446 swf_SetU16(i->tag,myshapeid);
2447 SRECT r = gfxline_getSWFbbox(line);
2448 r = swf_ClipRect(i->pagebbox, r);
2449 swf_SetRect(i->tag,&r);
2450 swf_SetShapeStyles(i->tag,shape);
2451 swf_ShapeCountBits(shape,NULL,NULL);
2452 swf_SetShapeBits(i->tag,shape);
2453 swf_ShapeSetAll(i->tag,shape,UNDEFINED_COORD,UNDEFINED_COORD,0,fsid,0);
2454 i->swflastx = i->swflasty = UNDEFINED_COORD;
2455 i->shapeisempty = 1;
2456 drawgfxline(dev, line, 1);
2457 if(i->shapeisempty) {
2458 /* an empty clip shape is equivalent to a shape with no area */
2459 int x = line?line->x:0;
2460 int y = line?line->y:0;
2461 moveto(dev, i->tag, x,y);
2462 lineto(dev, i->tag, x,y);
2463 lineto(dev, i->tag, x,y);
2465 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)) {
2466 if(i->config_watermark) {
2467 gfxbbox_t r; r.xmin = r.ymin = 0;r.xmax = i->max_x;r.ymax = i->max_y;
2468 draw_watermark(dev, r, 1);
2471 swf_ShapeSetEnd(i->tag);
2472 swf_ShapeFree(shape);
2474 /* TODO: remember the bbox, and check all shapes against it */
2476 msg("<trace> Placing clip ID %d", myshapeid);
2477 i->tag = swf_InsertTag(i->tag,ST_PLACEOBJECT2);
2478 i->cliptags[i->clippos] = i->tag;
2479 i->clipshapes[i->clippos] = myshapeid;
2480 i->clipdepths[i->clippos] = getNewDepth(dev);
2484 static void swf_endclip(gfxdevice_t*dev)
2486 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
2487 if(i->config_noclips)
2495 msg("<error> Invalid end of clipping region");
2499 /*swf_ObjectPlaceClip(i->cliptags[i->clippos],i->clipshapes[i->clippos],i->clipdepths[i->clippos],&i->page_matrix,NULL,NULL,
2500 / * clip to depth: * / i->depth <= i->clipdepths[i->clippos]? i->depth : i->depth - 1);
2502 swf_ObjectPlaceClip(i->cliptags[i->clippos],i->clipshapes[i->clippos],i->clipdepths[i->clippos],&i->page_matrix,NULL,NULL,i->depth);
2504 static int gfxline_type(gfxline_t*line)
2510 int haszerosegments=0;
2513 if(line->type == gfx_moveTo) {
2516 } else if(line->type == gfx_lineTo) {
2520 } else if(line->type == gfx_splineTo) {
2522 if(tmpsplines>lines)
2530 if(lines==0 && splines==0) return 0;
2531 else if(lines==1 && splines==0) return 1;
2532 else if(lines==0 && splines==1) return 2;
2533 else if(splines==0) return 3;
2537 static int gfxline_has_dots(gfxline_t*line)
2545 if(line->type == gfx_moveTo) {
2546 /* test the length of the preceding line, and assume it is a dot if
2547 it's length is less than 1.0. But *only* if there's a noticable
2548 gap between the previous line and the next moveTo. (I've come
2549 across a PDF where thousands of "dots" were stringed together,
2551 int last_short_gap = short_gap;
2552 if((fabs(line->x - x) + fabs(line->y - y)) < 1.0) {
2557 if(isline && dist < 1 && !short_gap && !last_short_gap) {
2562 } else if(line->type == gfx_lineTo) {
2563 dist += fabs(line->x - x) + fabs(line->y - y);
2565 } else if(line->type == gfx_splineTo) {
2566 dist += fabs(line->sx - x) + fabs(line->sy - y) +
2567 fabs(line->x - line->sx) + fabs(line->y - line->sy);
2574 if(isline && dist < 1 && !short_gap) {
2580 static int gfxline_fix_short_edges(gfxline_t*line)
2584 if(line->type == gfx_lineTo) {
2585 if(fabs(line->x - x) + fabs(line->y - y) < 0.01) {
2588 } else if(line->type == gfx_splineTo) {
2589 if(fabs(line->sx - x) + fabs(line->sy - y) +
2590 fabs(line->x - line->sx) + fabs(line->y - line->sy) < 0.01) {
2601 static char is_inside_page(gfxdevice_t*dev, gfxcoord_t x, gfxcoord_t y)
2603 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
2604 if(x<i->min_x || x>i->max_x) return 0;
2605 if(y<i->min_y || y>i->max_y) return 0;
2609 gfxline_t* gfxline_move(gfxline_t*line, double x, double y)
2611 gfxline_t*l = line = gfxline_clone(line);
2623 //#define NORMALIZE_POLYGON_POSITIONS
2625 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)
2627 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
2628 if(line_is_empty(line))
2630 int type = gfxline_type(line);
2631 int has_dots = gfxline_has_dots(line);
2632 gfxbbox_t r = gfxline_getbbox(line);
2633 int is_outside_page = !is_inside_page(dev, r.xmin, r.ymin) || !is_inside_page(dev, r.xmax, r.ymax);
2635 /* TODO: * split line into segments, and perform this check for all segments */
2637 if(i->config_disable_polygon_conversion || /*type>=5 ||*/
2639 (width <= i->config_caplinewidth
2640 || (cap_style == gfx_capRound && joint_style == gfx_joinRound)
2641 || (cap_style == gfx_capRound && type<=2))))
2645 /* convert line to polygon */
2646 msg("<trace> draw as polygon, type=%d dots=%d", type, has_dots);
2648 gfxline_fix_short_edges(line);
2649 /* we need to convert the line into a polygon */
2650 gfxpoly_t* poly = gfxpoly_from_stroke(line, width, cap_style, joint_style, miterLimit, DEFAULT_GRID);
2651 gfxline_t*gfxline = gfxline_from_gfxpoly(poly);
2652 dev->fill(dev, gfxline, color);
2653 gfxline_free(gfxline);
2654 gfxpoly_destroy(poly);
2658 msg("<trace> draw as stroke, type=%d dots=%d", type, has_dots);
2661 if(i->config_normalize_polygon_positions) {
2663 double startx = 0, starty = 0;
2664 if(line && line->type == gfx_moveTo) {
2668 line = gfxline_move(line, -startx, -starty);
2669 i->shapeposx = (int)(startx*20);
2670 i->shapeposy = (int)(starty*20);
2673 swfoutput_setstrokecolor(dev, color->r, color->g, color->b, color->a);
2674 swfoutput_setlinewidth(dev, width);
2677 drawgfxline(dev, line, 0);
2679 if(i->config_normalize_polygon_positions) {
2680 free(line); //account for _move
2685 static void swf_fill(gfxdevice_t*dev, gfxline_t*line, gfxcolor_t*color)
2687 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
2688 if(line_is_empty(line))
2692 gfxbbox_t r = gfxline_getbbox(line);
2693 int is_outside_page = !is_inside_page(dev, r.xmin, r.ymin) || !is_inside_page(dev, r.xmax, r.ymax);
2697 if(!i->config_ignoredraworder)
2700 if(i->config_normalize_polygon_positions) {
2702 double startx = 0, starty = 0;
2703 if(line && line->type == gfx_moveTo) {
2707 line = gfxline_move(line, -startx, -starty);
2708 i->shapeposx = (int)(startx*20);
2709 i->shapeposy = (int)(starty*20);
2712 swfoutput_setfillcolor(dev, color->r, color->g, color->b, color->a);
2715 drawgfxline(dev, line, 1);
2717 if(i->currentswfid==2 && r.xmin==0 && r.ymin==0 && r.xmax==i->max_x && r.ymax==i->max_y) {
2718 if(i->config_watermark) {
2719 draw_watermark(dev, r, 1);
2723 msg("<trace> end of swf_fill (shapeid=%d)", i->shapeid);
2725 if(i->config_normalize_polygon_positions) {
2726 free(line); //account for _move
2730 static GRADIENT* gfxgradient_to_GRADIENT(gfxgradient_t*gradient)
2733 gfxgradient_t*g = gradient;
2738 GRADIENT* swfgradient = malloc(sizeof(GRADIENT));
2739 swfgradient->num = num;
2740 swfgradient->rgba = malloc(sizeof(swfgradient->rgba[0])*num);
2741 swfgradient->ratios = malloc(sizeof(swfgradient->ratios[0])*num);
2746 swfgradient->ratios[num] = g->pos*255;
2747 swfgradient->rgba[num] = *(RGBA*)&g->color;
2754 static void swf_fillgradient(gfxdevice_t*dev, gfxline_t*line, gfxgradient_t*gradient, gfxgradienttype_t type, gfxmatrix_t*matrix)
2756 if(line_is_empty(line))
2758 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
2760 if(line_is_empty(line))
2763 GRADIENT* swfgradient = gfxgradient_to_GRADIENT(gradient);
2770 double f = type==gfxgradient_radial?4:4;
2772 m.sx = (int)(matrix->m00*20*f); m.r1 = (int)(matrix->m10*20*f);
2773 m.r0 = (int)(matrix->m01*20*f); m.sy = (int)(matrix->m11*20*f);
2774 m.tx = (int)(matrix->tx*20);
2775 m.ty = (int)(matrix->ty*20);
2778 int myshapeid = getNewID(dev);
2779 i->tag = swf_InsertTag(i->tag, ST_DEFINESHAPE2);
2781 swf_ShapeNew(&shape);
2782 int fsid = swf_ShapeAddGradientFillStyle(shape,&m,swfgradient,type==gfxgradient_radial);
2783 swf_SetU16(i->tag, myshapeid);
2784 SRECT r = gfxline_getSWFbbox(line);
2785 r = swf_ClipRect(i->pagebbox, r);
2786 swf_SetRect(i->tag,&r);
2787 swf_SetShapeStyles(i->tag,shape);
2788 swf_ShapeCountBits(shape,NULL,NULL);
2789 swf_SetShapeBits(i->tag,shape);
2790 swf_ShapeSetAll(i->tag,shape,UNDEFINED_COORD,UNDEFINED_COORD,0,fsid,0);
2791 i->swflastx = i->swflasty = UNDEFINED_COORD;
2792 drawgfxline(dev, line, 1);
2793 swf_ShapeSetEnd(i->tag);
2794 swf_ShapeFree(shape);
2796 int depth = getNewDepth(dev);
2797 msg("<trace> Placing gradient, shape ID %d, depth %d", myshapeid, depth);
2798 i->tag = swf_InsertTag(i->tag,ST_PLACEOBJECT2);
2799 swf_ObjectPlace(i->tag,myshapeid,depth,&i->page_matrix,NULL,NULL);
2801 swf_FreeGradient(swfgradient);free(swfgradient);
2804 static SWFFONT* gfxfont_to_swffont(gfxfont_t*font, const char* id, int version)
2806 SWFFONT*swffont = (SWFFONT*)rfx_calloc(sizeof(SWFFONT));
2808 SRECT bounds = {0,0,0,0};
2810 swffont->version = version;
2811 swffont->name = (U8*)strdup(id);
2812 swffont->layout = (SWFLAYOUT*)rfx_calloc(sizeof(SWFLAYOUT));
2813 swffont->layout->ascent = 0;
2814 swffont->layout->descent = 0;
2815 swffont->layout->leading = 0;
2816 swffont->layout->bounds = (SRECT*)rfx_calloc(sizeof(SRECT)*font->num_glyphs);
2817 swffont->encoding = FONT_ENCODING_UNICODE;
2818 swffont->numchars = font->num_glyphs;
2819 swffont->maxascii = font->max_unicode;
2820 swffont->ascii2glyph = (int*)rfx_calloc(sizeof(int)*swffont->maxascii);
2821 swffont->glyph2ascii = (U16*)rfx_calloc(sizeof(U16)*swffont->numchars);
2822 swffont->glyph = (SWFGLYPH*)rfx_calloc(sizeof(SWFGLYPH)*swffont->numchars);
2823 swffont->glyphnames = (char**)rfx_calloc(sizeof(char*)*swffont->numchars);
2824 for(t=0;t<font->max_unicode;t++) {
2825 swffont->ascii2glyph[t] = font->unicode2glyph[t];
2827 SRECT max = {0,0,0,0};
2828 for(t=0;t<font->num_glyphs;t++) {
2832 swffont->glyph2ascii[t] = font->glyphs[t].unicode;
2833 if(swffont->glyph2ascii[t] == 0xffff || swffont->glyph2ascii[t] == 0x0000) {
2834 /* flash 8 flashtype requires unique unicode IDs for each character.
2835 We use the Unicode private user area to assign characters, hoping that
2836 the font doesn't contain more than 2048 glyphs */
2837 swffont->glyph2ascii[t] = 0xe000 + (t&0x1fff);
2840 if(font->glyphs[t].name) {
2841 swffont->glyphnames[t] = strdup(font->glyphs[t].name);
2843 swffont->glyphnames[t] = 0;
2845 advance = font->glyphs[t].advance;
2847 swf_Shape01DrawerInit(&draw, 0);
2848 line = font->glyphs[t].line;
2850 const double scale = GLYPH_SCALE;
2853 c.x = line->sx * scale; c.y = -line->sy * scale;
2854 //to.x = floor(line->x * scale); to.y = floor(-line->y * scale);
2855 to.x = line->x * scale; to.y = -line->y * scale;
2856 if(line->type == gfx_moveTo) {
2857 draw.moveTo(&draw, &to);
2858 } else if(line->type == gfx_lineTo) {
2859 draw.lineTo(&draw, &to);
2860 } else if(line->type == gfx_splineTo) {
2861 draw.splineTo(&draw, &c, &to);
2866 swffont->glyph[t].shape = swf_ShapeDrawerToShape(&draw);
2868 SRECT bbox = swf_ShapeDrawerGetBBox(&draw);
2869 swf_ExpandRect2(&max, &bbox);
2871 swffont->layout->bounds[t] = bbox;
2873 if(advance<32768.0/20) {
2874 swffont->glyph[t].advance = (int)(advance*20);
2876 //msg("<warning> Advance value overflow in glyph %d", t);
2877 swffont->glyph[t].advance = 32767;
2880 draw.dealloc(&draw);
2882 swf_ExpandRect2(&bounds, &swffont->layout->bounds[t]);
2884 for(t=0;t<font->num_glyphs;t++) {
2885 SRECT bbox = swffont->layout->bounds[t];
2887 /* if the glyph doesn't have a bounding box, use the
2888 combined bounding box (necessary e.g. for space characters) */
2889 if(!(bbox.xmin|bbox.ymin|bbox.xmax|bbox.ymax)) {
2890 swffont->layout->bounds[t] = bbox = max;
2893 /* check that the advance value is reasonable, by comparing it
2894 with the bounding box */
2895 if(bbox.xmax>0 && (bbox.xmax*10 < swffont->glyph[t].advance || !swffont->glyph[t].advance)) {
2896 if(swffont->glyph[t].advance)
2897 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);
2898 swffont->glyph[t].advance = bbox.xmax;
2900 //swffont->glyph[t].advance = bbox.xmax - bbox.xmin;
2904 /* Flash player will use the advance value from the char, and the ascent/descent values
2905 from the layout for text selection.
2906 ascent will extend the char into negative y direction, from the baseline, while descent
2907 will extend in positive y direction, also from the baseline.
2908 The baseline is defined as the y-position zero
2911 swffont->layout->ascent = bounds.ymin<0?-bounds.ymin:0;
2912 swffont->layout->descent = bounds.ymax>0?bounds.ymax:0;
2913 swffont->layout->leading = bounds.ymax - bounds.ymin;
2915 /* if the font has proper ascent/descent values (>0) and those define
2916 greater line spacing that what we estimated from the bounding boxes,
2917 use the font's parameters */
2918 if(font->ascent*20 > swffont->layout->ascent)
2919 swffont->layout->ascent = font->ascent*20;
2920 if(font->descent*20 > swffont->layout->descent)
2921 swffont->layout->descent = font->descent*20;
2926 static void swf_addfont(gfxdevice_t*dev, gfxfont_t*font)
2928 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
2930 if(i->swffont && i->swffont->name && !strcmp((char*)i->swffont->name,font->id))
2931 return; // the requested font is the current font
2933 fontlist_t*last=0,*l = i->fontlist;
2936 if(!strcmp((char*)l->swffont->name, font->id)) {
2937 return; // we already know this font
2941 l = (fontlist_t*)rfx_calloc(sizeof(fontlist_t));
2942 l->swffont = gfxfont_to_swffont(font, font->id, (i->config_flashversion>=8 && !NO_FONT3)?3:2);
2949 swf_FontSetID(l->swffont, getNewID(i->dev));
2951 if(getScreenLogLevel() >= LOGLEVEL_DEBUG) {
2953 // print font information
2954 msg("<debug> Font %s",font->id);
2955 msg("<debug> | ID: %d", l->swffont->id);
2956 msg("<debug> | Version: %d", l->swffont->version);
2957 msg("<debug> | Name: %s", l->swffont->name);
2958 msg("<debug> | Numchars: %d", l->swffont->numchars);
2959 msg("<debug> | Maxascii: %d", l->swffont->maxascii);
2960 msg("<debug> | Style: %d", l->swffont->style);
2961 msg("<debug> | Encoding: %d", l->swffont->encoding);
2962 for(iii=0; iii<l->swffont->numchars;iii++) {
2963 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,
2964 l->swffont->layout->bounds[iii].xmin/20.0,
2965 l->swffont->layout->bounds[iii].ymin/20.0,
2966 l->swffont->layout->bounds[iii].xmax/20.0,
2967 l->swffont->layout->bounds[iii].ymax/20.0
2970 for(t=0;t<l->swffont->maxascii;t++) {
2971 if(l->swffont->ascii2glyph[t] == iii)
2972 msg("<debug> | - maps to %d",t);
2978 static void swf_switchfont(gfxdevice_t*dev, const char*fontid)
2980 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
2982 if(i->swffont && i->swffont->name && !strcmp((char*)i->swffont->name,fontid))
2983 return; // the requested font is the current font
2985 fontlist_t*l = i->fontlist;
2987 if(!strcmp((char*)l->swffont->name, fontid)) {
2988 i->swffont = l->swffont;
2993 msg("<error> Unknown font id: %s", fontid);
2997 /* sets the matrix which is to be applied to characters drawn by swfoutput_drawchar() */
2998 static void setfontscale(gfxdevice_t*dev,double m11,double m12, double m21,double m22,double x, double y, char force)
3004 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
3005 if(i->lastfontm11 == m11 &&
3006 i->lastfontm12 == m12 &&
3007 i->lastfontm21 == m21 &&
3008 i->lastfontm22 == m22 && !force)
3013 i->lastfontm11 = m11;
3014 i->lastfontm12 = m12;
3015 i->lastfontm21 = m21;
3016 i->lastfontm22 = m22;
3018 double xsize = sqrt(m11*m11 + m12*m12);
3019 double ysize = sqrt(m21*m21 + m22*m22);
3022 if(i->config_flashversion>=8 && !NO_FONT3)
3025 i->current_font_size = (xsize>ysize?xsize:ysize)*extrazoom;
3026 if(i->current_font_size < 1)
3027 i->current_font_size = 1;
3030 swf_GetMatrix(0, &m);
3032 if(m21 || m12 || fabs(m11+m22)>0.001) {
3033 double ifs = (double)extrazoom/(i->current_font_size);
3034 m.sx = (S32)((m11*ifs)*65536); m.r1 = -(S32)((m21*ifs)*65536);
3035 m.r0 = (S32)((m12*ifs)*65536); m.sy = -(S32)((m22*ifs)*65536);
3038 /* this is the position of the first char to set a new fontmatrix-
3039 we hope that it's close enough to all other characters using the
3040 font, so we use its position as origin for the matrix */
3047 static void swf_drawchar(gfxdevice_t*dev, gfxfont_t*font, int glyph, gfxcolor_t*color, gfxmatrix_t*matrix)
3049 swfoutput_internal*i = (swfoutput_internal*)dev->internal;
3051 msg("<error> swf_drawchar called (glyph %d) without font", glyph);
3055 if(i->config_drawonlyshapes) {
3056 gfxglyph_t*g = &font->glyphs[glyph];
3057 gfxline_t*line2 = gfxline_clone(g->line);
3058 gfxline_transform(line2, matrix);
3059 dev->fill(dev, line2, color);
3060 gfxline_free(line2);
3064 if(!i->swffont || !i->swffont->name || strcmp((char*)i->swffont->name,font->id)) // not equal to current font
3066 swf_switchfont(dev, font->id); // set the current font
3070 msg("<warning> swf_drawchar: Font is NULL");
3073 if(glyph<0 || glyph>=i->swffont->numchars) {
3074 msg("<warning> No character %d in font %s (%d chars)", glyph, FIXNULL((char*)i->swffont->name), i->swffont->numchars);
3078 setfontscale(dev, matrix->m00, matrix->m01, matrix->m10, matrix->m11, matrix->tx, matrix->ty, 0);
3080 double det = i->fontmatrix.sx/65536.0 * i->fontmatrix.sy/65536.0 -
3081 i->fontmatrix.r0/65536.0 * i->fontmatrix.r1/65536.0;
3082 if(fabs(det) < 0.0005) {
3083 /* x direction equals y direction- the text is invisible */
3084 msg("<verbose> Not drawing invisible character %d (det=%f, m=[%f %f;%f %f]\n", glyph,
3086 i->fontmatrix.sx/65536.0, i->fontmatrix.r1/65536.0,
3087 i->fontmatrix.r0/65536.0, i->fontmatrix.sy/65536.0);
3091 /*if(i->swffont->glyph[glyph].shape->bitlen <= 16) {
3092 msg("<warning> Glyph %d in current charset (%s, %d characters) is empty",
3093 glyph, FIXNULL((char*)i->swffont->name), i->swffont->numchars);
3097 /* calculate character position with respect to the current font matrix */
3098 double s = 20 * GLYPH_SCALE / det;
3099 double px = matrix->tx - i->fontmatrix.tx/20.0;
3100 double py = matrix->ty - i->fontmatrix.ty/20.0;
3101 int x = (SCOORD)(( px * i->fontmatrix.sy/65536.0 - py * i->fontmatrix.r1/65536.0)*s);
3102 int y = (SCOORD)((- px * i->fontmatrix.r0/65536.0 + py * i->fontmatrix.sx/65536.0)*s);
3103 if(x>32767 || x<-32768 || y>32767 || y<-32768) {
3104 msg("<verbose> Moving character origin to %f %f\n", matrix->tx, matrix->ty);
3106 setfontscale(dev, matrix->m00, matrix->m01, matrix->m10, matrix->m11, matrix->tx, matrix->ty, 1);
3107 /* since we just moved the char origin to the current char's position,
3108 it now has the relative position (0,0) */
3117 msg("<trace> Drawing char %d in font %d at %d,%d in color %02x%02x%02x%02x",
3118 glyph, i->swffont->id, x, y, color->r, color->g, color->b, color->a);
3120 if(color->a == 0 && i->config_invisibletexttofront) {
3121 RGBA color2 = *(RGBA*)color;
3122 if(i->config_flashversion>=8) {
3123 // use "multiply" blend mode
3124 color2.a = color2.r = color2.g = color2.b = 255;
3126 i->topchardata = charbuffer_append(i->topchardata, i->swffont, glyph, x, y, i->current_font_size, color2, &i->fontmatrix);
3128 i->chardata = charbuffer_append(i->chardata, i->swffont, glyph, x, y, i->current_font_size, *(RGBA*)color, &i->fontmatrix);
3130 swf_FontUseGlyph(i->swffont, glyph, i->current_font_size);