2 Implements generation of swf files using the rfxswf lib. The routines
3 in this file are called from pdf2swf.
5 This file is part of swftools.
7 Swftools is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 2 of the License, or
10 (at your option) any later version.
12 Swftools is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
17 You should have received a copy of the GNU General Public License
18 along with swftools; if not, write to the Free Software
19 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
24 #include "../config.h"
33 #include "swfoutput.h"
36 #include "../lib/log.h"
37 #include "../lib/rfxswf.h"
40 #define CHARDATAMAX 8192
44 typedef struct _chardata {
46 int fontid; /* TODO: use a SWFFONT instead */
59 int config_opennewwindow=0;
60 int config_ignoredraworder=0;
61 int config_drawonlyshapes=0;
62 int config_jpegquality=85;
63 int config_storeallcharacters=0;
64 int config_enablezlib=0;
65 int config_insertstoptag=0;
66 int config_flashversion=5;
67 int config_splinemaxerror=1;
68 int config_fontsplinemaxerror=1;
69 int config_filloverlap=0;
71 float config_minlinewidth=0.05;
73 typedef struct _swfoutput_internal
110 char fillstylechanged;
117 chardata_t chardata[CHARDATAMAX];
121 } swfoutput_internal;
123 static swfoutput_internal* init_internal_struct()
125 swfoutput_internal*i = (swfoutput_internal*)malloc(sizeof(swfoutput_internal));
126 memset(i, 0, sizeof(swfoutput_internal));
148 i->fillstylechanged = 0;
158 static void startshape(struct swfoutput* obj);
159 static void starttext(struct swfoutput* obj);
160 static void endshape(struct swfoutput* obj,int clip);
161 static void endtext(struct swfoutput* obj);
163 // matrix multiplication. changes p0
164 static void transform (plotxy*p0,struct swfmatrix*m)
167 x = m->m11*p0->x+m->m12*p0->y;
168 y = m->m21*p0->x+m->m22*p0->y;
173 // write a move-to command into the swf
174 static int moveto(struct swfoutput*obj, TAG*tag, plotxy p0)
176 swfoutput_internal*i = (swfoutput_internal*)obj->internal;
177 int rx = (int)(p0.x*20);
178 int ry = (int)(p0.y*20);
179 if(rx!=i->swflastx || ry!=i->swflasty || i->fillstylechanged) {
180 swf_ShapeSetMove (tag, i->shape, rx,ry);
181 i->fillstylechanged = 0;
188 static int moveto(struct swfoutput*obj, TAG*tag, float x, float y)
190 swfoutput_internal*i = (swfoutput_internal*)obj->internal;
194 return moveto(obj, tag, p);
196 static void addPointToBBox(struct swfoutput*obj, int px, int py)
198 swfoutput_internal*i = (swfoutput_internal*)obj->internal;
204 swf_ExpandRect(&i->bboxrect, p);
206 swf_ExpandRect3(&i->bboxrect, p, i->linewidth*3/2);
210 // write a line-to command into the swf
211 static void lineto(struct swfoutput*obj, TAG*tag, plotxy p0)
213 swfoutput_internal*i = (swfoutput_internal*)obj->internal;
214 int px = (int)(p0.x*20);
215 int py = (int)(p0.y*20);
216 int rx = (px-i->swflastx);
217 int ry = (py-i->swflasty);
218 /* we can't skip this for rx=0,ry=0, those
220 swf_ShapeSetLine (tag, i->shape, rx,ry);
222 addPointToBBox(obj, i->swflastx,i->swflasty);
223 addPointToBBox(obj, px,py);
229 static void lineto(struct swfoutput*obj, TAG*tag, double x, double y)
237 // write a spline-to command into the swf
238 static void splineto(struct swfoutput*obj, TAG*tag, plotxy control,plotxy end)
240 swfoutput_internal*i = (swfoutput_internal*)obj->internal;
241 int lastlastx = i->swflastx;
242 int lastlasty = i->swflasty;
244 int cx = ((int)(control.x*20)-i->swflastx);
245 int cy = ((int)(control.y*20)-i->swflasty);
248 int ex = ((int)(end.x*20)-i->swflastx);
249 int ey = ((int)(end.y*20)-i->swflasty);
253 if(cx || cy || ex || ey) {
254 swf_ShapeSetCurve(tag, i->shape, cx,cy,ex,ey);
255 addPointToBBox(obj, lastlastx ,lastlasty );
256 addPointToBBox(obj, lastlastx+cx,lastlasty+cy);
257 addPointToBBox(obj, lastlastx+cx+ex,lastlasty+cy+ey);
262 /* write a line, given two points and the transformation
264 static void line(struct swfoutput*obj, TAG*tag, plotxy p0, plotxy p1, struct swfmatrix*m)
268 moveto(obj, tag, p0);
269 lineto(obj, tag, p1);
272 /* write a cubic (!) spline. This involves calling the approximate()
273 function out of spline.cc to convert it to a quadratic spline. */
274 static void spline(struct swfoutput*obj, TAG*tag,plotxy p0,plotxy p1,plotxy p2,plotxy p3,struct swfmatrix*m)
276 swfoutput_internal*i = (swfoutput_internal*)obj->internal;
278 struct qspline q[128];
292 /* fonts use a different approximation than shapes */
293 num = cspline_approximate(&c, q, config_fontsplinemaxerror/20.0, APPROXIMATE_RECURSIVE_BINARY);
294 //num = cspline_approximate(&c, q, 10.0, APPROXIMATE_INFLECTION);
296 num = cspline_approximate(&c, q, config_splinemaxerror/20.0, APPROXIMATE_RECURSIVE_BINARY);
300 moveto(obj, tag,q[t].start);
301 splineto(obj, tag,q[t].control, q[t].end);
305 void resetdrawer(struct swfoutput*obj)
307 swfoutput_internal*i = (swfoutput_internal*)obj->internal;
312 static void stopFill(struct swfoutput*obj)
314 swfoutput_internal*i = (swfoutput_internal*)obj->internal;
317 swf_ShapeSetStyle(i->tag,i->shape,i->linestyleid,0x8000,0);
318 i->fillstylechanged = 1;
322 static void startFill(struct swfoutput*obj)
324 swfoutput_internal*i = (swfoutput_internal*)obj->internal;
327 swf_ShapeSetStyle(i->tag,i->shape,0x8000,i->fillstyleid,0);
328 i->fillstylechanged = 1;
333 /* draw an outline. These are generated by pdf2swf and by t1lib
334 (representing characters). */
335 void drawpath(struct swfoutput*obj, SWF_OUTLINE*outline, struct swfmatrix*m, int log)
337 swfoutput_internal*i = (swfoutput_internal*)obj->internal;
338 if( i->tag->id != ST_DEFINESHAPE &&
339 i->tag->id != ST_DEFINESHAPE2 &&
340 i->tag->id != ST_DEFINESHAPE3)
342 msg("<error> internal error: drawpath needs a shape tag, not %d\n",i->tag->id);
346 double lastx=0,lasty=0;
347 double firstx=0,firsty=0;
352 x += (outline->dest.x/(float)0xffff);
353 y += (outline->dest.y/(float)0xffff);
354 if(outline->type == SWF_PATHTYPE_MOVE)
356 //if(!init && fill && obj->drawmode != DRAWMODE_EOFILL && !ignoredraworder) {
357 if(config_filloverlap && !init && i->fill && obj->drawmode != DRAWMODE_EOFILL) {
358 /* drawmode=FILL (not EOFILL) means that
359 seperate shapes do not cancel each other out.
360 On SWF side, we need to start a new shape for each
361 closed polygon, because SWF only knows EOFILL.
368 if(((int)(lastx*20) != (int)(firstx*20) ||
369 (int)(lasty*20) != (int)(firsty*20)) &&
378 if(log) printf("fix: %f,%f -> %f,%f\n",p0.x,p0.y,p1.x,p1.y);
379 line(obj,i->tag, p0, p1, m);
385 else if(outline->type == SWF_PATHTYPE_LINE)
393 if(log) printf("line: %f,%f -> %f,%f\n",p0.x,p0.y,p1.x,p1.y);
394 line(obj,i->tag, p0,p1,m);
396 else if(outline->type == SWF_PATHTYPE_BEZIER)
402 SWF_BEZIERSEGMENT*o2 = (SWF_BEZIERSEGMENT*)outline;
405 p1.x=o2->C.x/(float)0xffff+lastx;
406 p1.y=o2->C.y/(float)0xffff+lasty;
407 p2.x=o2->B.x/(float)0xffff+lastx;
408 p2.y=o2->B.y/(float)0xffff+lasty;
411 if(log) printf("spline: %f,%f -> %f,%f\n",p3.x,p3.y,p0.x,p0.y);
412 spline(obj,i->tag,p0,p1,p2,p3,m);
415 msg("<error> drawpath: unknown outline type:%d\n", outline->type);
419 outline = outline->link;
421 if(((int)(lastx*20) != (int)(firstx*20) ||
422 (int)(lasty*20) != (int)(firsty*20)) &&
431 if(log) printf("fix: %f,%f -> %f,%f\n",p0.x,p0.y,p1.x,p1.y);
432 line(obj, i->tag, p0, p1, m);
436 plotxy getPivot(struct swfoutput*obj, SWF_OUTLINE*outline, int dir, double line_width, int end, int trytwo)
438 swfoutput_internal*i = (swfoutput_internal*)obj->internal;
439 SWF_PATHPOINT next, next2;
440 double xv=0,yv=0, xv2=0, yv2=0;
445 if(outline->type == SWF_PATHTYPE_LINE) {
446 next = outline->dest;
448 next = ((SWF_BEZIERSEGMENT*)outline)->B;
449 if(next.x==0 && next.y==0) {
450 next = ((SWF_BEZIERSEGMENT*)outline)->C;
452 if(next.x==0 && next.y==0) {
453 next = ((SWF_BEZIERSEGMENT*)outline)->dest;
457 if(trytwo && outline->last && outline->last->type != SWF_PATHTYPE_MOVE) {
458 if(outline->type == SWF_PATHTYPE_LINE) {
459 next2 = outline->last->dest;
461 SWF_PATHPOINT c = ((SWF_BEZIERSEGMENT*)(outline->last))->C;
462 SWF_PATHPOINT b = ((SWF_BEZIERSEGMENT*)(outline->last))->B;
463 next2.x = outline->last->dest.x - c.x;
464 next2.y = outline->last->dest.y - c.y;
465 if(next2.x==0 && next2.y==0) {
466 next2.x = outline->last->dest.x - b.x;
467 next2.y = outline->last->dest.y - b.y;
469 if(next2.x==0 && next2.y==0) {
470 next2.x = outline->last->dest.x;
471 next2.y = outline->last->dest.y;
477 if(outline->type == SWF_PATHTYPE_LINE) {
478 next = outline->dest;
480 SWF_PATHPOINT c = ((SWF_BEZIERSEGMENT*)outline)->C;
481 SWF_PATHPOINT b = ((SWF_BEZIERSEGMENT*)outline)->B;
482 next.x = outline->dest.x - c.x;
483 next.y = outline->dest.y - c.y;
484 if(next.x==0 && next.y==0) {
485 next.x = outline->dest.x - b.x;
486 next.y = outline->dest.y - b.y;
488 if(next.x==0 && next.y==0) {
489 next.x = outline->dest.x;
490 next.y = outline->dest.y;
494 if(trytwo && outline->link && outline->link->type != SWF_PATHTYPE_MOVE) {
495 if(outline->type == SWF_PATHTYPE_LINE) {
496 next2 = outline->link->dest;
498 next2 = ((SWF_BEZIERSEGMENT*)(outline->link))->B;
499 if(next2.x==0 && next2.y==0) {
500 next2 = ((SWF_BEZIERSEGMENT*)outline->link)->C;
502 if(next2.x==0 && next2.y==0) {
503 next2 = ((SWF_BEZIERSEGMENT*)outline->link)->dest;
511 xv = next.y/(float)0xffff;
512 yv = -next.x/(float)0xffff;
514 xv = -next.y/(float)0xffff;
515 yv = next.x/(float)0xffff;
518 double r = (line_width/2)/sqrt(xv*xv+yv*yv);
524 xv2 = next2.y/(float)0xffff;
525 yv2 = -next2.x/(float)0xffff;
527 xv2 = -next2.y/(float)0xffff;
528 yv2 = next2.x/(float)0xffff;
531 double r2 = (line_width/2)/sqrt(xv2*xv2+yv2*yv2);
536 double r3 = (line_width/2)/sqrt(xv*xv+yv*yv);
546 void drawShortPath(struct swfoutput*obj, double x, double y, struct swfmatrix* m, SWF_OUTLINE*outline)
548 swfoutput_internal*i = (swfoutput_internal*)obj->internal;
549 double lastx=x, lasty=y;
550 while (outline && outline->type != SWF_PATHTYPE_MOVE)
552 x += (outline->dest.x/(float)0xffff);
553 y += (outline->dest.y/(float)0xffff);
555 if(outline->type == SWF_PATHTYPE_LINE)
562 line(obj, i->tag, p0, p1, m);
564 else if(outline->type == SWF_PATHTYPE_BEZIER)
567 SWF_BEZIERSEGMENT*o2 = (SWF_BEZIERSEGMENT*)outline;
570 p1.x=o2->C.x/(float)0xffff+lastx;
571 p1.y=o2->C.y/(float)0xffff+lasty;
572 p2.x=o2->B.x/(float)0xffff+lastx;
573 p2.y=o2->B.y/(float)0xffff+lasty;
576 spline(obj, i->tag,p0,p1,p2,p3,m);
580 outline = outline->link;
584 void drawShortPathWithEnds(struct swfoutput*obj, double x, double y, struct swfmatrix* m, SWF_OUTLINE*outline, int num, int line_cap, int line_join, double line_width)
586 swfoutput_internal*i = (swfoutput_internal*)obj->internal;
590 if(line_cap == LINE_CAP_BUTT || line_cap == LINE_CAP_SQUARE) {
593 SWF_OUTLINE *last, *tmp=outline;
594 plotxy s,e,p0,p1,p2,p3,m0,m1,m2,m3;
600 while(tmp && tmp->type != SWF_PATHTYPE_MOVE) {
602 lx += (tmp->dest.x/(float)0xffff);
603 ly += (tmp->dest.y/(float)0xffff);
606 s = getPivot(obj, outline, 0, line_width, 0, 0);
607 e = getPivot(obj, last, 0, line_width, 1, 0);
609 if(line_cap == LINE_CAP_BUTT) {
610 /* make the clipping rectangle slighly bigger
611 than the line ending, so that it get's clipped
621 p2.x = x2 - s.y - s.x*ee;
622 p2.y = y2 + s.x - s.y*ee;
623 p3.x = x2 - s.y + s.x*ee;
624 p3.y = y2 + s.x + s.y*ee;
629 m2.x = lx + e.y - e.x*ee;
630 m2.y = ly - e.x - e.y*ee;
631 m3.x = lx + e.y + e.x*ee;
632 m3.y = ly - e.x + e.y*ee;
634 for(nr=0;nr<2;nr++) {
636 struct plotxy q0,q1,q2,q3,q4,q5;
639 if(line_cap == LINE_CAP_BUTT) {
641 /* FIXME: box should be smaller */
643 q1.x = i->max_x; q1.y = 0;
644 q2.x = i->max_x; q2.y = i->max_y;
645 q3.x = 0; q3.y = i->max_y;
647 /* FIXME: box should be smaller */
648 q0.x = i->max_x; q0.y = i->max_y;
649 q1.x = 0; q1.y = i->max_y;
651 q3.x = i->max_x; q3.y = 0;
655 moveto(obj, i->tag, q0);
656 lineto(obj, i->tag, q1);
657 lineto(obj, i->tag, q2);
658 lineto(obj, i->tag, q3);
659 lineto(obj, i->tag, q0);
662 lineto(obj, i->tag, q4);
665 line(obj, i->tag, p0, p1, m);
666 line(obj, i->tag, p1, p2, m);
667 line(obj, i->tag, p2, p3, m);
668 line(obj, i->tag, p3, p0, m);
670 if(line_cap == LINE_CAP_BUTT) {
671 lineto(obj, i->tag, q0);
672 endshape(obj, i->depth+2-nr);
684 drawShortPath(obj,x,y,m,outline);
686 if(line_cap == LINE_CAP_BUTT) {
692 void drawT1toRect(struct swfoutput*obj, double x, double y, struct swfmatrix* m, SWF_OUTLINE*outline, int num, int line_cap, int line_join, double line_width)
694 swfoutput_internal*i = (swfoutput_internal*)obj->internal;
695 plotxy d1,d2,p1,p2,p3,p4;
697 d1.x = (outline->dest.x/(float)0xffff);
698 d1.y = (outline->dest.y/(float)0xffff);
699 d2 = getPivot(obj, outline, 0, line_width, 0, 0);
701 assert(line_cap != LINE_CAP_ROUND);
702 if(line_cap == LINE_CAP_SQUARE) {
711 p2.x = x + d2.x + d1.x;
712 p2.y = y + d2.y + d1.y;
713 p3.x = x - d2.x + d1.x;
714 p3.y = y - d2.y + d1.y;
718 line(obj, i->tag, p1,p2, m);
719 line(obj, i->tag, p2,p3, m);
720 line(obj, i->tag, p3,p4, m);
721 line(obj, i->tag, p4,p1, m);
724 void drawShortPathWithStraightEnds(struct swfoutput*obj, double x, double y, struct swfmatrix* m, SWF_OUTLINE*outline, int num, int line_cap, int line_join, double line_width)
726 swfoutput_internal*i = (swfoutput_internal*)obj->internal;
727 SWF_OUTLINE*tmp=outline;
730 assert(i->shapeid>=0);
733 drawT1toRect(obj, x, y, m,outline, num, line_cap, line_join, line_width);
735 while(tmp->link && tmp->link->type!=SWF_PATHTYPE_MOVE) {
736 xx += (tmp->dest.x/(float)0xffff);
737 yy += (tmp->dest.y/(float)0xffff);
741 assert(tmp->type == SWF_PATHTYPE_LINE);
742 assert(outline->type == SWF_PATHTYPE_LINE);
746 if(outline->link == tmp) {
747 /* the two straight line segments (which are everything we
748 need to draw) are very likely to overlap. To avoid that
749 they cancel each other out at the end points, start a new
750 shape for the second one */
751 endshape(obj,0);startshape(obj);
755 drawT1toRect(obj, xx, yy, m, tmp, num, line_cap, line_join, line_width);
757 if(outline->link != tmp)
759 stopFill(obj);stop=1;
761 tmp->type = SWF_PATHTYPE_MOVE;
762 x += (outline->dest.x/(float)0xffff);
763 y += (outline->dest.y/(float)0xffff);
764 outline = outline->link;
765 drawShortPath(obj, x, y, m, outline);
773 static int t1len(struct swfoutput*obj, SWF_OUTLINE*line)
775 swfoutput_internal*i = (swfoutput_internal*)obj->internal;
777 while(line && line->type != SWF_PATHTYPE_MOVE) {
784 static float t1linelen(struct swfoutput*obj, SWF_OUTLINE*line)
786 swfoutput_internal*i = (swfoutput_internal*)obj->internal;
788 x = (line->dest.x/(float)0xffff);
789 y = (line->dest.y/(float)0xffff);
790 return sqrt(x*x+y*y);
793 void drawpath2poly(struct swfoutput *obj, SWF_OUTLINE*outline, struct swfmatrix*m, int log, int line_join, int line_cap, double line_width, double miter_limit)
795 swfoutput_internal*i = (swfoutput_internal*)obj->internal;
796 if( i->tag->id != ST_DEFINESHAPE &&
797 i->tag->id != ST_DEFINESHAPE2 &&
798 i->tag->id != ST_DEFINESHAPE3) {
799 msg("<error> internal error: drawpath needs a shape tag, not %d\n",i->tag->id);
802 assert(i->shapeid>=0);
804 double lastx=0,lasty=0;
807 SWF_OUTLINE*tmp = outline, *last = 0;
812 x += (tmp->dest.x/(float)0xffff);
813 y += (tmp->dest.y/(float)0xffff);
815 if(!tmp || tmp->type == SWF_PATHTYPE_MOVE) {
817 if(last->type == SWF_PATHTYPE_LINE && t1linelen(obj,last)>line_width*2 &&
818 lastwasline && line_cap != LINE_CAP_ROUND)
819 drawShortPathWithStraightEnds(obj, lastx, lasty, m, last, valid, line_cap, line_join, line_width);
821 drawShortPathWithEnds(obj, lastx, lasty, m, last, valid, line_cap, line_join, line_width);
835 if(tmp && tmp->type == SWF_PATHTYPE_LINE && t1linelen(obj,tmp)>line_width*2)
841 tmp->link->last = tmp; // make sure list is properly linked in both directions
846 static inline int colorcompare(struct swfoutput*obj, RGBA*a,RGBA*b)
858 static SRECT getcharacterbbox(struct swfoutput*obj, SWFFONT*font)
860 swfoutput_internal*i = (swfoutput_internal*)obj->internal;
863 memset(&r, 0, sizeof(r));
866 if(debug) printf("\n");
867 for(t=0;t<i->chardatapos;t++)
869 if(i->chardata[t].fontid != font->id) {
870 msg("<error> Internal error: fontid %d != fontid %d", i->chardata[t].fontid, font->id);
873 SRECT b = font->layout->bounds[i->chardata[t].charid];
874 b.xmin *= i->chardata[t].size;
875 b.ymin *= i->chardata[t].size;
876 b.xmax *= i->chardata[t].size;
877 b.ymax *= i->chardata[t].size;
882 b.xmin += i->chardata[t].x;
883 b.ymin += i->chardata[t].y;
884 b.xmax += i->chardata[t].x;
885 b.ymax += i->chardata[t].y;
887 /* until we solve the INTERNAL_SCALING problem (see below)
888 make sure the bounding box is big enough */
894 if(debug) printf("(%f,%f,%f,%f) -> (%f,%f,%f,%f) [font %d/%d, char %d]\n",
895 font->layout->bounds[i->chardata[t].charid].xmin/20.0,
896 font->layout->bounds[i->chardata[t].charid].ymin/20.0,
897 font->layout->bounds[i->chardata[t].charid].xmax/20.0,
898 font->layout->bounds[i->chardata[t].charid].ymax/20.0,
903 i->chardata[t].fontid,
905 i->chardata[t].charid
907 swf_ExpandRect2(&r, &b);
909 if(debug) printf("-----> (%f,%f,%f,%f)\n",
917 static void putcharacters(struct swfoutput*obj, TAG*tag)
919 swfoutput_internal*i = (swfoutput_internal*)obj->internal;
923 color.r = i->chardata[0].color.r^255;
932 int charadvance[128];
935 int glyphbits=1; //TODO: can this be zero?
938 if(tag->id != ST_DEFINETEXT &&
939 tag->id != ST_DEFINETEXT2) {
940 msg("<error> internal error: putcharacters needs an text tag, not %d\n",tag->id);
943 if(!i->chardatapos) {
944 msg("<warning> putcharacters called with zero characters");
947 for(pass = 0; pass < 2; pass++)
957 advancebits++; // add sign bit
958 swf_SetU8(tag, glyphbits);
959 swf_SetU8(tag, advancebits);
962 for(t=0;t<=i->chardatapos;t++)
964 if(lastfontid != i->chardata[t].fontid ||
965 lastx!=i->chardata[t].x ||
966 lasty!=i->chardata[t].y ||
967 !colorcompare(obj,&color, &i->chardata[t].color) ||
969 lastsize != i->chardata[t].size ||
972 if(charstorepos && pass==0)
975 for(s=0;s<charstorepos;s++)
977 while(charids[s]>=(1<<glyphbits))
979 while(charadvance[s]>=(1<<advancebits))
983 if(charstorepos && pass==1)
985 tag->writeBit = 0; // Q&D
986 swf_SetBits(tag, 0, 1); // GLYPH Record
987 swf_SetBits(tag, charstorepos, 7); // number of glyphs
989 for(s=0;s<charstorepos;s++)
991 swf_SetBits(tag, charids[s], glyphbits);
992 swf_SetBits(tag, charadvance[s], advancebits);
997 if(pass == 1 && t<i->chardatapos)
1003 if(lastx != i->chardata[t].x ||
1004 lasty != i->chardata[t].y)
1006 newx = i->chardata[t].x;
1007 newy = i->chardata[t].y;
1013 if(!colorcompare(obj,&color, &i->chardata[t].color))
1015 color = i->chardata[t].color;
1018 font.id = i->chardata[t].fontid;
1019 if(lastfontid != i->chardata[t].fontid || lastsize != i->chardata[t].size)
1022 tag->writeBit = 0; // Q&D
1023 swf_TextSetInfoRecord(tag, newfont, i->chardata[t].size, newcolor, newx,newy);
1026 lastfontid = i->chardata[t].fontid;
1027 lastx = i->chardata[t].x;
1028 lasty = i->chardata[t].y;
1029 lastsize = i->chardata[t].size;
1032 if(t==i->chardatapos)
1036 int nextt = t==i->chardatapos-1?t:t+1;
1037 int rel = i->chardata[nextt].x-i->chardata[t].x;
1038 if(rel>=0 && (rel<(1<<(advancebits-1)) || pass==0)) {
1040 lastx=i->chardata[nextt].x;
1044 lastx=i->chardata[t].x;
1046 charids[charstorepos] = i->chardata[t].charid;
1047 charadvance[charstorepos] = advance;
1054 static void putcharacter(struct swfoutput*obj, int fontid, int charid,
1055 int x,int y, int size)
1057 swfoutput_internal*i = (swfoutput_internal*)obj->internal;
1058 if(i->chardatapos == CHARDATAMAX)
1060 msg("<warning> Character buffer too small. SWF will be slightly bigger");
1064 i->chardata[i->chardatapos].fontid = fontid;
1065 i->chardata[i->chardatapos].charid = charid;
1066 i->chardata[i->chardatapos].x = x;
1067 i->chardata[i->chardatapos].y = y;
1068 i->chardata[i->chardatapos].color = obj->fillrgb;
1069 i->chardata[i->chardatapos].size = size;
1073 /* Notice: we can only put chars in the range -1639,1638 (-32768/20,32768/20).
1074 So if we set this value to high, the char coordinates will overflow.
1075 If we set it to low, however, the char positions will be inaccurate */
1076 #define FONT_INTERNAL_SIZE 4
1078 /* process a character. */
1079 static int drawchar(struct swfoutput*obj, SWFFONT *swffont, char*character, int charnr, int u, swfmatrix*m)
1081 swfoutput_internal*i = (swfoutput_internal*)obj->internal;
1083 msg("<warning> Font is NULL");
1087 int charid = getCharID(swffont, charnr, character, u);
1090 msg("<warning> Didn't find character '%s' (c=%d,u=%d) in current charset (%s, %d characters)",
1091 FIXNULL(character),charnr, u, FIXNULL((char*)swffont->name), swffont->numchars);
1102 float det = ((m->m11*m->m22)-(m->m21*m->m12));
1103 if(fabs(det) < 0.0005) {
1104 /* x direction equals y direction- the text is invisible */
1107 det = 20*FONT_INTERNAL_SIZE / det;
1110 p.x = (SCOORD)(( x * m->m22 - y * m->m12)*det);
1111 p.y = (SCOORD)((- x * m->m21 + y * m->m11)*det);
1113 putcharacter(obj, swffont->id, charid,p.x,p.y,FONT_INTERNAL_SIZE);
1114 swf_FontUseGlyph(swffont, charid);
1119 SWF_OUTLINE*outline = font->getOutline(character, charnr);
1120 char* charname = character;
1123 msg("<warning> Didn't find character '%s' (%d) in current charset (%s)",
1124 FIXNULL(character),charnr,FIXNULL(font->getName()));
1143 drawpath(tag, outline, &m2, 0);
1148 static void endtext(swfoutput*obj)
1150 swfoutput_internal*i = (swfoutput_internal*)obj->internal;
1154 i->tag = swf_InsertTag(i->tag,ST_DEFINETEXT);
1155 swf_SetU16(i->tag, i->textid);
1158 r = getcharacterbbox(obj, obj->swffont);
1160 swf_SetRect(i->tag,&r);
1163 swf_GetMatrix(0, &m);
1164 swf_SetMatrix(i->tag,&m);
1166 putcharacters(obj, i->tag);
1167 swf_SetU8(i->tag,0);
1168 i->tag = swf_InsertTag(i->tag,ST_PLACEOBJECT2);
1169 //swf_ObjectPlace(i->tag,i->textid,/*depth*/i->depth++,&i->page_matrix,NULL,NULL);
1171 swf_MatrixJoin(&m2,&obj->fontmatrix, &i->page_matrix);
1173 swf_ObjectPlace(i->tag,i->textid,/*depth*/i->depth++,&m2,NULL,NULL);
1178 /* draw a curved polygon. */
1179 void swfoutput_drawpath(swfoutput*obj, SWF_OUTLINE*outline,
1182 swfoutput_internal*i = (swfoutput_internal*)obj->internal;
1186 /* Multiple polygons in one shape don't overlap correctly,
1187 so we better start a new shape here if the polygon is filled
1189 if(i->shapeid>=0 && i->fill && !config_ignoredraworder) {
1202 drawpath(obj, outline,m, 0);
1205 void swfoutput_drawpath2poly(struct swfoutput*obj, SWF_OUTLINE*outline, struct swfmatrix*m, int line_join, int line_cap, double line_width, double miter_limit)
1207 swfoutput_internal*i = (swfoutput_internal*)obj->internal;
1212 assert(i->shapeid<0);
1216 drawpath2poly(obj, outline, m, 0, line_join, line_cap, line_width, miter_limit);
1219 int getCharID(SWFFONT *font, int charnr, char *charname, int u)
1222 if(charname && font->glyphnames) {
1223 for(t=0;t<font->numchars;t++) {
1224 if(font->glyphnames[t] && !strcmp(font->glyphnames[t],charname)) {
1225 msg("<debug> Char [%d,>%s<,%d] maps to %d\n", charnr, charname, u, t);
1229 /* if we didn't find the character, maybe
1230 we can find the capitalized version */
1231 for(t=0;t<font->numchars;t++) {
1232 if(font->glyphnames[t] && !strcasecmp(font->glyphnames[t],charname)) {
1233 msg("<debug> Char [%d,>>%s<<,%d] maps to %d\n", charnr, charname, u, t);
1239 if(u>0 && font->encoding != 255) {
1240 /* try to use the unicode id */
1241 if(u>=0 && u<font->maxascii && font->ascii2glyph[u]>=0) {
1242 msg("<debug> Char [%d,%s,>%d<] maps to %d\n", charnr, charname, u, font->ascii2glyph[u]);
1243 return font->ascii2glyph[u];
1247 if(font->encoding != FONT_ENCODING_UNICODE) {
1248 /* the following only works if the font encoding
1249 is US-ASCII based. It's needed for fonts which return broken unicode
1251 if(charnr>=0 && charnr<font->maxascii && font->ascii2glyph[charnr]>=0) {
1252 msg("<debug> Char [>%d<,%s,%d] maps to %d\n", charnr, charname, u, font->ascii2glyph[charnr]);
1253 return font->ascii2glyph[charnr];
1257 if(charnr>=0 && charnr<font->numchars) {
1258 msg("<debug> Char [>%d<,%s,%d] maps to %d\n", charnr, charname, u, charnr);
1266 /* set's the t1 font index of the font to use for swfoutput_drawchar(). */
1267 void swfoutput_setfont(struct swfoutput*obj, char*fontid, char*filename)
1269 swfoutput_internal*i = (swfoutput_internal*)obj->internal;
1270 fontlist_t*last=0,*iterator;
1272 msg("<error> No fontid");
1276 if(obj->swffont && obj->swffont->name && !strcmp((char*)obj->swffont->name,fontid))
1279 /* TODO: remove the need for this (enhance getcharacterbbox so that it can cope
1280 with multiple fonts */
1283 iterator = i->fontlist;
1285 if(!strcmp((char*)iterator->swffont->name,fontid)) {
1286 obj->swffont = iterator->swffont;
1290 iterator = iterator->next;
1294 msg("<error> No filename given for font- internal error?");
1298 swf_SetLoadFontParameters(64,/*skip unused*/0,/*full unicode*/1);
1299 SWFFONT*swffont = swf_LoadFont(filename);
1302 msg("<warning> Couldn't load font %s (%s)", fontid, filename);
1303 swffont = swf_LoadFont(0);
1306 if(swffont->glyph2ascii) {
1309 /* check whether the Unicode indices look o.k.
1310 If they don't, disable the unicode lookup by setting
1311 the encoding to 255 */
1312 for(t=0;t<swffont->numchars;t++) {
1313 int c = swffont->glyph2ascii[t];
1314 if(c && c < 32 && swffont->glyph[t].shape->bitlen > 16) {
1315 // the character maps into the unicode control character range
1316 // between 0001-001f. Yet it is not empty. Treat the one
1317 // mapping as broken, and look how many of those we find.
1322 msg("<warning> Font %s has bad unicode mapping", swffont->name);
1323 swffont->encoding = 255;
1327 swf_FontSetID(swffont, ++i->currentswfid);
1329 if(getScreenLogLevel() >= LOGLEVEL_DEBUG) {
1330 // print font information
1331 msg("<debug> Font %s (%s)",swffont->name, filename);
1332 msg("<debug> | ID: %d", swffont->id);
1333 msg("<debug> | Version: %d", swffont->version);
1334 msg("<debug> | Name: %s", fontid);
1335 msg("<debug> | Numchars: %d", swffont->numchars);
1336 msg("<debug> | Maxascii: %d", swffont->maxascii);
1337 msg("<debug> | Style: %d", swffont->style);
1338 msg("<debug> | Encoding: %d", swffont->encoding);
1339 for(int iii=0; iii<swffont->numchars;iii++) {
1340 msg("<debug> | Glyph %d) name=%s, unicode=%d size=%d bbox=(%.2f,%.2f,%.2f,%.2f)\n", iii, swffont->glyphnames?swffont->glyphnames[iii]:"<nonames>", swffont->glyph2ascii[iii], swffont->glyph[iii].shape->bitlen,
1341 swffont->layout->bounds[iii].xmin/20.0,
1342 swffont->layout->bounds[iii].ymin/20.0,
1343 swffont->layout->bounds[iii].xmax/20.0,
1344 swffont->layout->bounds[iii].ymax/20.0
1347 for(t=0;t<swffont->maxascii;t++) {
1348 if(swffont->ascii2glyph[t] == iii)
1349 msg("<debug> | - maps to %d",t);
1354 /* set the font name to the ID we use here */
1355 if(swffont->name) free(swffont->name);
1356 swffont->name = (U8*)strdup(fontid);
1358 iterator = new fontlist_t;
1359 iterator->swffont = swffont;
1363 last->next = iterator;
1365 i->fontlist = iterator;
1367 obj->swffont = swffont;
1370 int swfoutput_queryfont(struct swfoutput*obj, char*fontid)
1372 swfoutput_internal*i = (swfoutput_internal*)obj->internal;
1373 fontlist_t *iterator = i->fontlist;
1375 if(!strcmp((char*)iterator->swffont->name,fontid))
1377 iterator = iterator->next;
1382 /* set's the matrix which is to be applied to characters drawn by
1383 swfoutput_drawchar() */
1384 void swfoutput_setfontmatrix(struct swfoutput*obj,double m11,double m12,
1385 double m21,double m22)
1387 swfoutput_internal*i = (swfoutput_internal*)obj->internal;
1388 if(obj->fontm11 == m11 &&
1389 obj->fontm12 == m12 &&
1390 obj->fontm21 == m21 &&
1391 obj->fontm22 == m22)
1401 m.sx = (U32)(((obj->fontm11)*65536)/FONT_INTERNAL_SIZE); m.r1 = (U32)(((obj->fontm12)*65536)/FONT_INTERNAL_SIZE);
1402 m.r0 = (U32)(((obj->fontm21)*65536)/FONT_INTERNAL_SIZE); m.sy = (U32)(((obj->fontm22)*65536)/FONT_INTERNAL_SIZE);
1405 obj->fontmatrix = m;
1408 /* draws a character at x,y. */
1409 int swfoutput_drawchar(struct swfoutput* obj,double x,double y,char*character, int charnr, int u)
1411 swfoutput_internal*i = (swfoutput_internal*)obj->internal;
1413 m.m11 = obj->fontm11;
1414 m.m12 = obj->fontm12;
1415 m.m21 = obj->fontm21;
1416 m.m22 = obj->fontm22;
1419 return drawchar(obj, obj->swffont, character, charnr, u, &m);
1422 static void endpage(struct swfoutput*obj)
1424 swfoutput_internal*i = (swfoutput_internal*)obj->internal;
1430 swfoutput_endclip(obj);
1431 i->pagefinished = 1;
1434 void swfoutput_pagefeed(struct swfoutput*obj)
1436 swfoutput_internal*i = (swfoutput_internal*)obj->internal;
1438 if(!i->pagefinished)
1441 if(config_insertstoptag) {
1443 atag = action_Stop(atag);
1444 atag = action_End(atag);
1445 i->tag = swf_InsertTag(i->tag,ST_DOACTION);
1446 swf_ActionSet(i->tag,atag);
1448 i->tag = swf_InsertTag(i->tag,ST_SHOWFRAME);
1451 for(i->depth--;i->depth>=i->startdepth;i->depth--) {
1452 i->tag = swf_InsertTag(i->tag,ST_REMOVEOBJECT2);
1453 swf_SetU16(i->tag,i->depth);
1455 i->depth = i->startdepth;
1458 static void setBackground(struct swfoutput*obj, int x1, int y1, int x2, int y2)
1460 swfoutput_internal*i = (swfoutput_internal*)obj->internal;
1462 rgb.a = rgb.r = rgb.g = rgb.b = 0xff;
1466 int shapeid = ++i->currentswfid;
1471 i->tag = swf_InsertTag(i->tag, ST_DEFINESHAPE);
1473 fs1 = swf_ShapeAddSolidFillStyle(s, &rgb);
1474 swf_SetU16(i->tag,shapeid);
1475 swf_SetRect(i->tag,&r);
1476 swf_SetShapeHeader(i->tag,s);
1477 swf_ShapeSetAll(i->tag,s,x1,y1,ls1,fs1,0);
1478 swf_ShapeSetLine(i->tag,s,(x2-x1),0);
1479 swf_ShapeSetLine(i->tag,s,0,(y2-y1));
1480 swf_ShapeSetLine(i->tag,s,(x1-x2),0);
1481 swf_ShapeSetLine(i->tag,s,0,(y1-y2));
1482 swf_ShapeSetEnd(i->tag);
1484 i->tag = swf_InsertTag(i->tag, ST_PLACEOBJECT2);
1485 swf_ObjectPlace(i->tag,shapeid,i->depth++,0,0,0);
1486 i->tag = swf_InsertTag(i->tag, ST_PLACEOBJECT2);
1487 swf_ObjectPlaceClip(i->tag,shapeid,i->depth++,0,0,0,65535);
1488 i->cliptag = i->tag;
1491 void swfoutput_newpage(struct swfoutput*obj, int pageNum, int movex, int movey, int x1, int y1, int x2, int y2)
1493 swfoutput_internal*i = (swfoutput_internal*)obj->internal;
1494 if(!i->firstpage && !i->pagefinished)
1497 swf_GetMatrix(0, &i->page_matrix);
1498 i->page_matrix.tx = movex*20;
1499 i->page_matrix.ty = movey*20;
1501 if(i->cliptag && i->frameno == i->lastframeno) {
1503 swf_GetPlaceObject(i->cliptag, &obj);
1504 obj.clipdepth = i->depth++;
1505 swf_ResetTag(i->cliptag, i->cliptag->id);
1506 swf_SetPlaceObject(i->cliptag, &obj);
1507 swf_PlaceObjectFree(&obj);
1513 msg("<notice> processing page %d (%dx%d:%d:%d)", pageNum,x2-x1,y2-y1, x1, y1);
1515 x1*=20;y1*=20;x2*=20;y2*=20;
1517 /* set clipping/background rectangle */
1518 /* TODO: this should all be done in SWFOutputDev */
1519 setBackground(obj, x1, y1, x2, y2);
1521 /* increase SWF's bounding box */
1527 swf_ExpandRect2(&i->swf.movieSize, &r);
1529 i->lastframeno = i->frameno;
1531 i->pagefinished = 0;
1534 /* initialize the swf writer */
1535 void swfoutput_init(struct swfoutput* obj)
1537 memset(obj, 0, sizeof(struct swfoutput));
1538 obj->internal = init_internal_struct();
1540 swfoutput_internal*i = (swfoutput_internal*)obj->internal;
1545 msg("<verbose> initializing swf output for size %d*%d\n", i->max_x,i->max_y);
1550 memset(&i->swf,0x00,sizeof(SWF));
1552 i->swf.fileVersion = config_flashversion;
1553 i->swf.frameRate = 0x0040; // 1 frame per 4 seconds
1554 i->swf.movieSize.xmin = 0;
1555 i->swf.movieSize.ymin = 0;
1556 i->swf.movieSize.xmax = 0;
1557 i->swf.movieSize.ymax = 0;
1559 i->swf.firstTag = swf_InsertTag(NULL,ST_SETBACKGROUNDCOLOR);
1560 i->tag = i->swf.firstTag;
1561 rgb.a = rgb.r = rgb.g = rgb.b = 0xff;
1562 swf_SetRGB(i->tag,&rgb);
1564 i->startdepth = i->depth = 3; /* leave room for clip and background rectangle */
1567 i->tag = swf_InsertTag(i->tag, ST_PROTECT);
1570 static void startshape(struct swfoutput*obj)
1572 swfoutput_internal*i = (swfoutput_internal*)obj->internal;
1579 i->tag = swf_InsertTag(i->tag,ST_DEFINESHAPE);
1581 swf_ShapeNew(&i->shape);
1582 i->linestyleid = swf_ShapeAddLineStyle(i->shape,i->linewidth,&obj->strokergb);
1583 rgb.r = obj->fillrgb.r;
1584 rgb.g = obj->fillrgb.g;
1585 rgb.b = obj->fillrgb.b;
1586 i->fillstyleid = swf_ShapeAddSolidFillStyle(i->shape,&obj->fillrgb);
1588 i->shapeid = ++i->currentswfid;
1589 swf_SetU16(i->tag,i->shapeid); // ID
1591 i->bboxrectpos = i->tag->len;
1594 r.xmax = 20*i->max_x;
1595 r.ymax = 20*i->max_y;
1596 swf_SetRect(i->tag,&r);
1598 memset(&i->bboxrect, 0, sizeof(i->bboxrect));
1600 swf_SetShapeStyles(i->tag,i->shape);
1601 swf_ShapeCountBits(i->shape,NULL,NULL);
1602 swf_SetShapeBits(i->tag,i->shape);
1604 /* TODO: do we really need this? */
1605 swf_ShapeSetAll(i->tag,i->shape,/*x*/0,/*y*/0,i->linestyleid,0,0);
1606 i->swflastx=i->swflasty=0;
1608 i->shapeisempty = 1;
1611 static void starttext(struct swfoutput*obj)
1613 swfoutput_internal*i = (swfoutput_internal*)obj->internal;
1617 i->textid = ++i->currentswfid;
1619 i->swflastx=i->swflasty=0;
1623 /* TODO: move to ../lib/rfxswf */
1624 void changeRect(struct swfoutput*obj, TAG*tag, int pos, SRECT*newrect)
1626 swfoutput_internal*i = (swfoutput_internal*)obj->internal;
1627 /* determine length of old rect */
1631 swf_GetRect(tag, &old);
1632 swf_ResetReadBits(tag);
1633 int pos_end = tag->pos;
1635 int len = tag->len - pos_end;
1636 U8*data = (U8*)malloc(len);
1637 memcpy(data, &tag->data[pos_end], len);
1640 swf_SetRect(tag, newrect);
1641 swf_SetBlock(tag, data, len);
1643 tag->pos = tag->readBit = 0;
1646 void cancelshape(swfoutput*obj)
1648 swfoutput_internal*i = (swfoutput_internal*)obj->internal;
1649 /* delete old shape tag */
1651 i->tag = i->tag->prev;
1652 swf_DeleteTag(todel);
1653 if(i->shape) {swf_ShapeFree(i->shape);i->shape=0;}
1655 i->bboxrectpos = -1;
1658 void fixAreas(swfoutput*obj)
1660 swfoutput_internal*i = (swfoutput_internal*)obj->internal;
1661 if(!i->shapeisempty && i->fill &&
1662 (i->bboxrect.xmin == i->bboxrect.xmax ||
1663 i->bboxrect.ymin == i->bboxrect.ymax) &&
1664 config_minlinewidth >= 0.001
1666 msg("<debug> Shape has size 0: width=%.2f height=%.2f",
1667 (i->bboxrect.xmax-i->bboxrect.xmin)/20.0,
1668 (i->bboxrect.ymax-i->bboxrect.ymin)/20.0
1671 SRECT r = i->bboxrect;
1673 if(r.xmin == r.xmax && r.ymin == r.ymax) {
1674 /* this thing comes down to a single dot- nothing to fix here */
1680 RGBA save_col = obj->strokergb;
1681 int save_width = i->linewidth;
1683 obj->strokergb = obj->fillrgb;
1684 i->linewidth = (int)(config_minlinewidth*20);
1685 if(i->linewidth==0) i->linewidth = 1;
1689 moveto(obj, i->tag, r.xmin/20.0,r.ymin/20.0);
1690 lineto(obj, i->tag, r.xmax/20.0,r.ymax/20.0);
1692 obj->strokergb = save_col;
1693 i->linewidth = save_width;
1698 static void endshape_noput(swfoutput*obj)
1700 swfoutput_internal*i = (swfoutput_internal*)obj->internal;
1703 //changeRect(obj, i->tag, i->bboxrectpos, &i->bboxrect);
1706 swf_ShapeFree(i->shape);
1711 static void endshape(swfoutput*obj, int clipdepth)
1713 swfoutput_internal*i = (swfoutput_internal*)obj->internal;
1720 if(i->shapeisempty ||
1721 (i->bboxrect.xmin == i->bboxrect.xmax &&
1722 i->bboxrect.ymin == i->bboxrect.ymax))
1724 // delete the shape again, we didn't do anything
1729 swf_ShapeSetEnd(i->tag);
1731 changeRect(obj, i->tag, i->bboxrectpos, &i->bboxrect);
1733 i->tag = swf_InsertTag(i->tag,ST_PLACEOBJECT2);
1736 swf_ObjectPlaceClip(i->tag,i->shapeid,i->depth++,&i->page_matrix,NULL,NULL,clipdepth);
1738 swf_ObjectPlace(i->tag,i->shapeid,/*depth*/i->depth++,&i->page_matrix,NULL,NULL);
1740 swf_ShapeFree(i->shape);
1743 i->bboxrectpos = -1;
1746 void swfoutput_finalize(struct swfoutput*obj)
1748 swfoutput_internal*i = (swfoutput_internal*)obj->internal;
1750 if(i->tag && i->tag->id == ST_END)
1751 return; //already done
1753 if(i->frameno == i->lastframeno) // fix: add missing pagefeed
1754 swfoutput_pagefeed(obj);
1757 fontlist_t *tmp,*iterator = i->fontlist;
1759 TAG*mtag = i->swf.firstTag;
1760 if(iterator->swffont) {
1761 mtag = swf_InsertTag(mtag, ST_DEFINEFONT2);
1762 /*if(!storeallcharacters)
1763 swf_FontReduce(iterator->swffont);*/
1764 swf_FontSetDefine2(mtag, iterator->swffont);
1767 iterator = iterator->next;
1769 i->tag = swf_InsertTag(i->tag,ST_END);
1772 SWF* swfoutput_get(struct swfoutput*obj)
1774 swfoutput_internal*i = (swfoutput_internal*)obj->internal;
1776 swfoutput_finalize(obj);
1778 return swf_CopySWF(&i->swf);
1781 void swfoutput_getdimensions(struct swfoutput*obj, int*x1, int*y1, int*x2, int*y2)
1783 swfoutput_internal*i = (swfoutput_internal*)obj->internal;
1784 if(x1) *x1 = i->swf.movieSize.xmin/20;
1785 if(y1) *y1 = i->swf.movieSize.ymin/20;
1786 if(x2) *x2 = i->swf.movieSize.xmax/20;
1787 if(y2) *y2 = i->swf.movieSize.ymax/20;
1790 int swfoutput_save(struct swfoutput* obj, char*filename)
1792 swfoutput_internal*i = (swfoutput_internal*)obj->internal;
1793 swfoutput_finalize(obj);
1797 fi = open(filename, O_BINARY|O_CREAT|O_TRUNC|O_WRONLY, 0777);
1802 msg("<fatal> Could not create \"%s\". ", FIXNULL(filename));
1806 if(config_enablezlib || config_flashversion>=6) {
1807 if FAILED(swf_WriteSWC(fi,&i->swf))
1808 msg("<error> WriteSWC() failed.\n");
1810 if FAILED(swf_WriteSWF(fi,&i->swf))
1811 msg("<error> WriteSWF() failed.\n");
1816 msg("<notice> SWF written\n");
1820 /* Perform cleaning up, complete the swf, and write it out. */
1821 void swfoutput_destroy(struct swfoutput* obj)
1823 swfoutput_internal*i = (swfoutput_internal*)obj->internal;
1825 /* not initialized yet- nothing to destroy */
1829 fontlist_t *tmp,*iterator = i->fontlist;
1831 if(iterator->swffont) {
1832 swf_FontFree(iterator->swffont);iterator->swffont=0;
1835 iterator = iterator->next;
1838 swf_FreeTags(&i->swf);
1841 memset(obj, 0, sizeof(swfoutput));
1844 void swfoutput_setdrawmode(swfoutput* obj, int mode)
1846 swfoutput_internal*i = (swfoutput_internal*)obj->internal;
1847 obj->drawmode = mode;
1848 if(mode == DRAWMODE_FILL)
1850 else if(mode == DRAWMODE_EOFILL)
1852 else if(mode == DRAWMODE_STROKE)
1854 else if(mode == DRAWMODE_CLIP)
1856 else if(mode == DRAWMODE_EOCLIP)
1860 void swfoutput_setfillcolor(swfoutput* obj, U8 r, U8 g, U8 b, U8 a)
1862 swfoutput_internal*i = (swfoutput_internal*)obj->internal;
1863 if(obj->fillrgb.r == r &&
1864 obj->fillrgb.g == g &&
1865 obj->fillrgb.b == b &&
1866 obj->fillrgb.a == a) return;
1876 void swfoutput_setstrokecolor(swfoutput* obj, U8 r, U8 g, U8 b, U8 a)
1878 swfoutput_internal*i = (swfoutput_internal*)obj->internal;
1879 if(obj->strokergb.r == r &&
1880 obj->strokergb.g == g &&
1881 obj->strokergb.b == b &&
1882 obj->strokergb.a == a) return;
1886 obj->strokergb.r = r;
1887 obj->strokergb.g = g;
1888 obj->strokergb.b = b;
1889 obj->strokergb.a = a;
1892 void swfoutput_setlinewidth(struct swfoutput*obj, double _linewidth)
1894 swfoutput_internal*i = (swfoutput_internal*)obj->internal;
1895 if(i->linewidth == (U16)(_linewidth*20))
1900 i->linewidth = (U16)(_linewidth*20);
1904 void swfoutput_startclip(swfoutput*obj, SWF_OUTLINE*outline, struct swfmatrix*m)
1906 swfoutput_internal*i = (swfoutput_internal*)obj->internal;
1912 if(i->clippos >= 127)
1914 msg("<warning> Too many clip levels.");
1919 int olddrawmode = obj->drawmode;
1920 swfoutput_setdrawmode(obj, DRAWMODE_CLIP);
1921 swfoutput_drawpath(obj, outline, m);
1922 swf_ShapeSetEnd(i->tag);
1923 swfoutput_setdrawmode(obj, olddrawmode);
1925 i->tag = swf_InsertTag(i->tag,ST_PLACEOBJECT2);
1926 i->cliptags[i->clippos] = i->tag;
1927 i->clipshapes[i->clippos] = i->shapeid;
1928 i->clipdepths[i->clippos] = i->depth++;
1931 endshape_noput(obj);
1934 void swfoutput_endclip(swfoutput*obj)
1936 swfoutput_internal*i = (swfoutput_internal*)obj->internal;
1943 msg("<error> Invalid end of clipping region");
1947 swf_ObjectPlaceClip(i->cliptags[i->clippos],i->clipshapes[i->clippos],i->clipdepths[i->clippos],&i->page_matrix,NULL,NULL,i->depth++);
1950 static void drawlink(struct swfoutput*obj, ActionTAG*,ActionTAG*, swfcoord*points, char mouseover);
1952 void swfoutput_linktourl(struct swfoutput*obj, char*url, swfcoord*points)
1954 swfoutput_internal*i = (swfoutput_internal*)obj->internal;
1956 if(!strncmp("http://pdf2swf:", url, 15)) {
1957 char*tmp = strdup(url);
1958 int l = strlen(tmp);
1961 swfoutput_namedlink(obj, tmp+15, points);
1971 if(config_opennewwindow)
1972 actions = action_GetUrl(0, url, "_parent");
1974 actions = action_GetUrl(0, url, "_this");
1975 actions = action_End(actions);
1977 drawlink(obj, actions, 0, points,0);
1979 void swfoutput_linktopage(struct swfoutput*obj, int page, swfcoord*points)
1981 swfoutput_internal*i = (swfoutput_internal*)obj->internal;
1989 actions = action_GotoFrame(0, page);
1990 actions = action_End(actions);
1992 drawlink(obj, actions, 0, points,0);
1995 /* Named Links (a.k.a. Acrobatmenu) are used to implement various gadgets
1996 of the viewer objects, like subtitles, index elements etc.
1998 void swfoutput_namedlink(struct swfoutput*obj, char*name, swfcoord*points)
2000 swfoutput_internal*i = (swfoutput_internal*)obj->internal;
2001 ActionTAG *actions1,*actions2;
2002 char*tmp = strdup(name);
2010 if(!strncmp(tmp, "call:", 5))
2012 char*x = strchr(&tmp[5], ':');
2014 actions1 = action_PushInt(0, 0); //number of parameters (0)
2015 actions1 = action_PushString(actions1, &tmp[5]); //function name
2016 actions1 = action_CallFunction(actions1);
2019 actions1 = action_PushString(0, x+1); //parameter
2020 actions1 = action_PushInt(actions1, 1); //number of parameters (1)
2021 actions1 = action_PushString(actions1, &tmp[5]); //function name
2022 actions1 = action_CallFunction(actions1);
2024 actions2 = action_End(0);
2029 actions1 = action_PushString(0, "/:subtitle");
2030 actions1 = action_PushString(actions1, name);
2031 actions1 = action_SetVariable(actions1);
2032 actions1 = action_End(actions1);
2034 actions2 = action_PushString(0, "/:subtitle");
2035 actions2 = action_PushString(actions2, "");
2036 actions2 = action_SetVariable(actions2);
2037 actions2 = action_End(actions2);
2040 drawlink(obj, actions1, actions2, points,mouseover);
2042 swf_ActionFree(actions1);
2043 swf_ActionFree(actions2);
2047 static void drawlink(struct swfoutput*obj, ActionTAG*actions1, ActionTAG*actions2, swfcoord*points, char mouseover)
2049 swfoutput_internal*i = (swfoutput_internal*)obj->internal;
2054 struct plotxy p1,p2,p3,p4;
2058 double xmax=xmin=points[0].x,ymax=ymin=points[0].y;
2062 int buttonid = ++i->currentswfid;
2065 if(points[t].x>xmax) xmax=points[t].x;
2066 if(points[t].y>ymax) ymax=points[t].y;
2067 if(points[t].x<xmin) xmin=points[t].x;
2068 if(points[t].y<ymin) ymin=points[t].y;
2071 p1.x=points[0].x; p1.y=points[0].y; p2.x=points[1].x; p2.y=points[1].y;
2072 p3.x=points[2].x; p3.y=points[2].y; p4.x=points[3].x; p4.y=points[3].y;
2074 /* the following code subtracts the upper left edge from all coordinates,
2075 and set's posx,posy so that ST_PLACEOBJECT is used with a matrix.
2076 Necessary for preprocessing with swfcombine. */
2077 posx = xmin; posy = ymin;
2078 p1.x-=posx;p2.x-=posx;p3.x-=posx;p4.x-=posx;
2079 p1.y-=posy;p2.y-=posy;p3.y-=posy;p4.y-=posy;
2080 xmin -= posx; ymin -= posy;
2081 xmax -= posx; ymax -= posy;
2084 myshapeid = ++i->currentswfid;
2085 i->tag = swf_InsertTag(i->tag,ST_DEFINESHAPE3);
2086 swf_ShapeNew(&i->shape);
2087 rgb.r = rgb.b = rgb.a = rgb.g = 0;
2088 fsid = swf_ShapeAddSolidFillStyle(i->shape,&rgb);
2089 swf_SetU16(i->tag, myshapeid);
2090 r.xmin = (int)(xmin*20);
2091 r.ymin = (int)(ymin*20);
2092 r.xmax = (int)(xmax*20);
2093 r.ymax = (int)(ymax*20);
2094 swf_SetRect(i->tag,&r);
2095 swf_SetShapeStyles(i->tag,i->shape);
2096 swf_ShapeCountBits(i->shape,NULL,NULL);
2097 swf_SetShapeBits(i->tag,i->shape);
2098 swf_ShapeSetAll(i->tag,i->shape,/*x*/0,/*y*/0,0,fsid,0);
2099 i->swflastx = i->swflasty = 0;
2100 moveto(obj, i->tag, p1);
2101 lineto(obj, i->tag, p2);
2102 lineto(obj, i->tag, p3);
2103 lineto(obj, i->tag, p4);
2104 lineto(obj, i->tag, p1);
2105 swf_ShapeSetEnd(i->tag);
2108 myshapeid2 = ++i->currentswfid;
2109 i->tag = swf_InsertTag(i->tag,ST_DEFINESHAPE3);
2110 swf_ShapeNew(&i->shape);
2111 rgb.r = rgb.b = rgb.a = rgb.g = 255;
2113 fsid = swf_ShapeAddSolidFillStyle(i->shape,&rgb);
2114 swf_SetU16(i->tag, myshapeid2);
2115 r.xmin = (int)(xmin*20);
2116 r.ymin = (int)(ymin*20);
2117 r.xmax = (int)(xmax*20);
2118 r.ymax = (int)(ymax*20);
2119 swf_SetRect(i->tag,&r);
2120 swf_SetShapeStyles(i->tag,i->shape);
2121 swf_ShapeCountBits(i->shape,NULL,NULL);
2122 swf_SetShapeBits(i->tag,i->shape);
2123 swf_ShapeSetAll(i->tag,i->shape,/*x*/0,/*y*/0,0,fsid,0);
2124 i->swflastx = i->swflasty = 0;
2125 moveto(obj, i->tag, p1);
2126 lineto(obj, i->tag, p2);
2127 lineto(obj, i->tag, p3);
2128 lineto(obj, i->tag, p4);
2129 lineto(obj, i->tag, p1);
2130 swf_ShapeSetEnd(i->tag);
2134 i->tag = swf_InsertTag(i->tag,ST_DEFINEBUTTON);
2135 swf_SetU16(i->tag,buttonid); //id
2136 swf_ButtonSetFlags(i->tag, 0); //menu=no
2137 swf_ButtonSetRecord(i->tag,0x01,myshapeid,i->depth,0,0);
2138 swf_ButtonSetRecord(i->tag,0x02,myshapeid2,i->depth,0,0);
2139 swf_ButtonSetRecord(i->tag,0x04,myshapeid2,i->depth,0,0);
2140 swf_ButtonSetRecord(i->tag,0x08,myshapeid,i->depth,0,0);
2141 swf_SetU8(i->tag,0);
2142 swf_ActionSet(i->tag,actions1);
2143 swf_SetU8(i->tag,0);
2147 i->tag = swf_InsertTag(i->tag,ST_DEFINEBUTTON2);
2148 swf_SetU16(i->tag,buttonid); //id
2149 swf_ButtonSetFlags(i->tag, 0); //menu=no
2150 swf_ButtonSetRecord(i->tag,0x01,myshapeid,i->depth,0,0);
2151 swf_ButtonSetRecord(i->tag,0x02,myshapeid2,i->depth,0,0);
2152 swf_ButtonSetRecord(i->tag,0x04,myshapeid2,i->depth,0,0);
2153 swf_ButtonSetRecord(i->tag,0x08,myshapeid,i->depth,0,0);
2154 swf_SetU8(i->tag,0); // end of button records
2155 swf_ButtonSetCondition(i->tag, BC_IDLE_OVERUP);
2156 swf_ActionSet(i->tag,actions1);
2158 swf_ButtonSetCondition(i->tag, BC_OVERUP_IDLE);
2159 swf_ActionSet(i->tag,actions2);
2160 swf_SetU8(i->tag,0);
2161 swf_ButtonPostProcess(i->tag, 2);
2163 swf_SetU8(i->tag,0);
2164 swf_ButtonPostProcess(i->tag, 1);
2168 i->tag = swf_InsertTag(i->tag,ST_PLACEOBJECT2);
2170 if(posx!=0 || posy!=0) {
2172 p.x = (int)(posx*20);
2173 p.y = (int)(posy*20);
2174 p = swf_TurnPoint(p, &i->page_matrix);
2179 swf_ObjectPlace(i->tag, buttonid, i->depth++,&m,0,0);
2182 swf_ObjectPlace(i->tag, buttonid, i->depth++,&i->page_matrix,0,0);
2186 static void drawimage(struct swfoutput*obj, int bitid, int sizex,int sizey,
2187 double x1,double y1,
2188 double x2,double y2,
2189 double x3,double y3,
2190 double x4,double y4)
2192 swfoutput_internal*i = (swfoutput_internal*)obj->internal;
2197 struct plotxy p1,p2,p3,p4;
2199 double xmax=x1,ymax=y1,xmin=x1,ymin=y1;
2200 if(x2>xmax) xmax=x2;
2201 if(y2>ymax) ymax=y2;
2202 if(x2<xmin) xmin=x2;
2203 if(y2<ymin) ymin=y2;
2204 if(x3>xmax) xmax=x3;
2205 if(y3>ymax) ymax=y3;
2206 if(x3<xmin) xmin=x3;
2207 if(y3<ymin) ymin=y3;
2208 if(x4>xmax) xmax=x4;
2209 if(y4>ymax) ymax=y4;
2210 if(x4<xmin) xmin=x4;
2211 if(y4<ymin) ymin=y4;
2217 {p1.x = (int)(p1.x*20)/20.0;
2218 p1.y = (int)(p1.y*20)/20.0;
2219 p2.x = (int)(p2.x*20)/20.0;
2220 p2.y = (int)(p2.y*20)/20.0;
2221 p3.x = (int)(p3.x*20)/20.0;
2222 p3.y = (int)(p3.y*20)/20.0;
2223 p4.x = (int)(p4.x*20)/20.0;
2224 p4.y = (int)(p4.y*20)/20.0;}
2227 m.sx = (int)(65536*20*(p4.x-p1.x)/sizex);
2228 m.r1 = -(int)(65536*20*(p4.y-p1.y)/sizex);
2229 m.r0 = (int)(65536*20*(p1.x-p2.x)/sizey);
2230 m.sy = -(int)(65536*20*(p1.y-p2.y)/sizey);
2232 m.tx = (int)(p1.x*20);
2233 m.ty = (int)(p1.y*20);
2236 myshapeid = ++i->currentswfid;
2237 i->tag = swf_InsertTag(i->tag,ST_DEFINESHAPE);
2239 swf_ShapeNew(&shape);
2240 //lsid = ShapeAddLineStyle(shape,linewidth,&obj->strokergb);
2241 //fsid = ShapeAddSolidFillStyle(shape,&obj->fillrgb);
2242 fsid = swf_ShapeAddBitmapFillStyle(shape,&m,bitid,1);
2243 swf_SetU16(i->tag, myshapeid);
2244 r.xmin = (int)(xmin*20);
2245 r.ymin = (int)(ymin*20);
2246 r.xmax = (int)(xmax*20);
2247 r.ymax = (int)(ymax*20);
2248 swf_SetRect(i->tag,&r);
2249 swf_SetShapeStyles(i->tag,shape);
2250 swf_ShapeCountBits(shape,NULL,NULL);
2251 swf_SetShapeBits(i->tag,shape);
2252 swf_ShapeSetAll(i->tag,shape,/*x*/0,/*y*/0,lsid,fsid,0);
2253 i->swflastx = i->swflasty = 0;
2254 moveto(obj, i->tag, p1);
2255 lineto(obj, i->tag, p2);
2256 lineto(obj, i->tag, p3);
2257 lineto(obj, i->tag, p4);
2258 lineto(obj, i->tag, p1);
2260 ShapeMoveTo (tag, shape, (int)(x1*20),(int)(y1*20));
2261 ShapeSetLine (tag, shape, (int)(x1*20);
2262 ShapeSetLine (tag, shape, x*20,0);
2263 ShapeSetLine (tag, shape, 0,-y*20);
2264 ShapeSetLine (tag, shape, -x*20,0);*/
2265 swf_ShapeSetEnd(i->tag);
2266 swf_ShapeFree(shape);
2269 i->tag = swf_InsertTag(i->tag,ST_PLACEOBJECT2);
2271 swf_ObjectPlace(i->tag,myshapeid,/*depth*/i->depth++,&i->page_matrix,NULL,NULL);
2274 int swfoutput_drawimagejpeg_old(struct swfoutput*obj, char*filename, int sizex,int sizey,
2275 double x1,double y1,
2276 double x2,double y2,
2277 double x3,double y3,
2278 double x4,double y4)
2280 swfoutput_internal*i = (swfoutput_internal*)obj->internal;
2287 int bitid = ++i->currentswfid;
2289 i->tag = swf_InsertTag(i->tag,ST_DEFINEBITSJPEG2);
2290 swf_SetU16(i->tag, bitid);
2291 if(swf_SetJPEGBits(i->tag, filename, config_jpegquality)<0) {
2292 swf_DeleteTag(i->tag);
2297 drawimage(obj, bitid, sizex, sizey, x1,y1,x2,y2,x3,y3,x4,y4);
2301 int swfoutput_drawimagejpeg(struct swfoutput*obj, RGBA*mem, int sizex,int sizey,
2302 double x1,double y1,
2303 double x2,double y2,
2304 double x3,double y3,
2305 double x4,double y4)
2307 swfoutput_internal*i = (swfoutput_internal*)obj->internal;
2316 int bitid = ++i->currentswfid;
2318 i->tag = swf_InsertTag(i->tag,ST_DEFINEBITSJPEG2);
2319 swf_SetU16(i->tag, bitid);
2320 swf_SetJPEGBits2(i->tag,sizex,sizey,mem,config_jpegquality);
2321 drawimage(obj, bitid, sizex, sizey, x1,y1,x2,y2,x3,y3,x4,y4);
2325 int swfoutput_drawimagelossless(struct swfoutput*obj, RGBA*mem, int sizex,int sizey,
2326 double x1,double y1,
2327 double x2,double y2,
2328 double x3,double y3,
2329 double x4,double y4)
2331 swfoutput_internal*i = (swfoutput_internal*)obj->internal;
2338 int bitid = ++i->currentswfid;
2340 i->tag = swf_InsertTag(i->tag,ST_DEFINEBITSLOSSLESS);
2341 swf_SetU16(i->tag, bitid);
2342 if(swf_SetLosslessBits(i->tag,sizex,sizey,mem, BMF_32BIT)<0) {
2343 swf_DeleteTag(i->tag);
2348 drawimage(obj, bitid, sizex, sizey, x1,y1,x2,y2,x3,y3,x4,y4);
2352 int swfoutput_drawimagelosslessN(struct swfoutput*obj, U8*mem, RGBA*pal, int sizex,int sizey,
2353 double x1,double y1,
2354 double x2,double y2,
2355 double x3,double y3,
2356 double x4,double y4, int n)
2358 swfoutput_internal*i = (swfoutput_internal*)obj->internal;
2368 /* SWF expects scanlines to be 4 byte aligned */
2371 mem2 = (U8*)malloc(BYTES_PER_SCANLINE(sizex)*sizey);
2373 for(y=0;y<sizey;y++)
2375 for(x=0;x<sizex;x++)
2376 *ptr++ = mem[y*sizex+x];
2377 ptr+= BYTES_PER_SCANLINE(sizex)-sizex;
2382 int bitid = ++i->currentswfid;
2384 i->tag = swf_InsertTag(i->tag,ST_DEFINEBITSLOSSLESS2);
2385 swf_SetU16(i->tag, bitid);
2386 if(swf_SetLosslessBitsIndexed(i->tag,sizex,sizey,mem, pal, n)<0) {
2387 swf_DeleteTag(i->tag);
2394 drawimage(obj, bitid, sizex, sizey, x1,y1,x2,y2,x3,y3,x4,y4);
2398 void swfoutput_drawimageagain(struct swfoutput*obj, int id, int sizex,int sizey,
2399 double x1,double y1,
2400 double x2,double y2,
2401 double x3,double y3,
2402 double x4,double y4)
2404 swfoutput_internal*i = (swfoutput_internal*)obj->internal;
2411 drawimage(obj, id, sizex, sizey, x1,y1,x2,y2,x3,y3,x4,y4);
2414 void swfoutput_setparameter(char*name, char*value)
2416 if(!strcmp(name, "drawonlyshapes")) {
2417 config_drawonlyshapes = atoi(value);
2418 } else if(!strcmp(name, "ignoredraworder")) {
2419 config_ignoredraworder = atoi(value);
2420 } else if(!strcmp(name, "filloverlap")) {
2421 config_filloverlap = atoi(value);
2422 } else if(!strcmp(name, "linksopennewwindow")) {
2423 config_opennewwindow = atoi(value);
2424 } else if(!strcmp(name, "opennewwindow")) {
2425 config_opennewwindow = atoi(value);
2426 } else if(!strcmp(name, "storeallcharacters")) {
2427 config_storeallcharacters = atoi(value);
2428 } else if(!strcmp(name, "enablezlib")) {
2429 config_enablezlib = atoi(value);
2430 } else if(!strcmp(name, "insertstop")) {
2431 config_insertstoptag = atoi(value);
2432 } else if(!strcmp(name, "protected")) {
2433 config_protect = atoi(value);
2434 } else if(!strcmp(name, "flashversion")) {
2435 config_flashversion = atoi(value);
2436 } else if(!strcmp(name, "minlinewidth")) {
2437 config_minlinewidth = atof(value);
2438 } else if(!strcmp(name, "jpegquality")) {
2439 int val = atoi(value);
2441 if(val>100) val=100;
2442 config_jpegquality = val;
2443 } else if(!strcmp(name, "splinequality")) {
2444 int v = atoi(value);
2445 v = 500-(v*5); // 100% = 0.25 pixel, 0% = 25 pixel
2447 config_splinemaxerror = v;
2448 } else if(!strcmp(name, "fontquality")) {
2449 int v = atoi(value);
2450 v = 500-(v*5); // 100% = 0.25 pixel, 0% = 25 pixel
2452 config_fontsplinemaxerror = v;
2454 fprintf(stderr, "unknown parameter: %s (=%s)\n", name, value);