1 /* gfxdevice_swf.c
2 
3    Part of the swftools package.
4 
5    Copyright (c) 2001,2002,2003,2004,2005 Matthias Kramm <kramm@quiss.org>
6 
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.
11 
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.
16 
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 */
20 
21 #include <stdlib.h>
22 #include <stdio.h>
23 #include <string.h>
24 #include "../../config.h"
25 #include <fcntl.h>
26 #ifdef HAVE_UNISTD_H
27 #include <unistd.h>
28 #endif
29 #ifdef HAVE_IO_H
30 #include <io.h>
31 #endif
32 #ifdef HAVE_ASSERT_H
33 #include <assert.h>
34 #else
35 #define assert(a)
36 #endif
37 #include <math.h>
38 #include "../mem.h"
39 #include "../log.h"
40 #include "../rfxswf.h"
41 #include "../gfxdevice.h"
42 #include "../gfxtools.h"
43 #include "swf.h"
44 #include "../gfxpoly.h"
45 #include "../gfximage.h"
46 
47 #define CHARDATAMAX 1024
48 #define CHARMIDX 0
49 #define CHARMIDY 0
50 
51 typedef struct _charatposition {
52     int charid;
53     SWFFONT*font;
54     int x;
55     int y;
56     int size;
57     RGBA color;
58 } charatposition_t;
59 
60 typedef struct _chararray {
61     charatposition_t chr[CHARDATAMAX+1];
62     int pos;
63     struct _chararray *next;
64 } chararray_t;
65 
66 typedef struct _charbuffer {
67     MATRIX matrix;
68     chararray_t*array;
69     chararray_t*last;
70     struct _charbuffer *next;
71 } charbuffer_t;
72 
73 typedef struct _fontlist
74 {
75     SWFFONT *swffont;
76     struct _fontlist*next;
77 } fontlist_t;
78 
79 typedef long int twip;
80 
81 typedef struct _swfmatrix {
82     double m11,m12,m21,m22,m31,m32;
83 } swfmatrix_t;
84 
85 typedef struct _swfoutput_internal
86 {
87     gfxdevice_t*dev; // the gfxdevice object where this internal struct resides
88 
89     double config_dumpfonts;
90     double config_ppmsubpixels;
91     double config_jpegsubpixels;
92     char hasbuttons;
93     int config_invisibletexttofront;
94     int config_dots;
95     int config_simpleviewer;
96     int config_opennewwindow;
97     int config_ignoredraworder;
98     int config_drawonlyshapes;
99     int config_frameresets;
100     int config_linknameurl;
101     int config_jpegquality;
102     int config_storeallcharacters;
103     int config_enablezlib;
104     int config_insertstoptag;
105     int config_showimages;
106     int config_watermark;
107     int config_noclips;
108     int config_flashversion;
109     int config_reordertags;
110     int config_showclipshapes;
111     int config_splinemaxerror;
112     int config_fontsplinemaxerror;
113     int config_filloverlap;
114     int config_local_with_network;
115     int config_local_with_filesystem;
116     int config_protect;
117     int config_bboxvars;
118     int config_disable_polygon_conversion;
119     int config_normalize_polygon_positions;
120     int config_alignfonts;
121     double config_override_line_widths;
122     double config_remove_small_polygons;
123     char config_disablelinks;
124     RGBA config_linkcolor;
125     float config_minlinewidth;
126     double config_caplinewidth;
127     char* config_linktarget;
128     char*config_internallinkfunction;
129     char*config_externallinkfunction;
130     char config_animate;
131     double config_framerate;
132 
133     SWF* swf;
134 
135     fontlist_t* fontlist;
136 
137     char storefont;
138 
139     MATRIX page_matrix;
140 
141     TAG *tag;
142     int currentswfid;
143     int startids;
144     int depth;
145     int startdepth;
146     int linewidth;
147 
148     SHAPE* shape;
149     int shapeid;
150     int textmode;
151 
152     int watermarks;
153 
154     int fillstyleid;
155     int linestyleid;
156     int swflastx;
157     int swflasty;
158     int lastwasfill;
159     int shapeisempty;
160     char fill;
161     int min_x,max_x;
162     int min_y,max_y;
163     TAG* cliptags[128];
164     int clipshapes[128];
165     U32 clipdepths[128];
166     int clippos;
167 
168     /* image cache */
169     /*
170     int pic_xids[1024];
171     int pic_yids[1024];
172     int pic_ids[1024];
173     int pic_width[1024];
174     int pic_height[1024];
175     int picpos;
176     */
177 
178     int frameno;
179     int lastframeno;
180 
181     char fillstylechanged;
182 
183     int jpeg; //next image type
184 
185     int bboxrectpos;
186     SRECT bboxrect;
187 
188     SRECT pagebbox;
189 
190     gfxline_t*stored_clipshapes; //for config_showclipshapes
191 
192     charbuffer_t* chardata;
193     charbuffer_t* topchardata; //chars supposed to be above everything else
194 
195     int firstpage;
196     char pagefinished;
197 
198     char overflow;
199 
200     int current_font_size;
201     MATRIX fontmatrix;
202     double lastfontm11,lastfontm12,lastfontm21,lastfontm22;
203     SWFFONT *swffont;
204     RGBA strokergb;
205     RGBA fillrgb;
206     int drawmode;
207 
208     int shapeposx;
209     int shapeposy;
210 
211     char* mark;
212 
213 } swfoutput_internal;
214 
215 static const int NO_FONT3=0;
216 
217 static void swf_fillbitmap(gfxdevice_t*dev, gfxline_t*line, gfximage_t*img, gfxmatrix_t*matrix, gfxcxform_t*cxform);
218 static int  swf_setparameter(gfxdevice_t*driver, const char*key, const char*value);
219 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);
220 static void swf_startclip(gfxdevice_t*dev, gfxline_t*line);
221 static void swf_endclip(gfxdevice_t*dev);
222 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);
223 static void swf_fill(gfxdevice_t*dev, gfxline_t*line, gfxcolor_t*color);
224 static void swf_fillgradient(gfxdevice_t*dev, gfxline_t*line, gfxgradient_t*gradient, gfxgradienttype_t type, gfxmatrix_t*matrix);
225 static void swf_drawchar(gfxdevice_t*dev, gfxfont_t*font, int glyph, gfxcolor_t*color, gfxmatrix_t*matrix);
226 static void swf_addfont(gfxdevice_t*dev, gfxfont_t*font);
227 static void swf_drawlink(gfxdevice_t*dev, gfxline_t*line, const char*action, const char*text);
228 static void swf_startframe(gfxdevice_t*dev, int width, int height);
229 static void swf_endframe(gfxdevice_t*dev);
230 static void swfoutput_namedlink(gfxdevice_t*dev, char*name, gfxline_t*points);
231 static void swfoutput_linktopage(gfxdevice_t*dev, int page, gfxline_t*points);
232 static void swfoutput_linktourl(gfxdevice_t*dev, const char*url, gfxline_t*points);
233 
234 static gfxresult_t* swf_finish(gfxdevice_t*driver);
235 
init_internal_struct()236 static swfoutput_internal* init_internal_struct()
237 {
238     swfoutput_internal*i = (swfoutput_internal*)malloc(sizeof(swfoutput_internal));
239     memset(i, 0, sizeof(swfoutput_internal));
240 
241     i->storefont = 0;
242     i->currentswfid = 0;
243     i->depth = 0;
244     i->overflow = 0;
245     i->startdepth = 0;
246     i->linewidth = 0;
247     i->shapeid = -1;
248     i->textmode = 0;
249     i->frameno = 0;
250     i->lastframeno = 0;
251 
252     i->mark = 0;
253 
254     i->fillstyleid;
255     i->linestyleid;
256     i->swflastx=0;
257     i->swflasty=0;
258     i->lastwasfill = 0;
259     i->shapeisempty = 1;
260     i->fill = 0;
261     i->clippos = 0;
262 
263     i->fillstylechanged = 0;
264 
265     i->bboxrectpos = -1;
266     i->chardata = 0;
267     i->firstpage = 1;
268     i->pagefinished = 1;
269 
270     i->config_disablelinks=0;
271     i->config_dumpfonts=0;
272     i->config_ppmsubpixels=0;
273     i->config_jpegsubpixels=0;
274     i->config_opennewwindow=1;
275     i->config_ignoredraworder=0;
276     i->config_drawonlyshapes=0;
277     i->config_jpegquality=85;
278     i->config_storeallcharacters=0;
279     i->config_dots=1;
280     i->config_enablezlib=0;
281     i->config_insertstoptag=0;
282     i->config_flashversion=6;
283     i->config_framerate=0.25;
284     i->config_splinemaxerror=1;
285     i->config_fontsplinemaxerror=1;
286     i->config_filloverlap=0;
287     i->config_local_with_network=0;
288     i->config_local_with_filesystem=0;
289     i->config_protect=0;
290     i->config_bboxvars=0;
291     i->config_override_line_widths=0;
292     i->config_showclipshapes=0;
293     i->config_minlinewidth=0.05;
294     i->config_caplinewidth=1;
295     i->config_linktarget=0;
296     i->config_internallinkfunction=0;
297     i->config_externallinkfunction=0;
298     i->config_reordertags=1;
299     i->config_linknameurl=0;
300 
301     i->config_linkcolor.r = i->config_linkcolor.g = i->config_linkcolor.b = 255;
302     i->config_linkcolor.a = 0x40;
303 
304     return i;
305 };
306 
307 static int id_error = 0;
308 
getNewID(gfxdevice_t * dev)309 static U16 getNewID(gfxdevice_t* dev)
310 {
311     swfoutput_internal*i = (swfoutput_internal*)dev->internal;
312     if(i->currentswfid == 65535) {
313 	if(!id_error) {
314 	    msg("<error> ID Table overflow");
315 	    msg("<error> This file is too complex to render- SWF only supports 65536 shapes at once");
316 	}
317 	id_error=1;
318 	i->overflow = 1;
319 	exit(1);
320     }
321     return ++i->currentswfid;
322 }
getNewDepth(gfxdevice_t * dev)323 static U16 getNewDepth(gfxdevice_t* dev)
324 {
325     swfoutput_internal*i = (swfoutput_internal*)dev->internal;
326     if(i->depth == 65520) {
327 	if(!id_error) {
328 	    msg("<error> Depth Table overflow");
329 	    msg("<error> This file is too complex to render- SWF only supports 65536 shapes at once");
330 	}
331 	id_error=1;
332 	i->overflow = 1;
333 	exit(1);
334     }
335     return ++i->depth;
336 }
337 
338 static void startshape(gfxdevice_t* dev);
339 static void starttext(gfxdevice_t* dev);
340 static void endshape(gfxdevice_t* dev);
341 static void endtext(gfxdevice_t* dev);
342 
343 typedef struct _plotxy
344 {
345     double x,y;
346 } plotxy_t;
347 
twipsnap(double f)348 static inline int twipsnap(double f)
349 {
350     /* if(f < -0x40000000/20.0) {
351 	fprintf(stderr, "Warning: Coordinate underflow (%f)\n", f);
352 	f = -0x40000000/20.0;
353     } else if(f>0x3fffffff/20.0) {
354 	fprintf(stderr, "Warning: Coordinate overflow (%f)\n", f);
355 	f = 0x3fffffff/20.0;
356     }*/
357 
358     /* clamp coordinates to a rectangle with the property that we
359        can represent a line from the upper left corner to the upper
360        right corner using no more than 64 strokes */
361     const double min = -(1<<(18+4))/20.0;
362     const double max = ((1<<(18+4))-1)/20.0;
363     if(f < min) {
364 	fprintf(stderr, "Warning: Coordinate underflow (%f)\n", f);
365 	f = min;
366     } else if(f>max) {
367 	fprintf(stderr, "Warning: Coordinate overflow (%f)\n", f);
368 	f = max;
369     }
370 
371     return (int)(f*20);
372 }
373 
374 // write a move-to command into the swf
movetoxy(gfxdevice_t * dev,TAG * tag,plotxy_t p0)375 static int movetoxy(gfxdevice_t*dev, TAG*tag, plotxy_t p0)
376 {
377     swfoutput_internal*i = (swfoutput_internal*)dev->internal;
378     int rx = twipsnap(p0.x);
379     int ry = twipsnap(p0.y);
380     if(rx!=i->swflastx || ry!=i->swflasty || i->fillstylechanged) {
381       swf_ShapeSetMove (tag, i->shape, rx,ry);
382       i->fillstylechanged = 0;
383       i->swflastx=rx;
384       i->swflasty=ry;
385       return 1;
386     }
387     return 0;
388 }
moveto(gfxdevice_t * dev,TAG * tag,double x,double y)389 static int moveto(gfxdevice_t*dev, TAG*tag, double  x, double y)
390 {
391     swfoutput_internal*i = (swfoutput_internal*)dev->internal;
392     plotxy_t p;
393     p.x = x;
394     p.y = y;
395     return movetoxy(dev, tag, p);
396 }
addPointToBBox(gfxdevice_t * dev,int px,int py)397 static void addPointToBBox(gfxdevice_t*dev, int px, int py)
398 {
399     swfoutput_internal*i = (swfoutput_internal*)dev->internal;
400 
401     SPOINT p;
402     p.x = px;
403     p.y = py;
404     if(i->fill) {
405 	swf_ExpandRect(&i->bboxrect, p);
406     } else {
407 	swf_ExpandRect3(&i->bboxrect, p, i->linewidth*3/2);
408     }
409 }
410 
411 /*static void plot(gfxdevice_t*dev, int x, int y, TAG*tag)
412 {
413     swfoutput_internal*i = (swfoutput_internal*)dev->internal;
414     int width = i->linewidth/4;
415     if(width > 5)
416 	width = 5;
417     ////square
418     //swf_ShapeSetLine(tag, i->shape,-width,-width);
419     //swf_ShapeSetLine(tag, i->shape,width*2,0);
420     //swf_ShapeSetLine(tag, i->shape,0,width*2);
421     //swf_ShapeSetLine(tag, i->shape,-width*2,0);
422     //swf_ShapeSetLine(tag, i->shape,0,-width*2);
423     //swf_ShapeSetLine(tag, i->shape,width,width);
424 
425     // diamond
426     swf_ShapeSetLine(tag, i->shape,-width,0);
427     swf_ShapeSetLine(tag, i->shape,width,-width);
428     swf_ShapeSetLine(tag, i->shape,width,width);
429     swf_ShapeSetLine(tag, i->shape,-width,width);
430     swf_ShapeSetLine(tag, i->shape,-width,-width);
431     swf_ShapeSetLine(tag, i->shape,width,0);
432 
433     addPointToBBox(dev, x-width ,y-width);
434     addPointToBBox(dev, x+width ,y+width);
435 }*/
436 
437 // write a line-to command into the swf
linetoxy(gfxdevice_t * dev,TAG * tag,plotxy_t p0)438 static void linetoxy(gfxdevice_t*dev, TAG*tag, plotxy_t p0)
439 {
440     swfoutput_internal*i = (swfoutput_internal*)dev->internal;
441     int px = twipsnap(p0.x);
442     int py = twipsnap(p0.y);
443     int rx = (px-i->swflastx);
444     int ry = (py-i->swflasty);
445     if(rx|ry) {
446 	swf_ShapeSetLine (tag, i->shape, rx,ry);
447 	addPointToBBox(dev, i->swflastx,i->swflasty);
448 	addPointToBBox(dev, px,py);
449     } /* this is a nice idea, but doesn't work with current flash
450          players (the pixel will be invisible if they're not
451          precisely on a pixel boundary)
452          Besides, we should only do this if this lineto itself
453          is again followed by a "move".
454          else if(!i->fill && i->config_dots) {
455        // treat lines of length 0 as plots, making them
456        // at least 1 twip wide so Flash will display them
457 	//plot(dev, i->swflastx, i->swflasty, tag);
458 	swf_ShapeSetLine (tag, i->shape, rx+1,ry);
459     }*/
460 
461     i->shapeisempty = 0;
462     i->swflastx+=rx;
463     i->swflasty+=ry;
464 }
lineto(gfxdevice_t * dev,TAG * tag,double x,double y)465 static void lineto(gfxdevice_t*dev, TAG*tag, double x, double y)
466 {
467     plotxy_t p;
468     p.x = x;
469     p.y = y;
470     linetoxy(dev,tag, p);
471 }
472 
473 // write a spline-to command into the swf
splineto(gfxdevice_t * dev,TAG * tag,plotxy_t control,plotxy_t end)474 static void splineto(gfxdevice_t*dev, TAG*tag, plotxy_t control,plotxy_t end)
475 {
476     swfoutput_internal*i = (swfoutput_internal*)dev->internal;
477     int lastlastx = i->swflastx;
478     int lastlasty = i->swflasty;
479 
480     int cx = (twipsnap(control.x)-i->swflastx);
481     int cy = (twipsnap(control.y)-i->swflasty);
482     i->swflastx += cx;
483     i->swflasty += cy;
484     int ex = (twipsnap(end.x)-i->swflastx);
485     int ey = (twipsnap(end.y)-i->swflasty);
486     i->swflastx += ex;
487     i->swflasty += ey;
488 
489     if((cx || cy) && (ex || ey)) {
490         swf_ShapeSetCurve(tag, i->shape, cx,cy,ex,ey);
491         addPointToBBox(dev, lastlastx   ,lastlasty   );
492         addPointToBBox(dev, lastlastx+cx,lastlasty+cy);
493         addPointToBBox(dev, lastlastx+cx+ex,lastlasty+cy+ey);
494     } else if(cx || cy || ex || ey) {
495         swf_ShapeSetLine(tag, i->shape, cx+ex,cy+ey);
496         addPointToBBox(dev, lastlastx   ,lastlasty   );
497         addPointToBBox(dev, lastlastx+cx,lastlasty+cy);
498         addPointToBBox(dev, lastlastx+cx+ex,lastlasty+cy+ey);
499     }
500 
501     i->shapeisempty = 0;
502 }
503 
504 /* write a line, given two points and the transformation
505    matrix. */
506 /*static void line(gfxdevice_t*dev, TAG*tag, plotxy_t p0, plotxy_t p1)
507 {
508     moveto(dev, tag, p0);
509     lineto(dev, tag, p1);
510 }*/
511 
resetdrawer(gfxdevice_t * dev)512 void resetdrawer(gfxdevice_t*dev)
513 {
514     swfoutput_internal*i = (swfoutput_internal*)dev->internal;
515     i->swflastx = 0;
516     i->swflasty = 0;
517 }
518 
stopFill(gfxdevice_t * dev)519 static void stopFill(gfxdevice_t*dev)
520 {
521     swfoutput_internal*i = (swfoutput_internal*)dev->internal;
522     if(i->lastwasfill!=0)
523     {
524 	swf_ShapeSetStyle(i->tag,i->shape,i->linestyleid,0x8000,0);
525 	i->fillstylechanged = 1;
526 	i->lastwasfill = 0;
527     }
528 }
startFill(gfxdevice_t * dev)529 static void startFill(gfxdevice_t*dev)
530 {
531     swfoutput_internal*i = (swfoutput_internal*)dev->internal;
532     if(i->lastwasfill!=1)
533     {
534 	swf_ShapeSetStyle(i->tag,i->shape,0x8000,i->fillstyleid,0);
535 	i->fillstylechanged = 1;
536 	i->lastwasfill = 1;
537     }
538 }
539 
colorcompare(RGBA * a,RGBA * b)540 static inline int colorcompare(RGBA*a,RGBA*b)
541 {
542 
543     if(a->r!=b->r ||
544        a->g!=b->g ||
545        a->b!=b->b ||
546        a->a!=b->a) {
547         return 0;
548     }
549     return 1;
550 }
551 
getcharacterbbox(chararray_t * chardata,MATRIX * m,int flashversion)552 static SRECT getcharacterbbox(chararray_t*chardata, MATRIX* m, int flashversion)
553 {
554     SRECT r;
555     char debug = 0;
556     memset(&r, 0, sizeof(r));
557 
558     int t;
559     if(debug) printf("\n");
560 
561     double div = 1.0 / 1024.0;
562     if(flashversion>=8 && !NO_FONT3) {
563 	div = 1.0 / 20480.0;
564     }
565 
566     while(chardata) {
567 	for(t=0;t<chardata->pos;t++) {
568 	    charatposition_t*chr = &chardata->chr[t];
569 	    SRECT b = chr->font->layout->bounds[chr->charid];
570 	    b.xmin = floor((b.xmin*(double)chr->size) *div);
571 	    b.ymin = floor((b.ymin*(double)chr->size) *div);
572 	    b.xmax = ceil((b.xmax*(double)chr->size)  *div);
573 	    b.ymax = ceil((b.ymax*(double)chr->size)  *div);
574 
575 	    b.xmin += chr->x;
576 	    b.ymin += chr->y;
577 	    b.xmax += chr->x;
578 	    b.ymax += chr->y;
579 
580 	    /* until we solve the INTERNAL_SCALING problem (see below)
581 	       make sure the bounding box is big enough */
582 	    b.xmin -= 20;
583 	    b.ymin -= 20;
584 	    b.xmax += 20;
585 	    b.ymax += 20;
586 
587 	    b = swf_TurnRect(b, m);
588 
589 	    if(debug) printf("(%f,%f,%f,%f) -> (%f,%f,%f,%f) [font %d, char %d]\n",
590 		    chr->font->layout->bounds[chr->charid].xmin/20.0,
591 		    chr->font->layout->bounds[chr->charid].ymin/20.0,
592 		    chr->font->layout->bounds[chr->charid].xmax/20.0,
593 		    chr->font->layout->bounds[chr->charid].ymax/20.0,
594 		    b.xmin/20.0,
595 		    b.ymin/20.0,
596 		    b.xmax/20.0,
597 		    b.ymax/20.0,
598 		    chr->font->id,
599 		    chr->charid);
600 	    swf_ExpandRect2(&r, &b);
601 	}
602 	chardata = chardata->next;
603     }
604     if(debug) printf("-----> (%f,%f,%f,%f)\n",
605 	    r.xmin/20.0,
606 	    r.ymin/20.0,
607 	    r.xmax/20.0,
608 	    r.ymax/20.0);
609     return r;
610 }
611 
chararray_reverse(chararray_t * buf)612 static chararray_t*chararray_reverse(chararray_t*buf)
613 {
614     chararray_t*prev = 0;
615     while(buf) {
616 	chararray_t*next = buf->next;
617 	buf->next = prev;
618 	prev = buf;
619 	buf = next;
620     }
621     return prev;
622 }
623 
chararray_writetotag(chararray_t * _chardata,TAG * tag)624 static void chararray_writetotag(chararray_t*_chardata, TAG*tag)
625 {
626     SWFFONT font;
627     RGBA color;
628     color.r = _chardata?_chardata->chr[0].color.r^255:0;
629     color.g = 0;
630     color.b = 0;
631     color.a = 0;
632     SWFFONT*lastfont;
633     int lastx;
634     int lasty;
635     int lastsize;
636     int lastchar;
637     int charids[128];
638     int charadvance[128];
639     int charstorepos;
640     int pass;
641     int glyphbits=1; //TODO: can this be zero?
642     int advancebits=1;
643 
644     if(tag->id != ST_DEFINETEXT &&
645         tag->id != ST_DEFINETEXT2) {
646         msg("<error> internal error: charbuffer_put needs an text tag, not %d\n",tag->id);
647         exit(1);
648     }
649     if(!_chardata) {
650         msg("<warning> charbuffer_put called with zero characters");
651     }
652 
653     for(pass = 0; pass < 2; pass++)
654     {
655         charstorepos = 0;
656         lastfont = 0;
657         lastx = CHARMIDX;
658         lasty = CHARMIDY;
659         lastsize = -1;
660 	lastchar = -1;
661 
662         if(pass==1)
663         {
664             advancebits++; // add sign bit
665             swf_SetU8(tag, glyphbits);
666             swf_SetU8(tag, advancebits);
667         }
668 
669 	chararray_t*chardata = _chardata;
670 
671 	while(chardata) {
672 	    int t;
673 
674 	    assert(!chardata->next || chardata->pos == CHARDATAMAX);
675 	    assert(chardata->pos);
676 
677 	    int to = chardata->next?chardata->pos-1:chardata->pos;
678 
679 	    for(t=0;t<=to;t++)
680 	    {
681 		char islast = t==chardata->pos;
682 
683 		charatposition_t*chr = &chardata->chr[t];
684 
685 		if(lastfont != chr->font ||
686 			lastx!=chr->x ||
687 			lasty!=chr->y ||
688 			!colorcompare(&color, &chardata->chr[t].color) ||
689 			charstorepos==127 ||
690 			lastsize != chardata->chr[t].size ||
691 			islast)
692 		{
693 		    if(charstorepos && pass==0)
694 		    {
695 			int s;
696 			for(s=0;s<charstorepos;s++)
697 			{
698 			    while(charids[s]>=(1<<glyphbits))
699 				glyphbits++;
700 			    while(charadvance[s]>=(1<<advancebits))
701 				advancebits++;
702 			}
703 		    }
704 		    if(charstorepos && pass==1)
705 		    {
706 			tag->writeBit = 0; // Q&D
707 			swf_SetBits(tag, 0, 1); // GLYPH Record
708 			swf_SetBits(tag, charstorepos, 7); // number of glyphs
709 			int s;
710 			for(s=0;s<charstorepos;s++)
711 			{
712 			    swf_SetBits(tag, charids[s], glyphbits);
713 			    swf_SetBits(tag, charadvance[s], advancebits);
714 			}
715 		    }
716 		    charstorepos = 0;
717 
718 		    if(pass == 1 && !islast)
719 		    {
720 			RGBA*newcolor=0;
721 			SWFFONT*newfont=0;
722 			int newx = 0;
723 			int newy = 0;
724 			if(lastx != chr->x ||
725 			   lasty != chr->y)
726 			{
727 			    newx = chr->x;
728 			    newy = chr->y;
729 			    if(newx == 0)
730 				newx = SET_TO_ZERO;
731 			    if(newy == 0)
732 				newy = SET_TO_ZERO;
733 			}
734 			if(!colorcompare(&color, &chr->color))
735 			{
736 			    color = chr->color;
737 			    newcolor = &color;
738 			}
739 			font.id = chr->font->id;
740 			if(lastfont != chr->font || lastsize != chr->size)
741 			    newfont = &font;
742 
743 			tag->writeBit = 0; // Q&D
744 			swf_TextSetInfoRecord(tag, newfont, chr->size, newcolor, newx, newy);
745 		    }
746 
747 		    lastfont = chr->font;
748 		    lastx = chr->x;
749 		    lasty = chr->y;
750 		    lastsize = chr->size;
751 		}
752 
753 		if(islast)
754 			break;
755 
756 		int nextx = chr->x;
757 		if(t<chardata->pos-1) nextx = chardata->chr[t+1].x;
758 		if(t==chardata->pos-1 && chardata->next) nextx = chardata->next->chr[0].x;
759 		int dx = nextx-chr->x;
760 
761 		int advance;
762 		if(dx>=0 && (dx<(1<<(advancebits-1)) || pass==0)) {
763 		   advance = dx;
764 		   lastx=nextx;
765 		} else {
766 		   advance = 0;
767 		   lastx=chr->x;
768 		}
769 
770 		charids[charstorepos] = chr->charid;
771 		charadvance[charstorepos] = advance;
772 		lastchar = chr->charid;
773 		charstorepos ++;
774 	    }
775 	    chardata = chardata->next;
776 	}
777     }
778 }
779 
chararray_destroy(chararray_t * chr)780 static void chararray_destroy(chararray_t*chr)
781 {
782     while(chr) {
783 	chararray_t*next = chr->next;
784 	chr->next = 0;
785 	free(chr);
786 	chr = next;
787     }
788 }
789 
matrix_diff(MATRIX * m1,MATRIX * m2)790 static inline int matrix_diff(MATRIX*m1, MATRIX*m2)
791 {
792     return memcmp(m1,m2,sizeof(MATRIX));
793 }
charbuffer_append(charbuffer_t * buf,SWFFONT * font,int charid,int x,int y,int size,RGBA color,MATRIX * m)794 static charbuffer_t*charbuffer_append(charbuffer_t*buf, SWFFONT*font, int charid, int x,int y, int size, RGBA color, MATRIX*m)
795 {
796     if(!buf || matrix_diff(&buf->matrix,m)) {
797 	charbuffer_t*n = rfx_calloc(sizeof(charbuffer_t));
798 	n->matrix = *m;
799 	n->next = buf;
800 	buf = n;
801     }
802     if(!buf->last || buf->last->pos == CHARDATAMAX) {
803 	chararray_t*n = rfx_calloc(sizeof(chararray_t));
804 	if(!buf->array) {
805 	    buf->array = buf->last = n;
806 	} else {
807 	    buf->last->next = n;
808 	    buf->last = n;
809 	}
810     }
811     chararray_t*a = buf->last;
812     a->chr[a->pos].font = font;
813     a->chr[a->pos].charid = charid;
814     a->chr[a->pos].x = x;
815     a->chr[a->pos].y = y;
816     a->chr[a->pos].color = color;
817     a->chr[a->pos].size = size;
818     a->pos++;
819     return buf;
820 }
821 
822 /* Notice: we can only put chars in the range -1639,1638 (-32768/20,32768/20).
823    So if we set this value to high, the char coordinates will overflow.
824    If we set it to low, however, the char positions will be inaccurate */
825 #define GLYPH_SCALE 1
826 
chararray_writetodev(gfxdevice_t * dev,chararray_t * array,MATRIX * matrix,char invisible)827 static void chararray_writetodev(gfxdevice_t*dev, chararray_t*array, MATRIX*matrix, char invisible)
828 {
829     swfoutput_internal*i = (swfoutput_internal*)dev->internal;
830 
831     int textid = getNewID(dev);
832     i->tag = swf_InsertTag(i->tag,ST_DEFINETEXT2);
833     swf_SetU16(i->tag, textid);
834     SRECT r;
835     r = getcharacterbbox(array, matrix, i->config_flashversion);
836     r = swf_ClipRect(i->pagebbox, r);
837     swf_SetRect(i->tag,&r);
838     swf_SetMatrix(i->tag, matrix);
839     msg("<trace> Placing text as ID %d", textid);
840     chararray_writetotag(array, i->tag);
841     i->chardata = 0;
842 
843     swf_SetU8(i->tag,0);
844 
845     if(i->swf->fileVersion >= 8) {
846 	i->tag = swf_InsertTag(i->tag, ST_CSMTEXTSETTINGS);
847 	swf_SetU16(i->tag, textid);
848 
849 	//swf_SetU8(i->tag, /*subpixel grid*/(2<<3)|/*flashtype*/0x40);
850 	swf_SetU8(i->tag, /*grid*/(1<<3)|/*flashtype*/0x40);
851 	//swf_SetU8(i->tag, /*no grid*/(0<<3)|/*flashtype*/0x40);
852 
853 	swf_SetU32(i->tag, 0);//thickness
854 	swf_SetU32(i->tag, 0);//sharpness
855 	//swf_SetU32(i->tag, 0x20000);//thickness
856 	//swf_SetU32(i->tag, 0x800000);//sharpness
857 	swf_SetU8(i->tag, 0);//reserved
858     }
859     if(invisible && i->config_flashversion>=8) {
860 	i->tag = swf_InsertTag(i->tag,ST_PLACEOBJECT3);
861 	swf_ObjectPlaceBlend(i->tag,textid,getNewDepth(dev),&i->page_matrix,NULL,NULL,BLENDMODE_MULTIPLY);
862     } else {
863 	i->tag = swf_InsertTag(i->tag,ST_PLACEOBJECT2);
864 	swf_ObjectPlace(i->tag,textid,getNewDepth(dev),&i->page_matrix,NULL,NULL);
865     }
866 }
867 
charbuffer_writetodevandfree(gfxdevice_t * dev,charbuffer_t * buf,char invisible)868 static void charbuffer_writetodevandfree(gfxdevice_t*dev, charbuffer_t*buf, char invisible)
869 {
870     while(buf) {
871 	charbuffer_t*next = buf->next;buf->next = 0;
872 	chararray_writetodev(dev, buf->array, &buf->matrix, invisible);
873 	chararray_destroy(buf->array);
874 	free(buf);
875 	buf = next;
876     }
877 }
878 
endtext(gfxdevice_t * dev)879 static void endtext(gfxdevice_t*dev)
880 {
881     swfoutput_internal*i = (swfoutput_internal*)dev->internal;
882     if(!i->textmode)
883         return;
884     charbuffer_writetodevandfree(dev, i->chardata, 0);i->chardata = 0;
885     i->textmode = 0;
886 }
887 
888 static int watermark2_width=47;
889 static int watermark2_height=11;
890 static int watermark2[47] = {95,1989,71,0,2015,337,1678,0,2015,5,1921,320,1938,25,2006,1024,
891                              1042,21,13,960,1039,976,8,2000,1359,1088,31,1989,321,1728,0,1152,
892                              1344,832,0,1984,0,896,1088,1088,896,0,1984,128,256,512,1984};
893 
draw_watermark(gfxdevice_t * dev,gfxbbox_t r,char drawall)894 static void draw_watermark(gfxdevice_t*dev, gfxbbox_t r, char drawall)
895 {
896     swfoutput_internal*i = (swfoutput_internal*)dev->internal;
897     double wx = r.xmax / 5.0;
898     double tx = r.xmax*4.0 / 5.0;
899     double ty = r.ymax-wx*watermark2_height/watermark2_width;
900     double sx = (r.xmax - tx) / watermark2_width;
901     double sy = (r.ymax - ty) / watermark2_height;
902     double px = sx-0.5;
903     double py = sy-0.5;
904     if(ty > 0 && px > 1.0 && py > 1.0) {
905 	int x,y;
906 	for(y=0;y<watermark2_height;y++)
907 	for(x=0;x<watermark2_width;x++) {
908 	    if(((watermark2[x]>>y)&1)) {
909 		if(!drawall && rand()%5)
910 		    continue;
911 		unsigned int b = rand();
912 		moveto(dev, i->tag, x*sx+tx+((b>>1)&1)/20.0, y*sy+ty+((b>>3)&1)/20.0);
913 		lineto(dev, i->tag, x*sx+px+tx+((b>>2)&1)/20.0, y*sy+ty+((b>>3)&1)/20.0);
914 		lineto(dev, i->tag, x*sx+px+tx+((b>>2)&1)/20.0, y*sy+py+ty+((b>>4)&1)/20.0);
915 		lineto(dev, i->tag, x*sx+tx+((b>>1)&1)/20.0, y*sy+py+ty+((b>>4)&1)/20.0);
916 		lineto(dev, i->tag, x*sx+tx+((b>>1)&1)/20.0, y*sy+ty+((b>>3)&1)/20.0);
917 	    }
918 	}
919     }
920 }
921 
swfoutput_setfillcolor(gfxdevice_t * dev,U8 r,U8 g,U8 b,U8 a)922 static void swfoutput_setfillcolor(gfxdevice_t* dev, U8 r, U8 g, U8 b, U8 a)
923 {
924     swfoutput_internal*i = (swfoutput_internal*)dev->internal;
925     if(i->fillrgb.r == r &&
926        i->fillrgb.g == g &&
927        i->fillrgb.b == b &&
928        i->fillrgb.a == a) return;
929     if(i->shapeid>=0)
930      endshape(dev);
931 
932     i->fillrgb.r = r;
933     i->fillrgb.g = g;
934     i->fillrgb.b = b;
935     i->fillrgb.a = a;
936 }
insert_watermark(gfxdevice_t * dev,char drawall)937 static void insert_watermark(gfxdevice_t*dev, char drawall)
938 {
939     swfoutput_internal*i = (swfoutput_internal*)dev->internal;
940     if(!drawall && i->watermarks>20)
941 	return;
942     endshape(dev);
943     endtext(dev);
944 
945     if(drawall) {
946 	swfoutput_setfillcolor(dev, 0,0,255,192);
947     } else {
948 	swfoutput_setfillcolor(dev, rand(),rand(),rand(),(rand()&127)+128);
949     }
950     startshape(dev);
951     startFill(dev);
952 
953     gfxbbox_t r; r.xmin = r.ymin = 0;
954     r.xmax = i->max_x;
955     r.ymax = i->max_y;
956     draw_watermark(dev, r, drawall);
957     endshape(dev);
958     i->watermarks++;
959 }
960 
961 static void drawoutline(gfxdevice_t*dev, gfxline_t*line);
962 
endpage(gfxdevice_t * dev)963 static void endpage(gfxdevice_t*dev)
964 {
965     swfoutput_internal*i = (swfoutput_internal*)dev->internal;
966     if(i->pagefinished)
967 	return;
968 
969     if(i->shapeid>=0)
970 	endshape(dev);
971     if(i->textmode)
972 	endtext(dev);
973     if(i->topchardata) {
974 	charbuffer_writetodevandfree(dev, i->topchardata, 1);
975 	i->topchardata=0;
976     }
977 
978     while(i->clippos)
979         dev->endclip(dev);
980 
981     if(i->stored_clipshapes) {
982         // in case of config_showclipshapes
983 	drawoutline(dev, i->stored_clipshapes);
984     }
985 
986     if(i->config_watermark) {
987 	insert_watermark(dev, 1);
988     }
989 
990     i->pagefinished = 1;
991 }
992 
addViewer(gfxdevice_t * dev)993 static void addViewer(gfxdevice_t* dev)
994 {
995     swfoutput_internal*i = (swfoutput_internal*)dev->internal;
996 
997     SHAPE*s;
998     RGBA button_colors[3]= {{0xbf,0x00,0x00,0x80},{0xbf,0x20,0x20,0xc0}, {0xbf,0xc0,0xc0,0xff}};
999     int ids[6];
1000     int button_sizex = 20;
1001     int button_sizey = 20;
1002     int t;
1003     RGBA black = {255,0,0,0};
1004     for(t=0;t<6;t++) {
1005 	i->tag = swf_InsertTag(i->tag,ST_DEFINESHAPE3);
1006 	swf_ShapeNew(&s);
1007 	int ls1 = swf_ShapeAddLineStyle(s,40,&black);
1008 	int fs1 = swf_ShapeAddSolidFillStyle(s,&button_colors[t/2]);
1009 	int shapeid = ids[t] = getNewID(dev);
1010 	swf_SetU16(i->tag,shapeid);
1011 	SRECT r;
1012 	r.xmin = -20*button_sizex;
1013 	r.xmax = 20*button_sizex;
1014 	r.ymin = 0;
1015 	r.ymax = 40*button_sizey;
1016 	swf_SetRect(i->tag,&r);              // set shape bounds
1017 	swf_SetShapeHeader(i->tag,s);        // write all styles to tag
1018 	swf_ShapeSetAll(i->tag,s,0*button_sizex,0,ls1,fs1,0);
1019 	swf_ShapeSetLine(i->tag,s,(1-(t&1)*2)*20*button_sizex,20*button_sizey);
1020 	swf_ShapeSetLine(i->tag,s,-(1-(t&1)*2)*20*button_sizex,20*button_sizey);
1021 	swf_ShapeSetLine(i->tag,s,0,-40*button_sizey);
1022 	swf_ShapeSetEnd(i->tag);   // finish drawing
1023 	swf_ShapeFree(s);   // clean shape structure (which isn't needed anymore after writing the tag)
1024     }
1025     ActionTAG*a1=0,*a2=0,*a3=0;
1026     a1 = action_NextFrame(a1);
1027     a1 = action_Stop(a1);
1028     a1 = action_End(a1);
1029 
1030     a2 = action_PreviousFrame(a2);
1031     a2 = action_Stop(a2);
1032     a2 = action_End(a2);
1033 
1034     a3 = action_Stop(a3);
1035     a3 = action_End(a3);
1036 
1037     i->tag = swf_InsertTag(i->tag, ST_DOACTION);
1038     swf_ActionSet(i->tag,a3);
1039 
1040     i->tag = swf_InsertTag(i->tag,ST_DEFINEBUTTON);
1041     int buttonid1 = getNewID(dev);
1042     swf_SetU16(i->tag, buttonid1);
1043     swf_ButtonSetRecord(i->tag,BS_UP|BS_HIT,ids[0],1,NULL,NULL);
1044     swf_ButtonSetRecord(i->tag,BS_OVER,ids[2],1,NULL,NULL);
1045     swf_ButtonSetRecord(i->tag,BS_DOWN,ids[4],1,NULL,NULL);
1046     swf_SetU8(i->tag,0); // end of button records
1047     swf_ActionSet(i->tag,a1);
1048 
1049     i->tag = swf_InsertTag(i->tag,ST_DEFINEBUTTON);
1050     int buttonid2 = getNewID(dev);
1051     swf_SetU16(i->tag, buttonid2);
1052     swf_ButtonSetRecord(i->tag,BS_UP|BS_HIT,ids[1],1,NULL,NULL);
1053     swf_ButtonSetRecord(i->tag,BS_OVER,ids[3],1,NULL,NULL);
1054     swf_ButtonSetRecord(i->tag,BS_DOWN,ids[5],1,NULL,NULL);
1055     swf_SetU8(i->tag,0); // end of button records
1056     swf_ActionSet(i->tag,a2);
1057 
1058     i->tag = swf_InsertTag(i->tag,ST_PLACEOBJECT2);
1059     MATRIX m;
1060     swf_GetMatrix(0, &m);
1061     m.tx = button_sizex*20+200;
1062     swf_ObjectPlace(i->tag, buttonid2, 65534,&m,0,0);
1063     i->tag = swf_InsertTag(i->tag,ST_PLACEOBJECT2);
1064     m.tx = button_sizex*20+200+200;
1065     swf_ObjectPlace(i->tag, buttonid1, 65535,&m,0,0);
1066 }
1067 
1068 
swf_startframe(gfxdevice_t * dev,int width,int height)1069 void swf_startframe(gfxdevice_t*dev, int width, int height)
1070 {
1071     swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1072     if(i->firstpage) {
1073 	if(i->config_protect) {
1074 	    i->tag = swf_InsertTag(i->tag, ST_PROTECT);
1075 	    i->config_protect = 0;
1076 	}
1077 	if(i->config_simpleviewer) {
1078 	    addViewer(dev);
1079 	}
1080     }
1081 
1082     if(!i->firstpage && !i->pagefinished)
1083         endpage(dev);
1084 
1085     msg("<verbose> Starting new SWF page of size %dx%d", width, height);
1086 
1087     swf_GetMatrix(0, &i->page_matrix);
1088     i->page_matrix.tx = 0;
1089     i->page_matrix.ty = 0;
1090     i->min_x = 0;
1091     i->min_y = 0;
1092     i->max_x = width;
1093     i->max_y = height;
1094     i->watermarks = 0;
1095 
1096     /* create a bbox structure with the page size. This is used
1097        for clipping shape and text bounding boxes. As we don't want to
1098        generate bounding boxes which extend beyond the movie size (in
1099        order to not confuse Flash), we clip everything against i->pagebbox */
1100     i->pagebbox.xmin = 0;
1101     i->pagebbox.ymin = 0;
1102     i->pagebbox.xmax = width*20;
1103     i->pagebbox.ymax = height*20;
1104 
1105     /* increase SWF's bounding box */
1106     swf_ExpandRect2(&i->swf->movieSize, &i->pagebbox);
1107 
1108     i->lastframeno = i->frameno;
1109     i->firstpage = 0;
1110     i->pagefinished = 0;
1111     i->chardata = 0;
1112 }
1113 
swf_endframe(gfxdevice_t * dev)1114 void swf_endframe(gfxdevice_t*dev)
1115 {
1116     swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1117 
1118     if(!i->pagefinished)
1119         endpage(dev);
1120 
1121     if( (i->swf->fileVersion <= 8) && (i->config_insertstoptag) ) {
1122 	ActionTAG*atag=0;
1123 	atag = action_Stop(atag);
1124 	atag = action_End(atag);
1125 	i->tag = swf_InsertTag(i->tag,ST_DOACTION);
1126 	swf_ActionSet(i->tag,atag);
1127     }
1128     i->tag = swf_InsertTag(i->tag,ST_SHOWFRAME);
1129     i->frameno ++;
1130 
1131     for(i->depth;i->depth>i->startdepth;i->depth--) {
1132         i->tag = swf_InsertTag(i->tag,ST_REMOVEOBJECT2);
1133         swf_SetU16(i->tag,i->depth);
1134     }
1135     i->depth = i->startdepth;
1136 
1137     if(i->config_frameresets) {
1138 	for(i->currentswfid;i->currentswfid>i->startids;i->currentswfid--) {
1139 	    i->tag = swf_InsertTag(i->tag,ST_FREECHARACTER);
1140 	    swf_SetU16(i->tag,i->currentswfid);
1141 	}
1142 	i->currentswfid = i->startids;
1143     }
1144 }
1145 
setBackground(gfxdevice_t * dev,int x1,int y1,int x2,int y2)1146 static void setBackground(gfxdevice_t*dev, int x1, int y1, int x2, int y2)
1147 {
1148     swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1149     RGBA rgb;
1150     rgb.a = rgb.r = rgb.g = rgb.b = 0xff;
1151     SRECT r;
1152     SHAPE* s;
1153     int ls1=0,fs1=0;
1154     int shapeid = getNewID(dev);
1155     r.xmin = x1;
1156     r.ymin = y1;
1157     r.xmax = x2;
1158     r.ymax = y2;
1159     i->tag = swf_InsertTag(i->tag, ST_DEFINESHAPE);
1160     swf_ShapeNew(&s);
1161     fs1 = swf_ShapeAddSolidFillStyle(s, &rgb);
1162     swf_SetU16(i->tag,shapeid);
1163     swf_SetRect(i->tag,&r);
1164     swf_SetShapeHeader(i->tag,s);
1165     swf_ShapeSetAll(i->tag,s,x1,y1,ls1,fs1,0);
1166     swf_ShapeSetLine(i->tag,s,(x2-x1),0);
1167     swf_ShapeSetLine(i->tag,s,0,(y2-y1));
1168     swf_ShapeSetLine(i->tag,s,(x1-x2),0);
1169     swf_ShapeSetLine(i->tag,s,0,(y1-y2));
1170     swf_ShapeSetEnd(i->tag);
1171     swf_ShapeFree(s);
1172     i->tag = swf_InsertTag(i->tag, ST_PLACEOBJECT2);
1173     swf_ObjectPlace(i->tag,shapeid,getNewDepth(dev),0,0,0);
1174     i->tag = swf_InsertTag(i->tag, ST_PLACEOBJECT2);
1175     swf_ObjectPlaceClip(i->tag,shapeid,getNewDepth(dev),0,0,0,65535);
1176 }
1177 
1178 /* initialize the swf writer */
gfxdevice_swf_init(gfxdevice_t * dev)1179 void gfxdevice_swf_init(gfxdevice_t* dev)
1180 {
1181     memset(dev, 0, sizeof(gfxdevice_t));
1182 
1183     dev->name = "swf";
1184 
1185     dev->internal = init_internal_struct(); // set config to default values
1186 
1187     dev->startpage = swf_startframe;
1188     dev->endpage = swf_endframe;
1189     dev->finish = swf_finish;
1190     dev->fillbitmap = swf_fillbitmap;
1191     dev->setparameter = swf_setparameter;
1192     dev->stroke = swf_stroke;
1193     dev->startclip = swf_startclip;
1194     dev->endclip = swf_endclip;
1195     dev->fill = swf_fill;
1196     dev->fillgradient = swf_fillgradient;
1197     dev->addfont = swf_addfont;
1198     dev->drawchar = swf_drawchar;
1199     dev->drawlink = swf_drawlink;
1200 
1201     swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1202     i->dev = dev;
1203 
1204     msg("<verbose> initializing swf output\n", i->max_x,i->max_y);
1205 
1206     i->swffont = 0;
1207 
1208     i->swf = (SWF*)rfx_calloc(sizeof(SWF));
1209     i->swf->fileVersion    = 0;
1210     i->swf->frameRate      = 0x80;
1211     i->swf->movieSize.xmin = 0;
1212     i->swf->movieSize.ymin = 0;
1213     i->swf->movieSize.xmax = 0;
1214     i->swf->movieSize.ymax = 0;
1215 
1216     if(i->config_local_with_filesystem) {
1217         i->swf->fileAttributes = 8; // as3, local-with-filesystem
1218     } else {
1219         i->swf->fileAttributes = 9; // as3, local-with-network
1220     }
1221 
1222     i->swf->firstTag = swf_InsertTag(NULL,ST_SETBACKGROUNDCOLOR);
1223     i->tag = i->swf->firstTag;
1224     RGBA rgb;
1225     rgb.a = rgb.r = rgb.g = rgb.b = 0xff;
1226     //rgb.r = 0;
1227     swf_SetRGB(i->tag,&rgb);
1228 
1229     i->startdepth = i->depth = 0;
1230     i->startids = i->currentswfid = 0;
1231 }
1232 
startshape(gfxdevice_t * dev)1233 static void startshape(gfxdevice_t*dev)
1234 {
1235     swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1236     SRECT r;
1237 
1238     if(i->shapeid>=0)
1239 	return;
1240     //if(i->chardatapos && i->chardata[i->chardatapos-1].color.a)
1241     endtext(dev);
1242 
1243     i->tag = swf_InsertTag(i->tag,ST_DEFINESHAPE3);
1244 
1245     swf_ShapeNew(&i->shape);
1246     i->linestyleid = swf_ShapeAddLineStyle(i->shape,i->linewidth,&i->strokergb);
1247     i->fillstyleid = swf_ShapeAddSolidFillStyle(i->shape,&i->fillrgb);
1248     if(i->mark) {
1249 	RGBA markcol = {0,i->mark[0],i->mark[1],i->mark[2]};
1250 	swf_ShapeAddSolidFillStyle(i->shape,&markcol);
1251     }
1252 
1253     i->shapeid = getNewID(dev);
1254 
1255     msg("<debug> Using shape id %d", i->shapeid);
1256 
1257     swf_SetU16(i->tag,i->shapeid);  // ID
1258 
1259     i->bboxrectpos = i->tag->len;
1260     /* changed later */
1261     swf_SetRect(i->tag,&i->pagebbox);
1262 
1263     memset(&i->bboxrect, 0, sizeof(i->bboxrect));
1264 
1265     swf_SetShapeStyles(i->tag,i->shape);
1266     swf_ShapeCountBits(i->shape,NULL,NULL);
1267     swf_SetShapeBits(i->tag,i->shape);
1268 
1269     /* TODO: do we really need this? */
1270     //swf_ShapeSetAll(i->tag,i->shape,/*x*/0,/*y*/0,i->linestyleid,0,0);
1271     //swf_ShapeSetAll(i->tag,i->shape,/*x*/UNDEFINED_COORD,/*y*/UNDEFINED_COORD,i->linestyleid,0,0);
1272     i->swflastx=i->swflasty=UNDEFINED_COORD;
1273     i->lastwasfill = -1;
1274     i->shapeisempty = 1;
1275 }
1276 
starttext(gfxdevice_t * dev)1277 static void starttext(gfxdevice_t*dev)
1278 {
1279     swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1280     if(i->shapeid>=0)
1281         endshape(dev);
1282 
1283     if(i->config_watermark) {
1284 	insert_watermark(dev, 0);
1285     }
1286     i->textmode = 1;
1287     i->swflastx=i->swflasty=0;
1288 }
1289 
1290 
1291 /* TODO: move to ../lib/rfxswf */
changeRect(gfxdevice_t * dev,TAG * tag,int pos,SRECT * newrect)1292 void changeRect(gfxdevice_t*dev, TAG*tag, int pos, SRECT*newrect)
1293 {
1294     swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1295     /* determine length of old rect */
1296     tag->pos = pos;
1297     tag->readBit = 0;
1298     SRECT old;
1299     swf_GetRect(tag, &old);
1300     swf_ResetReadBits(tag);
1301     int pos_end = tag->pos;
1302 
1303     int len = tag->len - pos_end;
1304     U8*data = (U8*)malloc(len);
1305     memcpy(data, &tag->data[pos_end], len);
1306     tag->writeBit = 0;
1307     tag->len = pos;
1308     swf_SetRect(tag, newrect);
1309     swf_SetBlock(tag, data, len);
1310     free(data);
1311     tag->pos = tag->readBit = 0;
1312 }
1313 
cancelshape(gfxdevice_t * dev)1314 void cancelshape(gfxdevice_t*dev)
1315 {
1316     swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1317     /* delete old shape tag */
1318     TAG*todel = i->tag;
1319     i->tag = i->tag->prev;
1320     swf_DeleteTag(0, todel);
1321     if(i->shape) {swf_ShapeFree(i->shape);i->shape=0;}
1322     i->shapeid = -1;
1323     i->bboxrectpos = -1;
1324 
1325 //    i->currentswfid--; // doesn't work, for some reason
1326 }
1327 
fixAreas(gfxdevice_t * dev)1328 void fixAreas(gfxdevice_t*dev)
1329 {
1330     swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1331     if(!i->shapeisempty && i->fill &&
1332        (i->bboxrect.xmin == i->bboxrect.xmax ||
1333         i->bboxrect.ymin == i->bboxrect.ymax) &&
1334         i->config_minlinewidth >= 0.001
1335        ) {
1336 	msg("<debug> Shape has size 0: width=%.2f height=%.2f",
1337 		(i->bboxrect.xmax-i->bboxrect.xmin)/20.0,
1338 		(i->bboxrect.ymax-i->bboxrect.ymin)/20.0
1339 		);
1340 
1341 	SRECT r = i->bboxrect;
1342 
1343 	if(r.xmin == r.xmax && r.ymin == r.ymax) {
1344 	    /* this thing comes down to a single dot- nothing to fix here */
1345 	    return;
1346 	}
1347 
1348 	cancelshape(dev);
1349 
1350 	RGBA save_col = i->strokergb;
1351 	int  save_width = i->linewidth;
1352 
1353 	i->strokergb = i->fillrgb;
1354 	i->linewidth = (int)(i->config_minlinewidth*20);
1355 	if(i->linewidth==0) i->linewidth = 1;
1356 
1357 	startshape(dev);
1358 	stopFill(dev);
1359 
1360 	moveto(dev, i->tag, r.xmin/20.0,r.ymin/20.0);
1361 	lineto(dev, i->tag, r.xmax/20.0,r.ymax/20.0);
1362 
1363 	i->strokergb = save_col;
1364 	i->linewidth = save_width;
1365     }
1366 
1367 }
1368 
endshape_noput(gfxdevice_t * dev)1369 static void endshape_noput(gfxdevice_t*dev)
1370 {
1371     swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1372     if(i->shapeid<0)
1373         return;
1374     //changeRect(dev, i->tag, i->bboxrectpos, &i->bboxrect);
1375     i->shapeid = -1;
1376     if(i->shape) {
1377         swf_ShapeFree(i->shape);
1378         i->shape=0;
1379     }
1380     i->fill=0;
1381     i->shapeposx=0;
1382     i->shapeposy=0;
1383 }
1384 
endshape(gfxdevice_t * dev)1385 static void endshape(gfxdevice_t*dev)
1386 {
1387     swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1388     if(i->shapeid<0)
1389         return;
1390 
1391     fixAreas(dev);
1392 
1393     if(i->shapeisempty ||
1394        /*bbox empty?*/
1395        (i->bboxrect.xmin == i->bboxrect.xmax &&
1396         i->bboxrect.ymin == i->bboxrect.ymax))
1397     {
1398 	// delete the shape again, we didn't do anything
1399 	msg("<debug> cancelling shape: bbox is (%f,%f,%f,%f)",
1400 		i->bboxrect.xmin /20.0,
1401 		i->bboxrect.ymin /20.0,
1402 		i->bboxrect.xmax /20.0,
1403 		i->bboxrect.ymax /20.0
1404 		);
1405 	cancelshape(dev);
1406 	return;
1407     }
1408 
1409     swf_ShapeSetEnd(i->tag);
1410 
1411     SRECT r = swf_ClipRect(i->pagebbox, i->bboxrect);
1412     changeRect(dev, i->tag, i->bboxrectpos, &r);
1413 
1414     msg("<trace> Placing shape ID %d", i->shapeid);
1415 
1416     i->tag = swf_InsertTag(i->tag,ST_PLACEOBJECT2);
1417     MATRIX m = i->page_matrix;
1418     m.tx += i->shapeposx;
1419     m.ty += i->shapeposy;
1420     swf_ObjectPlace(i->tag,i->shapeid,getNewDepth(dev),&m,NULL,NULL);
1421 
1422     swf_ShapeFree(i->shape);
1423     i->shape = 0;
1424     i->shapeid = -1;
1425     i->bboxrectpos = -1;
1426 
1427     i->fill=0;
1428     i->shapeposx=0;
1429     i->shapeposy=0;
1430 }
1431 
wipeSWF(SWF * swf)1432 void wipeSWF(SWF*swf)
1433 {
1434     TAG*tag = swf->firstTag;
1435     while(tag) {
1436 	TAG*next = tag->next;
1437 	if(tag->id != ST_SETBACKGROUNDCOLOR &&
1438 	   tag->id != ST_END &&
1439 	   tag->id != ST_DOACTION &&
1440 	   tag->id != ST_SHOWFRAME) {
1441 	    swf_DeleteTag(swf, tag);
1442 	}
1443 	tag = next;
1444     }
1445 }
1446 
swfoutput_finalize(gfxdevice_t * dev)1447 void swfoutput_finalize(gfxdevice_t*dev)
1448 {
1449     swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1450 
1451     if(i->tag && i->tag->id == ST_END)
1452         return; //already done
1453 
1454     i->swf->fileVersion = i->config_flashversion;
1455     i->swf->frameRate = i->config_framerate*0x100;
1456 
1457     if(i->config_bboxvars) {
1458 	TAG* tag = swf_InsertTag(i->swf->firstTag, ST_DOACTION);
1459 	ActionTAG*a = 0;
1460 	a = action_PushString(a, "xmin");
1461 	a = action_PushFloat(a, i->swf->movieSize.xmin / 20.0);
1462 	a = action_SetVariable(a);
1463 	a = action_PushString(a, "ymin");
1464 	a = action_PushFloat(a, i->swf->movieSize.ymin / 20.0);
1465 	a = action_SetVariable(a);
1466 	a = action_PushString(a, "xmax");
1467 	a = action_PushFloat(a, i->swf->movieSize.xmax / 20.0);
1468 	a = action_SetVariable(a);
1469 	a = action_PushString(a, "ymax");
1470 	a = action_PushFloat(a, i->swf->movieSize.ymax / 20.0);
1471 	a = action_SetVariable(a);
1472 	a = action_PushString(a, "width");
1473 	a = action_PushFloat(a, (i->swf->movieSize.xmax - i->swf->movieSize.xmin) / 20.0);
1474 	a = action_SetVariable(a);
1475 	a = action_PushString(a, "height");
1476 	a = action_PushFloat(a, (i->swf->movieSize.ymax - i->swf->movieSize.ymin) / 20.0);
1477 	a = action_SetVariable(a);
1478 	a = action_End(a);
1479 	swf_ActionSet(tag, a);
1480 	swf_ActionFree(a);
1481     }
1482 
1483     if(i->mark) {
1484 	free(i->mark);i->mark = 0;
1485     }
1486 
1487     endpage(dev);
1488     fontlist_t *iterator = i->fontlist;
1489     char use_font3 = i->config_flashversion>=8 && !NO_FONT3;
1490 
1491     while(iterator) {
1492 	TAG*mtag = i->swf->firstTag;
1493 	if(iterator->swffont) {
1494 	    if(!i->config_storeallcharacters) {
1495 		msg("<debug> Reducing font %s", iterator->swffont->name);
1496 		swf_FontReduce(iterator->swffont);
1497 	    }
1498 	    int used = iterator->swffont->use && iterator->swffont->use->used_glyphs;
1499 	    if(used) {
1500 		if(!use_font3) {
1501 		    mtag = swf_InsertTag(mtag, ST_DEFINEFONT2);
1502 		    swf_FontSetDefine2(mtag, iterator->swffont);
1503 		} else {
1504 		    mtag = swf_InsertTag(mtag, ST_DEFINEFONT3);
1505 		    swf_FontSetDefine2(mtag, iterator->swffont);
1506 		}
1507 	    }
1508 	}
1509 
1510         iterator = iterator->next;
1511     }
1512 
1513     i->tag = swf_InsertTag(i->tag,ST_END);
1514     TAG* tag = i->tag->prev;
1515 
1516     if(use_font3 && i->config_storeallcharacters && i->config_alignfonts) {
1517 	swf_FontPostprocess(i->swf); // generate alignment information
1518     }
1519 
1520     /* remove the removeobject2 tags between the last ST_SHOWFRAME
1521        and the ST_END- they confuse the flash player  */
1522     while(tag->id == ST_REMOVEOBJECT2) {
1523         TAG* prev = tag->prev;
1524         swf_DeleteTag(i->swf, tag);
1525         tag = prev;
1526     }
1527 
1528     if(i->overflow) {
1529 	wipeSWF(i->swf);
1530     }
1531     if(i->config_enablezlib || i->config_flashversion>=6) {
1532 	i->swf->compressed = 1;
1533     }
1534 
1535     /* Add AVM2 actionscript */
1536     if(i->config_flashversion>=9 &&
1537             (i->config_insertstoptag || i->hasbuttons) && !i->config_linknameurl) {
1538         swf_AddButtonLinks(i->swf, i->config_insertstoptag,
1539                 i->config_internallinkfunction||i->config_externallinkfunction);
1540     }
1541 //    if(i->config_reordertags)
1542 //	swf_Optimize(i->swf);
1543 }
1544 
swfresult_save(gfxresult_t * gfx,const char * filename)1545 int swfresult_save(gfxresult_t*gfx, const char*filename)
1546 {
1547     SWF*swf = (SWF*)gfx->internal;
1548     int fi;
1549     if(filename)
1550      fi = open(filename, O_BINARY|O_CREAT|O_TRUNC|O_WRONLY, 0777);
1551     else
1552      fi = 1; // stdout
1553 
1554     if(fi<=0) {
1555 	msg("<fatal> Could not create \"%s\". ", FIXNULL(filename));
1556 	return -1;
1557     }
1558 
1559     if FAILED(swf_WriteSWF(fi,swf))
1560         msg("<error> WriteSWF() failed.\n");
1561 
1562     if(filename)
1563      close(fi);
1564     return 0;
1565 }
swfresult_get(gfxresult_t * gfx,const char * name)1566 void* swfresult_get(gfxresult_t*gfx, const char*name)
1567 {
1568     SWF*swf = (SWF*)gfx->internal;
1569     if(!strcmp(name, "swf")) {
1570 	return (void*)swf_CopySWF(swf);
1571     } else if(!strcmp(name, "xmin")) {
1572 	return (void*)(ptroff_t)(swf->movieSize.xmin/20);
1573     } else if(!strcmp(name, "ymin")) {
1574 	return (void*)(ptroff_t)(swf->movieSize.ymin/20);
1575     } else if(!strcmp(name, "xmax")) {
1576 	return (void*)(ptroff_t)(swf->movieSize.xmax/20);
1577     } else if(!strcmp(name, "ymax")) {
1578 	return (void*)(ptroff_t)(swf->movieSize.ymax/20);
1579     } else if(!strcmp(name, "width")) {
1580 	return (void*)(ptroff_t)((swf->movieSize.xmax - swf->movieSize.xmin)/20);
1581     } else if(!strcmp(name, "height")) {
1582 	return (void*)(ptroff_t)((swf->movieSize.ymax - swf->movieSize.ymin)/20);
1583     }
1584     return 0;
1585 }
swfresult_destroy(gfxresult_t * gfx)1586 void swfresult_destroy(gfxresult_t*gfx)
1587 {
1588     if(gfx->internal) {
1589 	swf_FreeTags((SWF*)gfx->internal);
1590 	free(gfx->internal);
1591 	gfx->internal = 0;
1592     }
1593     memset(gfx, 0, sizeof(gfxresult_t));
1594     free(gfx);
1595 }
1596 
1597 static void swfoutput_destroy(gfxdevice_t* dev);
1598 
swf_finish(gfxdevice_t * dev)1599 gfxresult_t* swf_finish(gfxdevice_t* dev)
1600 {
1601     swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1602     gfxresult_t*result;
1603 
1604     if(i->config_linktarget) {
1605 	free(i->config_linktarget);
1606 	i->config_linktarget = 0;
1607     }
1608 
1609     swfoutput_finalize(dev);
1610     SWF* swf = i->swf;i->swf = 0;
1611     swfoutput_destroy(dev);
1612 
1613     result = (gfxresult_t*)rfx_calloc(sizeof(gfxresult_t));
1614     result->internal = swf;
1615     result->save = swfresult_save;
1616     result->write = 0;
1617     result->get = swfresult_get;
1618     result->destroy = swfresult_destroy;
1619     return result;
1620 }
1621 
1622 /* Perform cleaning up */
swfoutput_destroy(gfxdevice_t * dev)1623 static void swfoutput_destroy(gfxdevice_t* dev)
1624 {
1625     swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1626     if(!i) {
1627         /* not initialized yet- nothing to destroy */
1628         return;
1629     }
1630 
1631     fontlist_t *tmp,*iterator = i->fontlist;
1632     while(iterator) {
1633 	if(iterator->swffont) {
1634 	    swf_FontFree(iterator->swffont);iterator->swffont=0;
1635 	}
1636         tmp = iterator;
1637         iterator = iterator->next;
1638         free(tmp);
1639     }
1640     if(i->swf) {swf_FreeTags(i->swf);free(i->swf);i->swf = 0;}
1641 
1642     free(i);i=0;
1643     memset(dev, 0, sizeof(gfxdevice_t));
1644 }
1645 
swfoutput_setstrokecolor(gfxdevice_t * dev,U8 r,U8 g,U8 b,U8 a)1646 static void swfoutput_setstrokecolor(gfxdevice_t* dev, U8 r, U8 g, U8 b, U8 a)
1647 {
1648     swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1649     if(i->strokergb.r == r &&
1650        i->strokergb.g == g &&
1651        i->strokergb.b == b &&
1652        i->strokergb.a == a) return;
1653 
1654     if(i->shapeid>=0)
1655      endshape(dev);
1656     i->strokergb.r = r;
1657     i->strokergb.g = g;
1658     i->strokergb.b = b;
1659     i->strokergb.a = a;
1660 }
1661 
1662 //#define ROUND_UP 19
1663 //#define ROUND_UP 10
1664 
swfoutput_setlinewidth(gfxdevice_t * dev,double _linewidth)1665 static void swfoutput_setlinewidth(gfxdevice_t*dev, double _linewidth)
1666 {
1667     swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1668     if(i->linewidth == (U16)(_linewidth*20+19.0/20.0))
1669         return;
1670     if(i->shapeid>=0)
1671 	endshape(dev);
1672     i->linewidth = (U16)(_linewidth*20+19.0/20.0);
1673 }
1674 
1675 
1676 static void drawlink(gfxdevice_t*dev, ActionTAG*,ActionTAG*, gfxline_t*points, char mouseover, char*type, const char*url);
1677 static void swfoutput_namedlink(gfxdevice_t*dev, char*name, gfxline_t*points);
1678 static void swfoutput_linktopage(gfxdevice_t*dev, int page, gfxline_t*points);
1679 static void swfoutput_linktourl(gfxdevice_t*dev, const char*url, gfxline_t*points);
1680 
1681 /*void swfoutput_drawlink(gfxdevice_t*dev, char*url, gfxline_t*points)
1682 {
1683     swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1684     dev->drawlink(dev, points, url);
1685 }*/
1686 
swf_drawlink(gfxdevice_t * dev,gfxline_t * points,const char * url,const char * text)1687 void swf_drawlink(gfxdevice_t*dev, gfxline_t*points, const char*url, const char*text)
1688 {
1689     swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1690 
1691     if(i->config_disablelinks)
1692         return;
1693 
1694     if(!strncmp("http://pdf2swf:", url, 15)) {
1695 	char*tmp = strdup(url);
1696 	int l = strlen(tmp);
1697 	if(tmp[l-1] == '/')
1698 	   tmp[l-1] = 0;
1699 	swfoutput_namedlink(dev, tmp+15, points);
1700 	free(tmp);
1701 	return;
1702     } else if(!strncmp("page", url, 4)) {
1703 	int t, nodigit=0;
1704 	for(t=4;url[t];t++)
1705 	    if(url[t]<'0' || url[t]>'9')
1706 		nodigit = 1;
1707 	if(!nodigit) {
1708 	    int page = atoi(&url[4]);
1709 	    if(page<0) page = 0;
1710 	    swfoutput_linktopage(dev, page, points);
1711 	}
1712     } else {
1713 	swfoutput_linktourl(dev, url, points);
1714     }
1715 }
swfoutput_linktourl(gfxdevice_t * dev,const char * url,gfxline_t * points)1716 void swfoutput_linktourl(gfxdevice_t*dev, const char*url, gfxline_t*points)
1717 {
1718     ActionTAG* actions = 0;
1719     swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1720     if(i->shapeid>=0)
1721 	endshape(dev);
1722     if(i->textmode)
1723 	endtext(dev);
1724 
1725     /* TODO: escape special characters in url */
1726 
1727     if(i->config_externallinkfunction && i->config_flashversion<=8) {
1728 	actions = action_PushString(actions, url); //parameter
1729 	actions = action_PushInt(actions, 1); //number of parameters (1)
1730 	actions = action_PushString(actions, i->config_externallinkfunction); //function name
1731 	actions = action_CallFunction(actions);
1732     } else if(!i->config_linktarget) {
1733 	if(!i->config_opennewwindow)
1734 	  actions = action_GetUrl(actions, url, "_parent");
1735 	else
1736 	  actions = action_GetUrl(actions, url, "_this");
1737     } else {
1738 	actions = action_GetUrl(actions, url, i->config_linktarget);
1739     }
1740     actions = action_End(actions);
1741 
1742     drawlink(dev, actions, 0, points, 0, "url", url);
1743 
1744     swf_ActionFree(actions);
1745 }
swfoutput_linktopage(gfxdevice_t * dev,int page,gfxline_t * points)1746 void swfoutput_linktopage(gfxdevice_t*dev, int page, gfxline_t*points)
1747 {
1748     swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1749     ActionTAG* actions = 0;
1750 
1751     if(i->shapeid>=0)
1752 	endshape(dev);
1753     if(i->textmode)
1754 	endtext(dev);
1755 
1756     if(!i->config_internallinkfunction || i->config_flashversion>=9) {
1757 	actions = action_GotoFrame(actions, page-1);
1758 	actions = action_End(actions);
1759     } else {
1760 	actions = action_PushInt(actions, page); //parameter
1761 	actions = action_PushInt(actions, 1); //number of parameters (1)
1762 	actions = action_PushString(actions, i->config_internallinkfunction); //function name
1763 	actions = action_CallFunction(actions);
1764 	actions = action_End(actions);
1765     }
1766 
1767     char name[80];
1768     sprintf(name, "page%d", page);
1769 
1770     drawlink(dev, actions, 0, points, 0, "page", name);
1771 
1772     swf_ActionFree(actions);
1773 }
1774 
1775 /* Named Links (a.k.a. Acrobatmenu) are used to implement various gadgets
1776    of the viewer objects, like subtitles, index elements etc.
1777 */
swfoutput_namedlink(gfxdevice_t * dev,char * name,gfxline_t * points)1778 void swfoutput_namedlink(gfxdevice_t*dev, char*name, gfxline_t*points)
1779 {
1780     swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1781     ActionTAG *actions1,*actions2;
1782     char*tmp = strdup(name);
1783     char mouseover = 1;
1784 
1785     if(i->shapeid>=0)
1786 	endshape(dev);
1787     if(i->textmode)
1788 	endtext(dev);
1789 
1790     char*type = 0;
1791     if(!strncmp(tmp, "call:", 5))
1792     {
1793 	char*x = strchr(&tmp[5], ':');
1794 	if(!x) {
1795 	    actions1 = action_PushInt(0, 0); //number of parameters (0)
1796 	    actions1 = action_PushString(actions1, &tmp[5]); //function name
1797 	    actions1 = action_CallFunction(actions1);
1798 	    actions1 = action_End(actions1);
1799 	} else {
1800 	    *x = 0;
1801 	    actions1 = action_PushString(0, x+1); //parameter
1802 	    actions1 = action_PushInt(actions1, 1); //number of parameters (1)
1803 	    actions1 = action_PushString(actions1, &tmp[5]); //function name
1804 	    actions1 = action_CallFunction(actions1);
1805 	    actions1 = action_End(actions1);
1806 	}
1807 	actions2 = action_End(0);
1808 	mouseover = 0;
1809         type = "call";
1810     }
1811     else
1812     {
1813 	actions1 = action_PushString(0, "/:subtitle");
1814 	actions1 = action_PushString(actions1, name);
1815 	actions1 = action_SetVariable(actions1);
1816 	actions1 = action_End(actions1);
1817 
1818 	actions2 = action_PushString(0, "/:subtitle");
1819 	actions2 = action_PushString(actions2, "");
1820 	actions2 = action_SetVariable(actions2);
1821 	actions2 = action_End(actions2);
1822         type = "subtitle";
1823     }
1824 
1825     drawlink(dev, actions1, actions2, points, mouseover, type, name);
1826 
1827     swf_ActionFree(actions1);
1828     swf_ActionFree(actions2);
1829     free(tmp);
1830 }
1831 
drawgfxline(gfxdevice_t * dev,gfxline_t * line,int fill)1832 static void drawgfxline(gfxdevice_t*dev, gfxline_t*line, int fill)
1833 {
1834     swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1835     gfxcoord_t lastx=0,lasty=0,px=0,py=0;
1836     char lastwasmoveto;
1837     int lines= 0, splines=0;
1838 
1839     i->fill = fill;
1840 
1841     while(1) {
1842 	if(!line)
1843 	    break;
1844 	/* check whether the next segment is zero */
1845 	if(line->type == gfx_moveTo) {
1846 	    moveto(dev, i->tag, line->x, line->y);
1847 	    px = lastx = line->x;
1848 	    py = lasty = line->y;
1849 	    lastwasmoveto = 1;
1850 	} if(line->type == gfx_lineTo) {
1851 	    lineto(dev, i->tag, line->x, line->y);
1852 	    px = line->x;
1853 	    py = line->y;
1854 	    lastwasmoveto = 0;
1855 	    lines++;
1856 	} else if(line->type == gfx_splineTo) {
1857 	    plotxy_t s,p;
1858 	    s.x = line->sx;p.x = line->x;
1859 	    s.y = line->sy;p.y = line->y;
1860 	    splineto(dev, i->tag, s, p);
1861 	    px = line->x;
1862 	    py = line->y;
1863 	    lastwasmoveto = 0;
1864 	    splines++;
1865 	}
1866 	line = line->next;
1867     }
1868     msg("<trace> drawgfxline, %d lines, %d splines", lines, splines);
1869 }
1870 
1871 
drawlink(gfxdevice_t * dev,ActionTAG * actions1,ActionTAG * actions2,gfxline_t * points,char mouseover,char * type,const char * url)1872 static void drawlink(gfxdevice_t*dev, ActionTAG*actions1, ActionTAG*actions2, gfxline_t*points, char mouseover, char*type, const char*url)
1873 {
1874     swfoutput_internal*i = (swfoutput_internal*)dev->internal;
1875     RGBA rgb;
1876     SRECT r;
1877     int lsid=0;
1878     int fsid;
1879     int myshapeid;
1880     int myshapeid2;
1881     double posx = 0;
1882     double posy = 0;
1883     int buttonid = getNewID(dev);
1884     gfxbbox_t bbox = gfxline_getbbox(points);
1885 
1886     if(i->config_linknameurl) {
1887         actions1 = 0;
1888         actions2 = 0;
1889     }
1890 
1891     i->hasbuttons = 1;
1892 
1893     /* shape */
1894     myshapeid = getNewID(dev);
1895     i->tag = swf_InsertTag(i->tag,ST_DEFINESHAPE3);
1896     swf_ShapeNew(&i->shape);
1897     rgb.r = rgb.b = rgb.a = rgb.g = 0;
1898     fsid = swf_ShapeAddSolidFillStyle(i->shape,&rgb);
1899     swf_SetU16(i->tag, myshapeid);
1900     r.xmin = (int)(bbox.xmin*20);
1901     r.ymin = (int)(bbox.ymin*20);
1902     r.xmax = (int)(bbox.xmax*20);
1903     r.ymax = (int)(bbox.ymax*20);
1904     r = swf_ClipRect(i->pagebbox, r);
1905     swf_SetRect(i->tag,&r);
1906     swf_SetShapeStyles(i->tag,i->shape);
1907     swf_ShapeCountBits(i->shape,NULL,NULL);
1908     swf_SetShapeBits(i->tag,i->shape);
1909     swf_ShapeSetAll(i->tag,i->shape,/*x*/0,/*y*/0,0,fsid,0);
1910     i->swflastx = i->swflasty = 0;
1911     drawgfxline(dev, points, 1);
1912     swf_ShapeSetEnd(i->tag);
1913     swf_ShapeFree(i->shape);
1914 
1915     /* shape2 */
1916     myshapeid2 = getNewID(dev);
1917     i->tag = swf_InsertTag(i->tag,ST_DEFINESHAPE3);
1918     swf_ShapeNew(&i->shape);
1919 
1920     rgb = i->config_linkcolor;
1921 
1922     fsid = swf_ShapeAddSolidFillStyle(i->shape,&rgb);
1923     swf_SetU16(i->tag, myshapeid2);
1924     r.xmin = (int)(bbox.xmin*20);
1925     r.ymin = (int)(bbox.ymin*20);
1926     r.xmax = (int)(bbox.xmax*20);
1927     r.ymax = (int)(bbox.ymax*20);
1928     r = swf_ClipRect(i->pagebbox, r);
1929     swf_SetRect(i->tag,&r);
1930     swf_SetShapeStyles(i->tag,i->shape);
1931     swf_ShapeCountBits(i->shape,NULL,NULL);
1932     swf_SetShapeBits(i->tag,i->shape);
1933     swf_ShapeSetAll(i->tag,i->shape,/*x*/0,/*y*/0,0,fsid,0);
1934     i->swflastx = i->swflasty = 0;
1935     drawgfxline(dev, points, 1);
1936     swf_ShapeSetEnd(i->tag);
1937     swf_ShapeFree(i->shape);
1938 
1939     if(!mouseover)
1940     {
1941 	i->tag = swf_InsertTag(i->tag,ST_DEFINEBUTTON);
1942 	swf_SetU16(i->tag,buttonid); //id
1943 	swf_ButtonSetFlags(i->tag, 0); //menu=no
1944 	swf_ButtonSetRecord(i->tag,0x01,myshapeid,i->depth,0,0);
1945 	swf_ButtonSetRecord(i->tag,0x02,myshapeid2,i->depth,0,0);
1946 	swf_ButtonSetRecord(i->tag,0x04,myshapeid2,i->depth,0,0);
1947 	swf_ButtonSetRecord(i->tag,0x08,myshapeid,i->depth,0,0);
1948 	swf_SetU8(i->tag,0);
1949 	swf_ActionSet(i->tag,actions1);
1950 	swf_SetU8(i->tag,0);
1951     }
1952     else
1953     {
1954 	i->tag = swf_InsertTag(i->tag,ST_DEFINEBUTTON2);
1955 	swf_SetU16(i->tag,buttonid); //id
1956 	swf_ButtonSetFlags(i->tag, 0); //menu=no
1957 	swf_ButtonSetRecord(i->tag,0x01,myshapeid,i->depth,0,0);
1958 	swf_ButtonSetRecord(i->tag,0x02,myshapeid2,i->depth,0,0);
1959 	swf_ButtonSetRecord(i->tag,0x04,myshapeid2,i->depth,0,0);
1960 	swf_ButtonSetRecord(i->tag,0x08,myshapeid,i->depth,0,0);
1961 	swf_SetU8(i->tag,0); // end of button records
1962         swf_ButtonSetCondition(i->tag, BC_IDLE_OVERUP);
1963 	swf_ActionSet(i->tag,actions1);
1964 	if(actions2) {
1965 	    swf_ButtonSetCondition(i->tag, BC_OVERUP_IDLE);
1966 	    swf_ActionSet(i->tag,actions2);
1967 	    swf_SetU8(i->tag,0);
1968 	    swf_ButtonPostProcess(i->tag, 2);
1969 	} else {
1970 	    swf_SetU8(i->tag,0);
1971             swf_ButtonPostProcess(i->tag, 1);
1972 	}
1973     }
1974 
1975     char buf[80];
1976     char*buf2 = 0;
1977     const char* name = 0;
1978     if(i->config_linknameurl) {
1979         buf2 = malloc(strlen(type)+strlen(url)+2);
1980         sprintf(buf2, "%s:%s", type, url);
1981         name = buf2;
1982     } else {
1983         name = buf;
1984         sprintf(buf, "button%d", buttonid);
1985     }
1986 
1987     msg("<trace> Placing link ID %d", buttonid);
1988     i->tag = swf_InsertTag(i->tag,ST_PLACEOBJECT2);
1989 
1990     if(posx!=0 || posy!=0) {
1991         SPOINT p;
1992         p.x = (int)(posx*20);
1993         p.y = (int)(posy*20);
1994         p = swf_TurnPoint(p, &i->page_matrix);
1995 	MATRIX m;
1996         m = i->page_matrix;
1997         m.tx = p.x;
1998         m.ty = p.y;
1999 	swf_ObjectPlace(i->tag, buttonid, getNewDepth(dev),&m,0,(U8*)name);
2000     } else {
2001 	swf_ObjectPlace(i->tag, buttonid, getNewDepth(dev),&i->page_matrix,0,(U8*)name);
2002     }
2003 
2004     if(buf2)
2005 	free(buf2);
2006 }
2007 
2008 
2009 ///////////
2010 /*
2011 for(t=0;t<picpos;t++)
2012       {
2013 	  if(pic_xids[t] == xid &&
2014 	     pic_yids[t] == yid) {
2015 	      width = pic_width[t];
2016 	      height = pic_height[t];
2017 	      found = t;break;
2018 	  }
2019       }
2020 	  pic_ids[picpos] = swfoutput_drawimagelosslessN(&output, pic, pal, width, height, x1,y1,x2,y2,x3,y3,x4,y4, numpalette);
2021 	  pic_xids[picpos] = xid;
2022 	  pic_yids[picpos] = yid;
2023 	  pic_width[picpos] = width;
2024 	  pic_height[picpos] = height;
2025 	  if(picpos<1024)
2026 	      picpos++;
2027 	    pic[width*y+x] = buf[0];
2028 	    xid+=x*buf[0]+1;
2029 	    yid+=y*buf[0]*3+1;
2030 
2031 	    xid += pal[1].r*3 + pal[1].g*11 + pal[1].b*17;
2032       yid += pal[1].r*7 + pal[1].g*5 + pal[1].b*23;
2033 
2034       int xid = 0;
2035       int yid = 0;
2036 	  xid += x*r+x*b*3+x*g*7+x*a*11;
2037 	  yid += y*r*3+y*b*17+y*g*19+y*a*11;
2038       int t,found = -1;
2039       for(t=0;t<picpos;t++)
2040       {
2041 	  if(pic_xids[t] == xid &&
2042 	     pic_yids[t] == yid) {
2043 	      found = t;break;
2044 	  }
2045       }
2046       if(found<0) {
2047 */
2048 ///////////
2049 
2050 
swf_setparameter(gfxdevice_t * dev,const char * name,const char * value)2051 int swf_setparameter(gfxdevice_t*dev, const char*name, const char*value)
2052 {
2053     swfoutput_internal*i = (swfoutput_internal*)dev->internal;
2054 
2055     msg("<trace> swfdevice: %s=%s", name, value);
2056     if(!strcmp(name, "jpegsubpixels")) {
2057 	i->config_jpegsubpixels = atof(value);
2058     } else if(!strcmp(name, "ppmsubpixels")) {
2059 	i->config_ppmsubpixels = atof(value);
2060     } else if(!strcmp(name, "subpixels")) {
2061 	i->config_ppmsubpixels = i->config_jpegsubpixels = atof(value);
2062     } else if(!strcmp(name, "drawonlyshapes")) {
2063 	i->config_drawonlyshapes = atoi(value);
2064     } else if(!strcmp(name, "ignoredraworder")) {
2065 	i->config_ignoredraworder = atoi(value);
2066     } else if(!strcmp(name, "mark")) {
2067 	if(!value || !value[0]) {
2068 	    if(i->mark) free(i->mark);
2069 	    i->mark = 0;
2070 	} else {
2071 	    int t;
2072 	    i->mark = strdup("...");
2073 	    for(t=0;t<3;t++) if(value[t]) i->mark[t] = value[t];
2074 	}
2075     } else if(!strcmp(name, "filloverlap")) {
2076 	i->config_filloverlap = atoi(value);
2077     } else if(!strcmp(name, "local_with_network")) {
2078 	i->config_local_with_network = atoi(value);
2079 	i->config_local_with_filesystem = !i->config_local_with_network;
2080     } else if(!strcmp(name, "local_with_filesystem")) {
2081 	i->config_local_with_filesystem = atoi(value);
2082 	i->config_local_with_network = !i->config_local_with_filesystem;
2083     } else if(!strcmp(name, "linksopennewwindow")) {
2084 	i->config_opennewwindow = atoi(value);
2085     } else if(!strcmp(name, "opennewwindow")) {
2086 	i->config_opennewwindow = atoi(value);
2087     } else if(!strcmp(name, "storeallcharacters")) {
2088 	i->config_storeallcharacters = atoi(value);
2089     } else if(!strcmp(name, "enablezlib")) {
2090 	i->config_enablezlib = atoi(value);
2091     } else if(!strcmp(name, "bboxvars")) {
2092 	i->config_bboxvars = atoi(value);
2093     } else if(!strcmp(name, "dots")) {
2094 	i->config_dots = atoi(value);
2095     } else if(!strcmp(name, "frameresets")) {
2096 	i->config_frameresets = atoi(value);
2097     } else if(!strcmp(name, "showclipshapes")) {
2098 	i->config_showclipshapes = atoi(value);
2099     } else if(!strcmp(name, "reordertags")) {
2100 	i->config_reordertags = atoi(value);
2101     } else if(!strcmp(name, "internallinkfunction")) {
2102 	i->config_internallinkfunction = strdup(value);
2103     } else if(!strcmp(name, "externallinkfunction")) {
2104 	i->config_externallinkfunction = strdup(value);
2105     } else if(!strcmp(name, "linkfunction")) { //sets both internallinkfunction and externallinkfunction
2106 	i->config_internallinkfunction = strdup(value);
2107 	i->config_externallinkfunction = strdup(value);
2108     } else if(!strcmp(name, "disable_polygon_conversion")) {
2109 	i->config_disable_polygon_conversion = atoi(value);
2110     } else if(!strcmp(name, "normalize_polygon_positions")) {
2111 	i->config_normalize_polygon_positions = atoi(value);
2112     } else if(!strcmp(name, "wxwindowparams")) {
2113 	i->config_watermark = atoi(value);
2114     } else if(!strcmp(name, "insertstop")) {
2115 	i->config_insertstoptag = atoi(value);
2116     } else if(!strcmp(name, "protect")) {
2117 	i->config_protect = atoi(value);
2118 	if(i->config_protect && i->tag) {
2119 	    i->tag = swf_InsertTag(i->tag, ST_PROTECT);
2120 	}
2121     } else if(!strcmp(name, "flashversion")) {
2122 	i->config_flashversion = atoi(value);
2123 	if(i->swf) {
2124 	    i->swf->fileVersion = i->config_flashversion;
2125 	}
2126     } else if(!strcmp(name, "framerate")) {
2127 	i->config_framerate = atof(value);
2128 	if(i->swf) {
2129 	    i->swf->frameRate = i->config_framerate*0x100;
2130 	}
2131     } else if(!strcmp(name, "minlinewidth")) {
2132 	i->config_minlinewidth = atof(value);
2133     } else if(!strcmp(name, "remove_small_polygons")) {
2134 	i->config_remove_small_polygons = atof(value);
2135     } else if(!strcmp(name, "caplinewidth")) {
2136 	i->config_caplinewidth = atof(value);
2137     } else if(!strcmp(name, "linktarget")) {
2138 	i->config_linktarget = strdup(value);
2139     } else if(!strcmp(name, "invisibletexttofront")) {
2140 	i->config_invisibletexttofront = atoi(value);
2141     } else if(!strcmp(name, "noclips")) {
2142 	i->config_noclips = atoi(value);
2143     } else if(!strcmp(name, "dumpfonts")) {
2144 	i->config_dumpfonts = atoi(value);
2145     } else if(!strcmp(name, "override_line_widths")) {
2146 	i->config_override_line_widths = atof(value);
2147     } else if(!strcmp(name, "animate")) {
2148 	i->config_animate = atoi(value);
2149 	i->config_framerate = 25;
2150     } else if(!strcmp(name, "linknameurl")) {
2151 	i->config_linknameurl = atoi(value);
2152     } else if(!strcmp(name, "showimages")) {
2153 	i->config_showimages = atoi(value);
2154     } else if(!strcmp(name, "disablelinks")) {
2155 	i->config_disablelinks = atoi(value);
2156     } else if(!strcmp(name, "simpleviewer")) {
2157 	i->config_simpleviewer = atoi(value);
2158     } else if(!strcmp(name, "next_bitmap_is_jpeg")) {
2159 	i->jpeg = 1;
2160     } else if(!strcmp(name, "jpegquality")) {
2161 	int val = atoi(value);
2162 	if(val<0) val=0;
2163 	if(val>101) val=101;
2164 	i->config_jpegquality = val;
2165     } else if(!strcmp(name, "splinequality")) {
2166 	int v = atoi(value);
2167 	v = 500-(v*5); // 100% = 0.25 pixel, 0% = 25 pixel
2168 	if(v<1) v = 1;
2169 	i->config_splinemaxerror = v;
2170     } else if(!strcmp(name, "fontquality")) {
2171 	int v = atoi(value);
2172 	v = 500-(v*5); // 100% = 0.25 pixel, 0% = 25 pixel
2173 	if(v<1) v = 1;
2174 	i->config_fontsplinemaxerror = v;
2175     } else if(!strcmp(name, "linkcolor")) {
2176 	if(strlen(value)!=8) {
2177 	    fprintf(stderr, "Unknown format for option 'linkcolor'. (%s <-> RRGGBBAA)\n", value);
2178 	    return 1;
2179 	}
2180 #	define NIBBLE(s) (((s)>='0' && (s)<='9')?((s)-'0'):((s)&0x0f)+9)
2181 	i->config_linkcolor.r = NIBBLE(value[0])<<4 | NIBBLE(value[1]);
2182 	i->config_linkcolor.g = NIBBLE(value[2])<<4 | NIBBLE(value[3]);
2183 	i->config_linkcolor.b = NIBBLE(value[4])<<4 | NIBBLE(value[5]);
2184 	i->config_linkcolor.a = NIBBLE(value[6])<<4 | NIBBLE(value[7]);
2185     } else if(!strcmp(name, "help")) {
2186 	printf("\nSWF layer options:\n");
2187         printf("jpegsubpixels=<pixels>      resolution adjustment for jpeg images (same as jpegdpi, but in pixels)\n");
2188         printf("ppmsubpixels=<pixels        resolution adjustment for  lossless images (same as ppmdpi, but in pixels)\n");
2189         printf("subpixels=<pixels>          shortcut for setting both jpegsubpixels and ppmsubpixels\n");
2190         printf("drawonlyshapes              convert everything to shapes (currently broken)\n");
2191         printf("ignoredraworder             allow to perform a few optimizations for creating smaller SWFs\n");
2192         printf("linksopennewwindow          make links open a new browser window\n");
2193         printf("linktarget                  target window name of new links\n");
2194         printf("linkcolor=<color)           color of links (format: RRGGBBAA)\n");
2195         printf("linknameurl		    Link buttons will be named like the URL they refer to (handy for iterating through links with actionscript)\n");
2196         printf("storeallcharacters          don't reduce the fonts to used characters in the output file\n");
2197         printf("enablezlib                  switch on zlib compression (also done if flashversion>=6)\n");
2198         printf("bboxvars                    store the bounding box of the SWF file in actionscript variables\n");
2199         printf("dots                        Take care to handle dots correctly\n");
2200         printf("reordertags=0/1             (default: 1) perform some tag optimizations\n");
2201         printf("internallinkfunction=<name> when the user clicks a internal link (to a different page) in the converted file, this actionscript function is called\n");
2202         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");
2203         printf("disable_polygon_conversion  never convert strokes to polygons (will remove capstyles and joint styles)\n");
2204         printf("caplinewidth=<width>        the minimum thichness a line needs to have so that capstyles become visible (and are converted)\n");
2205         printf("insertstop                  put an ActionScript \"STOP\" tag in every frame\n");
2206         printf("protect                     add a \"protect\" tag to the file, to prevent loading in the Flash editor\n");
2207         printf("flashversion=<version>      the SWF fileversion (6)\n");
2208         printf("framerate=<fps>		    SWF framerate\n");
2209         printf("minlinewidth=<width>        convert horizontal/vertical boxes smaller than this width to lines (0.05) \n");
2210         printf("simpleviewer                Add next/previous buttons to the SWF\n");
2211         printf("animate                     insert a showframe tag after each placeobject (animate draw order of PDF files)\n");
2212         printf("jpegquality=<quality>       set compression quality of jpeg images\n");
2213 	printf("splinequality=<value>       Set the quality of spline convertion to value (0-100, default: 100).\n");
2214 	printf("disablelinks                Disable links.\n");
2215     } else {
2216 	return 0;
2217     }
2218     return 1;
2219 }
2220 
2221 // --------------------------------------------------------------------
2222 
gfxcxform_to_cxform(gfxcxform_t * c)2223 static CXFORM gfxcxform_to_cxform(gfxcxform_t* c)
2224 {
2225     CXFORM cx;
2226     swf_GetCXForm(0, &cx, 1);
2227     if(!c)
2228 	return cx;
2229     if(c->rg!=0 || c->rb!=0 || c->ra!=0 ||
2230        c->gr!=0 || c->gb!=0 || c->ga!=0 ||
2231        c->br!=0 || c->bg!=0 || c->ba!=0 ||
2232        c->ar!=0 || c->ag!=0 || c->ab!=0)
2233 	msg("<warning> CXForm not SWF-compatible");
2234 
2235     cx.a0 = (S16)(c->aa*256);
2236     cx.r0 = (S16)(c->rr*256);
2237     cx.g0 = (S16)(c->gg*256);
2238     cx.b0 = (S16)(c->bb*256);
2239     cx.a1 = c->ta;
2240     cx.r1 = c->tr;
2241     cx.g1 = c->tg;
2242     cx.b1 = c->tb;
2243     return cx;
2244 }
2245 
2246 /* TODO */
imageInCache(gfxdevice_t * dev,void * data,int width,int height)2247 static int imageInCache(gfxdevice_t*dev, void*data, int width, int height)
2248 {
2249     return -1;
2250 }
addImageToCache(gfxdevice_t * dev,void * data,int width,int height)2251 static void addImageToCache(gfxdevice_t*dev, void*data, int width, int height)
2252 {
2253 }
2254 
add_image(swfoutput_internal * i,gfximage_t * img,int targetwidth,int targetheight,int * newwidth,int * newheight)2255 static int add_image(swfoutput_internal*i, gfximage_t*img, int targetwidth, int targetheight, int* newwidth, int* newheight)
2256 {
2257     gfxdevice_t*dev = i->dev;
2258     RGBA*newpic = 0;
2259     RGBA*mem = (RGBA*)img->data;
2260 
2261     int sizex = img->width;
2262     int sizey = img->height;
2263     int is_jpeg = i->jpeg;
2264     i->jpeg = 0;
2265 
2266     int newsizex=sizex, newsizey=sizey;
2267 
2268     /// {
2269     if(is_jpeg && i->config_jpegsubpixels) {
2270 	newsizex = (int)(targetwidth*i->config_jpegsubpixels + 0.5);
2271 	newsizey = (int)(targetheight*i->config_jpegsubpixels + 0.5);
2272     } else if(!is_jpeg && i->config_ppmsubpixels) {
2273 	newsizex = (int)(targetwidth*i->config_ppmsubpixels + 0.5);
2274 	newsizey = (int)(targetheight*i->config_ppmsubpixels + 0.5);
2275     }
2276     /// }
2277 
2278     if(sizex<=0 || sizey<=0)
2279 	return -1;
2280     if(newsizex<=0)
2281 	newsizex = 1;
2282     if(newsizey<=0)
2283 	newsizey = 1;
2284 
2285     /* TODO: cache images */
2286 
2287     if(newsizex<sizex || newsizey<sizey) {
2288 	msg("<verbose> Scaling %dx%d image to %dx%d", sizex, sizey, newsizex, newsizey);
2289 	gfximage_t*ni = gfximage_rescale(img, newsizex, newsizey);
2290 	newpic = (RGBA*)ni->data;
2291 	free(ni);
2292 	*newwidth = sizex = newsizex;
2293 	*newheight  = sizey = newsizey;
2294 	mem = newpic;
2295     } else {
2296 	*newwidth = newsizex = sizex;
2297 	*newheight = newsizey  = sizey;
2298     }
2299 
2300     int num_colors = swf_ImageGetNumberOfPaletteEntries(mem,sizex,sizey,0);
2301     int has_alpha = swf_ImageHasAlpha(mem,sizex,sizey);
2302 
2303     msg("<verbose> Drawing %dx%d %s%simage (id %d) at size %dx%d (%dx%d), %s%d colors",
2304 	    sizex, sizey,
2305 	    has_alpha?(has_alpha==2?"semi-transparent ":"transparent "):"",
2306 	    is_jpeg?"jpeg-":"", i->currentswfid+1,
2307 	    newsizex, newsizey,
2308 	    targetwidth, targetheight,
2309 	    /*newsizex, newsizey,*/
2310 	    num_colors>256?">":"", num_colors>256?256:num_colors);
2311 
2312     /*RGBA* pal = (RGBA*)rfx_alloc(sizeof(RGBA)*num_colors);
2313     swf_ImageGetNumberOfPaletteEntries(mem,sizex,sizey,pal);
2314     int t;
2315     for(t=0;t<num_colors;t++) {
2316 	printf("%02x%02x%02x%02x ",
2317 		pal[t].r, pal[t].g, pal[t].b, pal[t].a);
2318 	if((t&7)==7)
2319 	    printf("\n");
2320     }
2321     printf("\n");*/
2322 
2323     int bitid = -1;
2324     int cacheid = imageInCache(dev, mem, sizex, sizey);
2325 
2326     if(cacheid<=0) {
2327 	bitid = getNewID(dev);
2328 
2329 	i->tag = swf_AddImage(i->tag, bitid, mem, sizex, sizey, i->config_jpegquality);
2330 	addImageToCache(dev, mem, sizex, sizey);
2331     } else {
2332 	bitid = cacheid;
2333     }
2334 
2335     if(newpic)
2336 	free(newpic);
2337     return bitid;
2338 }
2339 
line_is_empty(gfxline_t * line)2340 int line_is_empty(gfxline_t*line)
2341 {
2342     while(line) {
2343 	if(line->type != gfx_moveTo)
2344 	    return 0;
2345 	line = line->next;
2346     }
2347     return 1;
2348 }
2349 
gfxline_getSWFbbox(gfxline_t * line)2350 static SRECT gfxline_getSWFbbox(gfxline_t*line)
2351 {
2352     gfxbbox_t bbox = gfxline_getbbox(line);
2353     SRECT r;
2354     r.xmin = (int)(bbox.xmin*20);
2355     r.ymin = (int)(bbox.ymin*20);
2356     r.xmax = (int)(bbox.xmax*20);
2357     r.ymax = (int)(bbox.ymax*20);
2358     return r;
2359 }
2360 
swf_fillbitmap(gfxdevice_t * dev,gfxline_t * line,gfximage_t * img,gfxmatrix_t * matrix,gfxcxform_t * cxform)2361 static void swf_fillbitmap(gfxdevice_t*dev, gfxline_t*line, gfximage_t*img, gfxmatrix_t*matrix, gfxcxform_t*cxform)
2362 {
2363     swfoutput_internal*i = (swfoutput_internal*)dev->internal;
2364 
2365     if(line_is_empty(line))
2366 	return;
2367 
2368     endshape(dev);
2369     endtext(dev);
2370 
2371     int targetx = (int)(sqrt(matrix->m00*matrix->m00 + matrix->m01*matrix->m01)*img->width);
2372     int targety = (int)(sqrt(matrix->m10*matrix->m10 + matrix->m11*matrix->m11)*img->height);
2373 
2374     int newwidth=0,newheight=0;
2375     int bitid = add_image(i, img, targetx, targety, &newwidth, &newheight);
2376     if(bitid<0)
2377 	return;
2378     double fx = (double)img->width / (double)newwidth;
2379     double fy = (double)img->height / (double)newheight;
2380 
2381     MATRIX m;
2382     m.sx = (int)(65536*20*matrix->m00*fx); m.r1 = (int)(65536*20*matrix->m10*fy);
2383     m.r0 = (int)(65536*20*matrix->m01*fx); m.sy = (int)(65536*20*matrix->m11*fy);
2384     m.tx = (int)(matrix->tx*20);
2385     m.ty = (int)(matrix->ty*20);
2386 
2387     /* shape */
2388     int myshapeid = getNewID(dev);
2389     i->tag = swf_InsertTag(i->tag,ST_DEFINESHAPE);
2390     SHAPE*shape;
2391     swf_ShapeNew(&shape);
2392     int fsid = swf_ShapeAddBitmapFillStyle(shape,&m,bitid,1);
2393     int lsid = 0;
2394     if(i->config_showimages) {
2395 	RGBA pink = {255,255,0,255};
2396 	lsid = swf_ShapeAddLineStyle(shape, 20, &pink);
2397     }
2398     swf_SetU16(i->tag, myshapeid);
2399     SRECT r = gfxline_getSWFbbox(line);
2400     r = swf_ClipRect(i->pagebbox, r);
2401     swf_SetRect(i->tag,&r);
2402     swf_SetShapeStyles(i->tag,shape);
2403     swf_ShapeCountBits(shape,NULL,NULL);
2404     swf_SetShapeBits(i->tag,shape);
2405     swf_ShapeSetAll(i->tag,shape,UNDEFINED_COORD,UNDEFINED_COORD,lsid,fsid,0);
2406     i->swflastx = i->swflasty = UNDEFINED_COORD;
2407     drawgfxline(dev, line, 1);
2408     swf_ShapeSetEnd(i->tag);
2409     swf_ShapeFree(shape);
2410 
2411     msg("<trace> Placing image, shape ID %d, bitmap ID %d", myshapeid, bitid);
2412     i->tag = swf_InsertTag(i->tag,ST_PLACEOBJECT2);
2413     CXFORM cxform2 = gfxcxform_to_cxform(cxform);
2414     swf_ObjectPlace(i->tag,myshapeid,getNewDepth(dev),&i->page_matrix,&cxform2,NULL);
2415 }
2416 
2417 static RGBA col_purple = {255,255,0,255};
drawoutline(gfxdevice_t * dev,gfxline_t * line)2418 static void drawoutline(gfxdevice_t*dev, gfxline_t*line)
2419 {
2420     swfoutput_internal*i = (swfoutput_internal*)dev->internal;
2421 
2422     int myshapeid = getNewID(dev);
2423     i->tag = swf_InsertTag(i->tag,ST_DEFINESHAPE3);
2424 
2425     SHAPE*shape;
2426     swf_ShapeNew(&shape);
2427     int lsid = swf_ShapeAddLineStyle(shape,60,&col_purple);
2428 
2429     swf_SetU16(i->tag,myshapeid);
2430     SRECT r = gfxline_getSWFbbox(line);
2431     r = swf_ClipRect(i->pagebbox, r);
2432     swf_SetRect(i->tag,&r);
2433     swf_SetShapeStyles(i->tag,shape);
2434     swf_ShapeCountBits(shape,NULL,NULL);
2435     swf_SetShapeBits(i->tag,shape);
2436     swf_ShapeSetAll(i->tag,shape,UNDEFINED_COORD,UNDEFINED_COORD,lsid,0,0);
2437     drawgfxline(dev, line, 1);
2438     swf_ShapeSetEnd(i->tag);
2439     swf_ShapeFree(shape);
2440 
2441     i->tag = swf_InsertTag(i->tag,ST_PLACEOBJECT2);
2442     swf_ObjectPlace(i->tag, myshapeid, getNewDepth(dev), 0,0,0);
2443 }
2444 
swf_startclip(gfxdevice_t * dev,gfxline_t * line)2445 static void swf_startclip(gfxdevice_t*dev, gfxline_t*line)
2446 {
2447     swfoutput_internal*i = (swfoutput_internal*)dev->internal;
2448     if(i->config_noclips)
2449 	return;
2450 
2451     endtext(dev);
2452     endshape(dev);
2453 
2454     if(i->clippos >= 127)
2455     {
2456         msg("<warning> Too many clip levels.");
2457         i->clippos --;
2458     }
2459 
2460     if(i->config_showclipshapes) {
2461         i->stored_clipshapes = gfxline_append(i->stored_clipshapes, gfxline_clone(line));
2462     }
2463 
2464     int myshapeid = getNewID(dev);
2465     i->tag = swf_InsertTag(i->tag,ST_DEFINESHAPE3);
2466     RGBA col;
2467     memset(&col, 0, sizeof(RGBA));
2468     col.a = 255;
2469     SHAPE*shape;
2470     swf_ShapeNew(&shape);
2471     int fsid = swf_ShapeAddSolidFillStyle(shape,&col);
2472     if(i->mark) {
2473 	RGBA markcol = {0,i->mark[0],i->mark[1],i->mark[2]};
2474 	swf_ShapeAddSolidFillStyle(shape,&markcol);
2475     }
2476     swf_SetU16(i->tag,myshapeid);
2477     SRECT r = gfxline_getSWFbbox(line);
2478     r = swf_ClipRect(i->pagebbox, r);
2479     swf_SetRect(i->tag,&r);
2480     swf_SetShapeStyles(i->tag,shape);
2481     swf_ShapeCountBits(shape,NULL,NULL);
2482     swf_SetShapeBits(i->tag,shape);
2483     swf_ShapeSetAll(i->tag,shape,UNDEFINED_COORD,UNDEFINED_COORD,0,fsid,0);
2484     i->swflastx = i->swflasty = UNDEFINED_COORD;
2485     i->shapeisempty = 1;
2486     drawgfxline(dev, line, 1);
2487     if(i->shapeisempty) {
2488 	/* an empty clip shape is equivalent to a shape with no area */
2489 	int x = line?line->x:0;
2490 	int y = line?line->y:0;
2491 	moveto(dev, i->tag, x,y);
2492 	lineto(dev, i->tag, x,y);
2493 	lineto(dev, i->tag, x,y);
2494     }
2495     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)) {
2496 	if(i->config_watermark) {
2497 	    gfxbbox_t r; r.xmin = r.ymin = 0;r.xmax = i->max_x;r.ymax = i->max_y;
2498 	    draw_watermark(dev, r, 1);
2499 	}
2500     }
2501     swf_ShapeSetEnd(i->tag);
2502     swf_ShapeFree(shape);
2503 
2504     /* TODO: remember the bbox, and check all shapes against it */
2505 
2506     msg("<trace> Placing clip ID %d", myshapeid);
2507     i->tag = swf_InsertTag(i->tag,ST_PLACEOBJECT2);
2508     i->cliptags[i->clippos] = i->tag;
2509     i->clipshapes[i->clippos] = myshapeid;
2510     i->clipdepths[i->clippos] = getNewDepth(dev);
2511     i->clippos++;
2512 }
2513 
swf_endclip(gfxdevice_t * dev)2514 static void swf_endclip(gfxdevice_t*dev)
2515 {
2516     swfoutput_internal*i = (swfoutput_internal*)dev->internal;
2517     if(i->config_noclips)
2518 	return;
2519     if(i->textmode)
2520 	endtext(dev);
2521     if(i->shapeid>=0)
2522 	endshape(dev);
2523 
2524     if(!i->clippos) {
2525         msg("<error> Invalid end of clipping region");
2526         return;
2527     }
2528     i->clippos--;
2529     /*swf_ObjectPlaceClip(i->cliptags[i->clippos],i->clipshapes[i->clippos],i->clipdepths[i->clippos],&i->page_matrix,NULL,NULL,
2530 	    / * clip to depth: * / i->depth <= i->clipdepths[i->clippos]? i->depth : i->depth - 1);
2531     i->depth ++;*/
2532     swf_ObjectPlaceClip(i->cliptags[i->clippos],i->clipshapes[i->clippos],i->clipdepths[i->clippos],&i->page_matrix,NULL,NULL,i->depth);
2533 }
gfxline_type(gfxline_t * line)2534 static int gfxline_type(gfxline_t*line)
2535 {
2536     int tmplines=0;
2537     int tmpsplines=0;
2538     int lines=0;
2539     int splines=0;
2540     int haszerosegments=0;
2541     int length=0;
2542     while(line) {
2543 	if(line->type == gfx_moveTo) {
2544 	    tmplines=0;
2545 	    tmpsplines=0;
2546 	} else if(line->type == gfx_lineTo) {
2547 	    tmplines++;
2548 	    if(tmplines>lines)
2549 		lines=tmplines;
2550 	} else if(line->type == gfx_splineTo) {
2551 	    tmpsplines++;
2552 	    if(tmpsplines>lines)
2553 		splines=tmpsplines;
2554 	}
2555 	length++;
2556 	line = line->next;
2557     }
2558     if(length>400)
2559 	return 5;
2560     if(lines==0 && splines==0) return 0;
2561     else if(lines==1 && splines==0) return 1;
2562     else if(lines==0 && splines==1) return 2;
2563     else if(splines==0) return 3;
2564     else return 4;
2565 }
2566 
gfxline_has_dots(gfxline_t * line)2567 static int gfxline_has_dots(gfxline_t*line)
2568 {
2569     int tmplines=0;
2570     double x=0,y=0;
2571     double dist = 0;
2572     int isline = 0;
2573     int short_gap = 0;
2574     while(line) {
2575 	if(line->type == gfx_moveTo) {
2576 	    /* test the length of the preceding line, and assume it is a dot if
2577 	       it's length is less than 1.0. But *only* if there's a noticable
2578 	       gap between the previous line and the next moveTo. (I've come
2579 	       across a PDF where thousands of "dots" were stringed together,
2580 	       forming a line) */
2581 	    int last_short_gap = short_gap;
2582 	    if((fabs(line->x - x) + fabs(line->y - y)) < 1.0) {
2583 		short_gap = 1;
2584 	    } else {
2585 		short_gap = 0;
2586 	    }
2587 	    if(isline && dist < 1 && !short_gap && !last_short_gap) {
2588 		return 1;
2589 	    }
2590 	    dist = 0;
2591 	    isline = 0;
2592 	} else if(line->type == gfx_lineTo) {
2593 	    dist += fabs(line->x - x) + fabs(line->y - y);
2594 	    isline = 1;
2595 	} else if(line->type == gfx_splineTo) {
2596 	    dist += fabs(line->sx - x) + fabs(line->sy - y) +
2597 		    fabs(line->x - line->sx) + fabs(line->y - line->sy);
2598 	    isline = 1;
2599 	}
2600 	x = line->x;
2601 	y = line->y;
2602 	line = line->next;
2603     }
2604     if(isline && dist < 1 && !short_gap) {
2605 	return 1;
2606     }
2607     return 0;
2608 }
2609 
gfxline_fix_short_edges(gfxline_t * line)2610 static int gfxline_fix_short_edges(gfxline_t*line)
2611 {
2612     double x,y;
2613     while(line) {
2614 	if(line->type == gfx_lineTo) {
2615 	    if(fabs(line->x - x) + fabs(line->y - y) < 0.01) {
2616 		line->x += 0.01;
2617 	    }
2618 	} else if(line->type == gfx_splineTo) {
2619 	    if(fabs(line->sx - x) + fabs(line->sy - y) +
2620 	       fabs(line->x - line->sx) + fabs(line->y - line->sy) < 0.01) {
2621 		line->x += 0.01;
2622 	    }
2623 	}
2624 	x = line->x;
2625 	y = line->y;
2626 	line = line->next;
2627     }
2628     return 0;
2629 }
2630 
is_inside_page(gfxdevice_t * dev,gfxcoord_t x,gfxcoord_t y)2631 static char is_inside_page(gfxdevice_t*dev, gfxcoord_t x, gfxcoord_t y)
2632 {
2633     swfoutput_internal*i = (swfoutput_internal*)dev->internal;
2634     if(x<i->min_x || x>i->max_x) return 0;
2635     if(y<i->min_y || y>i->max_y) return 0;
2636     return 1;
2637 }
2638 
gfxline_move(gfxline_t * line,double x,double y)2639 gfxline_t* gfxline_move(gfxline_t*line, double x, double y)
2640 {
2641     gfxline_t*l = line = gfxline_clone(line);
2642 
2643     while(l) {
2644 	l->x += x;
2645 	l->y += y;
2646 	l->sx += x;
2647 	l->sy += y;
2648 	l = l->next;
2649     }
2650     return line;
2651 }
2652 
2653 //#define NORMALIZE_POLYGON_POSITIONS
2654 
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)2655 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)
2656 {
2657     swfoutput_internal*i = (swfoutput_internal*)dev->internal;
2658     if(line_is_empty(line))
2659 	return;
2660     int type = gfxline_type(line);
2661     int has_dots = gfxline_has_dots(line);
2662     gfxbbox_t r = gfxline_getbbox(line);
2663     int is_outside_page = !is_inside_page(dev, r.xmin, r.ymin) || !is_inside_page(dev, r.xmax, r.ymax);
2664 
2665     if(i->config_override_line_widths) {
2666 	width = i->config_override_line_widths;
2667     }
2668 
2669     /* TODO: * split line into segments, and perform this check for all segments */
2670 
2671     if(i->config_disable_polygon_conversion || /*type>=5 ||*/
2672        (!has_dots &&
2673         (width <= i->config_caplinewidth
2674         || (cap_style == gfx_capRound && joint_style == gfx_joinRound)
2675         || (cap_style == gfx_capRound && type<=2))))
2676     {
2677         // ...
2678     } else {
2679 	/* convert line to polygon */
2680 	msg("<trace> draw as polygon, type=%d dots=%d", type, has_dots);
2681 	if(has_dots)
2682 	    gfxline_fix_short_edges(line);
2683 	/* we need to convert the line into a polygon */
2684 	gfxpoly_t* poly = gfxpoly_from_stroke(line, width, cap_style, joint_style, miterLimit, DEFAULT_GRID);
2685 	gfxline_t*gfxline = gfxline_from_gfxpoly(poly);
2686 	dev->fill(dev, gfxline, color);
2687 	gfxline_free(gfxline);
2688 	gfxpoly_destroy(poly);
2689 	return;
2690     }
2691 
2692     msg("<trace> draw as stroke, type=%d dots=%d", type, has_dots);
2693     endtext(dev);
2694 
2695     if(i->config_normalize_polygon_positions) {
2696 	endshape(dev);
2697 	double startx = 0, starty = 0;
2698 	if(line && line->type == gfx_moveTo) {
2699 	    startx = line->x;
2700 	    starty = line->y;
2701 	}
2702 	line = gfxline_move(line, -startx, -starty);
2703 	i->shapeposx = (int)(startx*20);
2704 	i->shapeposy = (int)(starty*20);
2705     }
2706 
2707     swfoutput_setstrokecolor(dev, color->r, color->g, color->b, color->a);
2708     swfoutput_setlinewidth(dev, width);
2709     startshape(dev);
2710     stopFill(dev);
2711     drawgfxline(dev, line, 0);
2712 
2713     if(i->config_normalize_polygon_positions) {
2714 	free(line); //account for _move
2715     }
2716 
2717 }
2718 
swf_fill(gfxdevice_t * dev,gfxline_t * line,gfxcolor_t * color)2719 static void swf_fill(gfxdevice_t*dev, gfxline_t*line, gfxcolor_t*color)
2720 {
2721     swfoutput_internal*i = (swfoutput_internal*)dev->internal;
2722     if(line_is_empty(line))
2723 	return;
2724     if(!color->a)
2725 	return;
2726 
2727     gfxbbox_t r = gfxline_getbbox(line);
2728     int is_outside_page = !is_inside_page(dev, r.xmin, r.ymin) || !is_inside_page(dev, r.xmax, r.ymax);
2729 
2730     if(r.xmax - r.xmin < i->config_remove_small_polygons &&
2731        r.ymax - r.ymin < i->config_remove_small_polygons) {
2732 	msg("<verbose> Not drawing %.2fx%.2f polygon", r.xmax - r.xmin, r.ymax - r.ymin);
2733 	return;
2734     }
2735 
2736     endtext(dev);
2737 
2738     if(!i->config_ignoredraworder)
2739 	endshape(dev);
2740 
2741     if(i->config_normalize_polygon_positions) {
2742 	endshape(dev);
2743 	double startx = 0, starty = 0;
2744 	if(line && line->type == gfx_moveTo) {
2745 	    startx = line->x;
2746 	    starty = line->y;
2747 	}
2748 	line = gfxline_move(line, -startx, -starty);
2749 	i->shapeposx = (int)(startx*20);
2750 	i->shapeposy = (int)(starty*20);
2751     }
2752 
2753     swfoutput_setfillcolor(dev, color->r, color->g, color->b, color->a);
2754     startshape(dev);
2755     startFill(dev);
2756     drawgfxline(dev, line, 1);
2757 
2758     if(i->currentswfid==2 && r.xmin==0 && r.ymin==0 && r.xmax==i->max_x && r.ymax==i->max_y) {
2759 	if(i->config_watermark) {
2760 	    draw_watermark(dev, r, 1);
2761 	}
2762     }
2763 
2764     msg("<trace> end of swf_fill (shapeid=%d)", i->shapeid);
2765 
2766     if(i->config_normalize_polygon_positions) {
2767 	free(line); //account for _move
2768     }
2769 }
2770 
gfxgradient_to_GRADIENT(gfxgradient_t * gradient)2771 static GRADIENT* gfxgradient_to_GRADIENT(gfxgradient_t*gradient)
2772 {
2773     int num = 0;
2774     gfxgradient_t*g = gradient;
2775     while(g) {
2776 	num++;
2777 	g = g->next;
2778     }
2779     GRADIENT* swfgradient = malloc(sizeof(GRADIENT));
2780     swfgradient->num = num;
2781     swfgradient->rgba = malloc(sizeof(swfgradient->rgba[0])*num);
2782     swfgradient->ratios = malloc(sizeof(swfgradient->ratios[0])*num);
2783 
2784     g = gradient;
2785     num = 0;
2786     while(g) {
2787 	swfgradient->ratios[num] = g->pos*255;
2788 	swfgradient->rgba[num] = *(RGBA*)&g->color;
2789 	num++;
2790 	g = g->next;
2791     }
2792     return swfgradient;
2793 }
2794 
swf_fillgradient(gfxdevice_t * dev,gfxline_t * line,gfxgradient_t * gradient,gfxgradienttype_t type,gfxmatrix_t * matrix)2795 static void swf_fillgradient(gfxdevice_t*dev, gfxline_t*line, gfxgradient_t*gradient, gfxgradienttype_t type, gfxmatrix_t*matrix)
2796 {
2797     if(line_is_empty(line))
2798 	return;
2799     swfoutput_internal*i = (swfoutput_internal*)dev->internal;
2800 
2801     if(line_is_empty(line))
2802 	return;
2803 
2804     GRADIENT* swfgradient = gfxgradient_to_GRADIENT(gradient);
2805     if(!swfgradient)
2806 	return;
2807 
2808     endshape(dev);
2809     endtext(dev);
2810 
2811     double f = type==gfxgradient_radial?4:4;
2812     MATRIX m;
2813     m.sx = (int)(matrix->m00*20*f); m.r1 = (int)(matrix->m10*20*f);
2814     m.r0 = (int)(matrix->m01*20*f); m.sy = (int)(matrix->m11*20*f);
2815     m.tx = (int)(matrix->tx*20);
2816     m.ty = (int)(matrix->ty*20);
2817 
2818     /* shape */
2819     int myshapeid = getNewID(dev);
2820     i->tag = swf_InsertTag(i->tag, ST_DEFINESHAPE2);
2821     SHAPE*shape;
2822     swf_ShapeNew(&shape);
2823     int fsid = swf_ShapeAddGradientFillStyle(shape,&m,swfgradient,type==gfxgradient_radial);
2824     swf_SetU16(i->tag, myshapeid);
2825     SRECT r = gfxline_getSWFbbox(line);
2826     r = swf_ClipRect(i->pagebbox, r);
2827     swf_SetRect(i->tag,&r);
2828     swf_SetShapeStyles(i->tag,shape);
2829     swf_ShapeCountBits(shape,NULL,NULL);
2830     swf_SetShapeBits(i->tag,shape);
2831     swf_ShapeSetAll(i->tag,shape,UNDEFINED_COORD,UNDEFINED_COORD,0,fsid,0);
2832     i->swflastx = i->swflasty = UNDEFINED_COORD;
2833     drawgfxline(dev, line, 1);
2834     swf_ShapeSetEnd(i->tag);
2835     swf_ShapeFree(shape);
2836 
2837     int depth = getNewDepth(dev);
2838     msg("<trace> Placing gradient, shape ID %d, depth %d", myshapeid, depth);
2839     i->tag = swf_InsertTag(i->tag,ST_PLACEOBJECT2);
2840     swf_ObjectPlace(i->tag,myshapeid,depth,&i->page_matrix,NULL,NULL);
2841 
2842     swf_FreeGradient(swfgradient);free(swfgradient);
2843 }
2844 
gfxfont_to_swffont(gfxfont_t * font,const char * id,int version)2845 static SWFFONT* gfxfont_to_swffont(gfxfont_t*font, const char* id, int version)
2846 {
2847     SWFFONT*swffont = (SWFFONT*)rfx_calloc(sizeof(SWFFONT));
2848     int t;
2849     SRECT bounds = {0,0,0,0};
2850     swffont->id = -1;
2851     swffont->version = version;
2852     swffont->name = (U8*)strdup(id);
2853     swffont->layout = (SWFLAYOUT*)rfx_calloc(sizeof(SWFLAYOUT));
2854     swffont->layout->ascent = 0;
2855     swffont->layout->descent = 0;
2856     swffont->layout->leading = 0;
2857     swffont->layout->bounds = (SRECT*)rfx_calloc(sizeof(SRECT)*font->num_glyphs);
2858     swffont->encoding = FONT_ENCODING_UNICODE;
2859     swffont->numchars = font->num_glyphs;
2860     swffont->maxascii = font->max_unicode;
2861     swffont->ascii2glyph = (int*)rfx_calloc(sizeof(int)*swffont->maxascii);
2862     swffont->glyph2ascii = (U16*)rfx_calloc(sizeof(U16)*swffont->numchars);
2863     swffont->glyph = (SWFGLYPH*)rfx_calloc(sizeof(SWFGLYPH)*swffont->numchars);
2864     swffont->glyphnames = (char**)rfx_calloc(sizeof(char*)*swffont->numchars);
2865 
2866     SRECT max = {0,0,0,0};
2867     for(t=0;t<font->num_glyphs;t++) {
2868 	drawer_t draw;
2869 	gfxline_t*line;
2870 	double advance = 0;
2871 	int u = font->glyphs[t].unicode;
2872 	int s;
2873 	char twice=0;
2874 	for(s=0;s<font->num_glyphs;s++) {
2875 	    if(swffont->glyph2ascii[s]==u)
2876 		twice=1;
2877 	}
2878 	if(u >= 0xd800 || u == 0x0000 || twice) {
2879 	    /* flash 8 flashtype requires unique unicode IDs for each character.
2880 	       We use the Unicode private user area to assign characters, hoping that
2881 	       the font doesn't contain more than 8192 glyphs */
2882 	    u = 0xe000 + (t&0x1fff);
2883 	}
2884 	swffont->glyph2ascii[t] = u;
2885 
2886 	if(font->glyphs[t].name) {
2887 	    swffont->glyphnames[t] = strdup(font->glyphs[t].name);
2888 	} else {
2889 	    swffont->glyphnames[t] = 0;
2890 	}
2891 	advance = font->glyphs[t].advance;
2892 
2893 	swf_Shape01DrawerInit(&draw, 0);
2894 	line = font->glyphs[t].line;
2895 
2896 	const double scale = GLYPH_SCALE;
2897 	while(line) {
2898 	    FPOINT c,to;
2899 	    c.x = line->sx * scale; c.y = -line->sy * scale;
2900 	    //to.x = floor(line->x * scale); to.y = floor(-line->y * scale);
2901 	    to.x = line->x * scale; to.y = -line->y * scale;
2902 
2903 	    /*if(strstr(swffont->name, "BIRNU") && t==90) {
2904 		to.x += 1;
2905 	    }*/
2906 
2907 	    if(line->type == gfx_moveTo) {
2908 		draw.moveTo(&draw, &to);
2909 	    } else if(line->type == gfx_lineTo) {
2910 		draw.lineTo(&draw, &to);
2911 	    } else if(line->type == gfx_splineTo) {
2912 		draw.splineTo(&draw, &c, &to);
2913 	    }
2914 	    line = line->next;
2915 	}
2916 	draw.finish(&draw);
2917 	swffont->glyph[t].shape = swf_ShapeDrawerToShape(&draw);
2918 
2919 	SRECT bbox = swf_ShapeDrawerGetBBox(&draw);
2920 	swf_ExpandRect2(&max, &bbox);
2921 
2922 	swffont->layout->bounds[t] = bbox;
2923 
2924 	if(advance<32768.0/20) {
2925 	    swffont->glyph[t].advance = (int)(advance*20);
2926 	} else {
2927 	    //msg("<warning> Advance value overflow in glyph %d", t);
2928 	    swffont->glyph[t].advance = 32767;
2929 	}
2930 
2931 	draw.dealloc(&draw);
2932 
2933 	swf_ExpandRect2(&bounds, &swffont->layout->bounds[t]);
2934     }
2935 
2936     for(t=0;t<font->num_glyphs;t++) {
2937 	SRECT bbox = swffont->layout->bounds[t];
2938 
2939 	/* if the glyph doesn't have a bounding box, use the
2940 	   combined bounding box (necessary e.g. for space characters) */
2941 	if(!(bbox.xmin|bbox.ymin|bbox.xmax|bbox.ymax)) {
2942 	    swffont->layout->bounds[t] = bbox = max;
2943 	}
2944 
2945 	/* check that the advance value is reasonable, by comparing it
2946 	   with the bounding box */
2947 	if(bbox.xmax>0 && (bbox.xmax*10 < swffont->glyph[t].advance || !swffont->glyph[t].advance)) {
2948 	    if(swffont->glyph[t].advance)
2949 		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);
2950 	    swffont->glyph[t].advance = bbox.xmax;
2951 	}
2952 	//swffont->glyph[t].advance = bbox.xmax - bbox.xmin;
2953     }
2954 
2955 
2956     /* Flash player will use the advance value from the char, and the ascent/descent values
2957        from the layout for text selection.
2958        ascent will extend the char into negative y direction, from the baseline, while descent
2959        will extend in positive y direction, also from the baseline.
2960        The baseline is defined as the y-position zero
2961      */
2962 
2963     swffont->layout->ascent = bounds.ymin<0?-bounds.ymin:0;
2964     swffont->layout->descent = bounds.ymax>0?bounds.ymax:0;
2965     swffont->layout->leading = bounds.ymax - bounds.ymin;
2966 
2967     /* if the font has proper ascent/descent values (>0) and those define
2968        greater (but not overly large) line spacing than what we estimated
2969        from the bounding boxes, use the font's parameters */
2970     if(font->ascent*20 > swffont->layout->ascent && font->ascent*20*2 < swffont->layout->ascent)
2971 	swffont->layout->ascent = font->ascent*20;
2972     if(font->descent*20 > swffont->layout->descent && font->descent*20*2 < swffont->layout->descent)
2973 	swffont->layout->descent = font->descent*20;
2974 
2975     swf_FontSort(swffont);
2976     return swffont;
2977 }
2978 
swf_addfont(gfxdevice_t * dev,gfxfont_t * font)2979 static void swf_addfont(gfxdevice_t*dev, gfxfont_t*font)
2980 {
2981     swfoutput_internal*i = (swfoutput_internal*)dev->internal;
2982 
2983     if(i->swffont && i->swffont->name && !strcmp((char*)i->swffont->name,font->id))
2984 	return; // the requested font is the current font
2985 
2986     fontlist_t*last=0,*l = i->fontlist;
2987     while(l) {
2988 	last = l;
2989 	if(!strcmp((char*)l->swffont->name, font->id)) {
2990 	    return; // we already know this font
2991 	}
2992 	l = l->next;
2993     }
2994     l = (fontlist_t*)rfx_calloc(sizeof(fontlist_t));
2995     l->swffont = gfxfont_to_swffont(font, font->id, (i->config_flashversion>=8 && !NO_FONT3)?3:2);
2996     l->next = 0;
2997     if(last) {
2998 	last->next = l;
2999     } else {
3000 	i->fontlist = l;
3001     }
3002     swf_FontSetID(l->swffont, getNewID(i->dev));
3003 
3004     if(getScreenLogLevel() >= LOGLEVEL_DEBUG)  {
3005 	int iii;
3006 	// print font information
3007 	msg("<debug> Font %s",font->id);
3008 	msg("<debug> |   ID: %d", l->swffont->id);
3009 	msg("<debug> |   Version: %d", l->swffont->version);
3010 	msg("<debug> |   Name: %s", l->swffont->name);
3011 	msg("<debug> |   Numchars: %d", l->swffont->numchars);
3012 	msg("<debug> |   Maxascii: %d", l->swffont->maxascii);
3013 	msg("<debug> |   Style: %d", l->swffont->style);
3014 	msg("<debug> |   Encoding: %d", l->swffont->encoding);
3015 	if(l->swffont->layout) {
3016 	    msg("<debug> |   Ascent: %.2f", l->swffont->layout->ascent / 20.0);
3017 	    msg("<debug> |   Descent: %.2f", l->swffont->layout->descent / 20.0);
3018 	    msg("<debug> |   Leading: %.2f", l->swffont->layout->leading / 20.0);
3019 	}
3020 
3021 	for(iii=0; iii<l->swffont->numchars;iii++) {
3022 	    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,
3023 		    l->swffont->layout->bounds[iii].xmin/20.0,
3024 		    l->swffont->layout->bounds[iii].ymin/20.0,
3025 		    l->swffont->layout->bounds[iii].xmax/20.0,
3026 		    l->swffont->layout->bounds[iii].ymax/20.0
3027 		    );
3028 	}
3029     }
3030 }
3031 
swf_switchfont(gfxdevice_t * dev,const char * fontid)3032 static void swf_switchfont(gfxdevice_t*dev, const char*fontid)
3033 {
3034     swfoutput_internal*i = (swfoutput_internal*)dev->internal;
3035 
3036     if(i->swffont && i->swffont->name && !strcmp((char*)i->swffont->name,fontid))
3037 	return; // the requested font is the current font
3038 
3039     fontlist_t*l = i->fontlist;
3040     while(l) {
3041 	if(!strcmp((char*)l->swffont->name, fontid)) {
3042 	    i->swffont = l->swffont;
3043 	    return; //done!
3044 	}
3045 	l = l->next;
3046     }
3047     msg("<error> Unknown font id: %s", fontid);
3048     return;
3049 }
3050 
3051 /* sets the matrix which is to be applied to characters drawn by swfoutput_drawchar() */
setfontscale(gfxdevice_t * dev,double m11,double m12,double m21,double m22,double x,double y,char force)3052 static void setfontscale(gfxdevice_t*dev,double m11,double m12, double m21,double m22,double x, double y, char force)
3053 {
3054     m11 *= 1024;
3055     m12 *= 1024;
3056     m21 *= 1024;
3057     m22 *= 1024;
3058     swfoutput_internal*i = (swfoutput_internal*)dev->internal;
3059     if(i->lastfontm11 == m11 &&
3060        i->lastfontm12 == m12 &&
3061        i->lastfontm21 == m21 &&
3062        i->lastfontm22 == m22 && !force)
3063         return;
3064    if(i->textmode)
3065 	endtext(dev);
3066 
3067     i->lastfontm11 = m11;
3068     i->lastfontm12 = m12;
3069     i->lastfontm21 = m21;
3070     i->lastfontm22 = m22;
3071 
3072     double xsize = sqrt(m11*m11 + m12*m12);
3073     double ysize = sqrt(m21*m21 + m22*m22);
3074 
3075     int extrazoom = 1;
3076     if(i->config_flashversion>=8 && !NO_FONT3)
3077 	extrazoom = 20;
3078 
3079     i->current_font_size = (xsize>ysize?xsize:ysize)*extrazoom;
3080     if(i->current_font_size < 1)
3081 	i->current_font_size = 1;
3082 
3083     MATRIX m;
3084     swf_GetMatrix(0, &m);
3085 
3086     if(m21 || m12 || fabs(m11+m22)>0.001 || m11<0) {
3087 	double ifs = (double)extrazoom/(i->current_font_size);
3088 	m.sx =  (S32)((m11*ifs)*65536); m.r1 = -(S32)((m21*ifs)*65536);
3089 	m.r0 =  (S32)((m12*ifs)*65536); m.sy = -(S32)((m22*ifs)*65536);
3090     }
3091 
3092     /* this is the position of the first char to set a new fontmatrix-
3093        we hope that it's close enough to all other characters using the
3094        font, so we use its position as origin for the matrix */
3095     m.tx = x*20;
3096     m.ty = y*20;
3097     i->fontmatrix = m;
3098 }
3099 
3100 
swf_drawchar(gfxdevice_t * dev,gfxfont_t * font,int glyph,gfxcolor_t * color,gfxmatrix_t * matrix)3101 static void swf_drawchar(gfxdevice_t*dev, gfxfont_t*font, int glyph, gfxcolor_t*color, gfxmatrix_t*matrix)
3102 {
3103     swfoutput_internal*i = (swfoutput_internal*)dev->internal;
3104     if(!font) {
3105 	msg("<error> swf_drawchar called (glyph %d) without font", glyph);
3106 	return;
3107     }
3108 
3109     if(i->config_drawonlyshapes) {
3110         gfxglyph_t*g = &font->glyphs[glyph];
3111         gfxline_t*line2 = gfxline_clone(g->line);
3112         gfxline_transform(line2, matrix);
3113 	dev->fill(dev, line2, color);
3114         gfxline_free(line2);
3115         return;
3116     }
3117 
3118     if(!i->swffont || !i->swffont->name || strcmp((char*)i->swffont->name,font->id)) // not equal to current font
3119     {
3120 	swf_switchfont(dev, font->id); // set the current font
3121     }
3122 
3123     if(!i->swffont) {
3124 	msg("<warning> swf_drawchar: Font is NULL");
3125 	return;
3126     }
3127     if(glyph<0 || glyph>=i->swffont->numchars) {
3128 	msg("<warning> No character %d in font %s (%d chars)", glyph, FIXNULL((char*)i->swffont->name), i->swffont->numchars);
3129 	return;
3130     }
3131     glyph = i->swffont->glyph2glyph[glyph];
3132 
3133     setfontscale(dev, matrix->m00, matrix->m01, matrix->m10, matrix->m11, matrix->tx, matrix->ty, 0);
3134 
3135     double det = i->fontmatrix.sx/65536.0 * i->fontmatrix.sy/65536.0 -
3136 	         i->fontmatrix.r0/65536.0 * i->fontmatrix.r1/65536.0;
3137     if(fabs(det) < 0.0005) {
3138 	/* x direction equals y direction- the text is invisible */
3139 	msg("<verbose> Not drawing invisible character %d (det=%f, m=[%f %f;%f %f]\n", glyph,
3140 		det,
3141 		i->fontmatrix.sx/65536.0, i->fontmatrix.r1/65536.0,
3142 		i->fontmatrix.r0/65536.0, i->fontmatrix.sy/65536.0);
3143 	return;
3144     }
3145 
3146     /*if(i->swffont->glyph[glyph].shape->bitlen <= 16) {
3147 	msg("<warning> Glyph %d in current charset (%s, %d characters) is empty",
3148 		glyph, FIXNULL((char*)i->swffont->name), i->swffont->numchars);
3149 	return 1;
3150     }*/
3151 
3152     /* calculate character position with respect to the current font matrix */
3153     double s = 20 * GLYPH_SCALE / det;
3154     double px = matrix->tx - i->fontmatrix.tx/20.0;
3155     double py = matrix->ty - i->fontmatrix.ty/20.0;
3156     int x = (SCOORD)((  px * i->fontmatrix.sy/65536.0 - py * i->fontmatrix.r1/65536.0)*s);
3157     int y = (SCOORD)((- px * i->fontmatrix.r0/65536.0 + py * i->fontmatrix.sx/65536.0)*s);
3158     if(x>32767 || x<-32768 || y>32767 || y<-32768) {
3159 	msg("<verbose> Moving character origin to %f %f\n", matrix->tx, matrix->ty);
3160 	endtext(dev);
3161 	setfontscale(dev, matrix->m00, matrix->m01, matrix->m10, matrix->m11, matrix->tx, matrix->ty, 1);
3162         /* since we just moved the char origin to the current char's position,
3163            it now has the relative position (0,0) */
3164         x = y = 0;
3165     }
3166 
3167     if(i->shapeid>=0)
3168 	endshape(dev);
3169 
3170     if(i->config_animate) {
3171         endtext(dev);
3172 	i->tag = swf_InsertTag(i->tag,ST_SHOWFRAME);
3173     }
3174 
3175     if(!i->textmode)
3176 	starttext(dev);
3177 
3178     msg("<trace> Drawing char %d in font %d at %d,%d in color %02x%02x%02x%02x",
3179 	    glyph, i->swffont->id, x, y, color->r, color->g, color->b, color->a);
3180 
3181     if(color->a == 0 && i->config_invisibletexttofront) {
3182 	RGBA color2 = *(RGBA*)color;
3183 	if(i->config_flashversion>=8) {
3184 	    // use "multiply" blend mode
3185 	    color2.a = color2.r = color2.g = color2.b = 255;
3186 	}
3187 	i->topchardata = charbuffer_append(i->topchardata, i->swffont, glyph, x, y, i->current_font_size, color2, &i->fontmatrix);
3188     } else {
3189 	i->chardata = charbuffer_append(i->chardata, i->swffont, glyph, x, y, i->current_font_size, *(RGBA*)color, &i->fontmatrix);
3190     }
3191     swf_FontUseGlyph(i->swffont, glyph, i->current_font_size);
3192     return;
3193 }
3194