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