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;
811 double previousx = x, previousy = y;
813 x += (tmp->dest.x/(float)0xffff);
814 y += (tmp->dest.y/(float)0xffff);
816 if(!tmp || tmp->type == SWF_PATHTYPE_MOVE) {
818 if(fabs(lastx-previousx)<0.001 && fabs(lasty-previousy)<0.001) {
819 /* endpoints match- the path is closed.
820 Don't bother to draw endings */
821 drawShortPath(obj, lastx, lasty, m, last);
823 if(last->type == SWF_PATHTYPE_LINE && t1linelen(obj,last)>line_width*2 &&
824 lastwasline && line_cap != LINE_CAP_ROUND)
825 drawShortPathWithStraightEnds(obj, lastx, lasty, m, last, valid, line_cap, line_join, line_width);
827 drawShortPathWithEnds(obj, lastx, lasty, m, last, valid, line_cap, line_join, line_width);
837 last = tmp; //remember last stroke start (first segment after moveto)
841 if(tmp && tmp->type == SWF_PATHTYPE_LINE && t1linelen(obj,tmp)>line_width*2)
847 tmp->link->last = tmp; // make sure list is properly linked in both directions
852 static inline int colorcompare(struct swfoutput*obj, RGBA*a,RGBA*b)
864 static SRECT getcharacterbbox(struct swfoutput*obj, SWFFONT*font)
866 swfoutput_internal*i = (swfoutput_internal*)obj->internal;
869 memset(&r, 0, sizeof(r));
872 if(debug) printf("\n");
873 for(t=0;t<i->chardatapos;t++)
875 if(i->chardata[t].fontid != font->id) {
876 msg("<error> Internal error: fontid %d != fontid %d", i->chardata[t].fontid, font->id);
879 SRECT b = font->layout->bounds[i->chardata[t].charid];
880 b.xmin *= i->chardata[t].size;
881 b.ymin *= i->chardata[t].size;
882 b.xmax *= i->chardata[t].size;
883 b.ymax *= i->chardata[t].size;
888 b.xmin += i->chardata[t].x;
889 b.ymin += i->chardata[t].y;
890 b.xmax += i->chardata[t].x;
891 b.ymax += i->chardata[t].y;
893 /* until we solve the INTERNAL_SCALING problem (see below)
894 make sure the bounding box is big enough */
900 if(debug) printf("(%f,%f,%f,%f) -> (%f,%f,%f,%f) [font %d/%d, char %d]\n",
901 font->layout->bounds[i->chardata[t].charid].xmin/20.0,
902 font->layout->bounds[i->chardata[t].charid].ymin/20.0,
903 font->layout->bounds[i->chardata[t].charid].xmax/20.0,
904 font->layout->bounds[i->chardata[t].charid].ymax/20.0,
909 i->chardata[t].fontid,
911 i->chardata[t].charid
913 swf_ExpandRect2(&r, &b);
915 if(debug) printf("-----> (%f,%f,%f,%f)\n",
923 static void putcharacters(struct swfoutput*obj, TAG*tag)
925 swfoutput_internal*i = (swfoutput_internal*)obj->internal;
929 color.r = i->chardata[0].color.r^255;
938 int charadvance[128];
941 int glyphbits=1; //TODO: can this be zero?
944 if(tag->id != ST_DEFINETEXT &&
945 tag->id != ST_DEFINETEXT2) {
946 msg("<error> internal error: putcharacters needs an text tag, not %d\n",tag->id);
949 if(!i->chardatapos) {
950 msg("<warning> putcharacters called with zero characters");
953 for(pass = 0; pass < 2; pass++)
963 advancebits++; // add sign bit
964 swf_SetU8(tag, glyphbits);
965 swf_SetU8(tag, advancebits);
968 for(t=0;t<=i->chardatapos;t++)
970 if(lastfontid != i->chardata[t].fontid ||
971 lastx!=i->chardata[t].x ||
972 lasty!=i->chardata[t].y ||
973 !colorcompare(obj,&color, &i->chardata[t].color) ||
975 lastsize != i->chardata[t].size ||
978 if(charstorepos && pass==0)
981 for(s=0;s<charstorepos;s++)
983 while(charids[s]>=(1<<glyphbits))
985 while(charadvance[s]>=(1<<advancebits))
989 if(charstorepos && pass==1)
991 tag->writeBit = 0; // Q&D
992 swf_SetBits(tag, 0, 1); // GLYPH Record
993 swf_SetBits(tag, charstorepos, 7); // number of glyphs
995 for(s=0;s<charstorepos;s++)
997 swf_SetBits(tag, charids[s], glyphbits);
998 swf_SetBits(tag, charadvance[s], advancebits);
1003 if(pass == 1 && t<i->chardatapos)
1009 if(lastx != i->chardata[t].x ||
1010 lasty != i->chardata[t].y)
1012 newx = i->chardata[t].x;
1013 newy = i->chardata[t].y;
1019 if(!colorcompare(obj,&color, &i->chardata[t].color))
1021 color = i->chardata[t].color;
1024 font.id = i->chardata[t].fontid;
1025 if(lastfontid != i->chardata[t].fontid || lastsize != i->chardata[t].size)
1028 tag->writeBit = 0; // Q&D
1029 swf_TextSetInfoRecord(tag, newfont, i->chardata[t].size, newcolor, newx,newy);
1032 lastfontid = i->chardata[t].fontid;
1033 lastx = i->chardata[t].x;
1034 lasty = i->chardata[t].y;
1035 lastsize = i->chardata[t].size;
1038 if(t==i->chardatapos)
1042 int nextt = t==i->chardatapos-1?t:t+1;
1043 int rel = i->chardata[nextt].x-i->chardata[t].x;
1044 if(rel>=0 && (rel<(1<<(advancebits-1)) || pass==0)) {
1046 lastx=i->chardata[nextt].x;
1050 lastx=i->chardata[t].x;
1052 charids[charstorepos] = i->chardata[t].charid;
1053 charadvance[charstorepos] = advance;
1060 static void putcharacter(struct swfoutput*obj, int fontid, int charid,
1061 int x,int y, int size)
1063 swfoutput_internal*i = (swfoutput_internal*)obj->internal;
1064 if(i->chardatapos == CHARDATAMAX)
1066 msg("<warning> Character buffer too small. SWF will be slightly bigger");
1070 i->chardata[i->chardatapos].fontid = fontid;
1071 i->chardata[i->chardatapos].charid = charid;
1072 i->chardata[i->chardatapos].x = x;
1073 i->chardata[i->chardatapos].y = y;
1074 i->chardata[i->chardatapos].color = obj->fillrgb;
1075 i->chardata[i->chardatapos].size = size;
1079 /* Notice: we can only put chars in the range -1639,1638 (-32768/20,32768/20).
1080 So if we set this value to high, the char coordinates will overflow.
1081 If we set it to low, however, the char positions will be inaccurate */
1082 #define FONT_INTERNAL_SIZE 4
1084 /* process a character. */
1085 static int drawchar(struct swfoutput*obj, SWFFONT *swffont, char*character, int charnr, int u, swfmatrix*m)
1087 swfoutput_internal*i = (swfoutput_internal*)obj->internal;
1089 msg("<warning> Font is NULL");
1093 int charid = getCharID(swffont, charnr, character, u);
1096 msg("<warning> Didn't find character '%s' (c=%d,u=%d) in current charset (%s, %d characters)",
1097 FIXNULL(character),charnr, u, FIXNULL((char*)swffont->name), swffont->numchars);
1108 float det = ((m->m11*m->m22)-(m->m21*m->m12));
1109 if(fabs(det) < 0.0005) {
1110 /* x direction equals y direction- the text is invisible */
1113 det = 20*FONT_INTERNAL_SIZE / det;
1116 p.x = (SCOORD)(( x * m->m22 - y * m->m12)*det);
1117 p.y = (SCOORD)((- x * m->m21 + y * m->m11)*det);
1119 putcharacter(obj, swffont->id, charid,p.x,p.y,FONT_INTERNAL_SIZE);
1120 swf_FontUseGlyph(swffont, charid);
1125 SWF_OUTLINE*outline = font->getOutline(character, charnr);
1126 char* charname = character;
1129 msg("<warning> Didn't find character '%s' (%d) in current charset (%s)",
1130 FIXNULL(character),charnr,FIXNULL(font->getName()));
1149 drawpath(tag, outline, &m2, 0);
1154 static void endtext(swfoutput*obj)
1156 swfoutput_internal*i = (swfoutput_internal*)obj->internal;
1160 i->tag = swf_InsertTag(i->tag,ST_DEFINETEXT);
1161 swf_SetU16(i->tag, i->textid);
1164 r = getcharacterbbox(obj, obj->swffont);
1166 swf_SetRect(i->tag,&r);
1169 swf_GetMatrix(0, &m); /* set unit matrix- the real matrix is in the placeobject */
1170 swf_SetMatrix(i->tag,&m);
1172 putcharacters(obj, i->tag);
1173 swf_SetU8(i->tag,0);
1174 i->tag = swf_InsertTag(i->tag,ST_PLACEOBJECT2);
1175 //swf_ObjectPlace(i->tag,i->textid,/*depth*/i->depth++,&i->page_matrix,NULL,NULL);
1177 swf_MatrixJoin(&m2,&obj->fontmatrix, &i->page_matrix);
1179 swf_ObjectPlace(i->tag,i->textid,/*depth*/i->depth++,&m2,NULL,NULL);
1184 /* draw a curved polygon. */
1185 void swfoutput_drawpath(swfoutput*obj, SWF_OUTLINE*outline,
1188 swfoutput_internal*i = (swfoutput_internal*)obj->internal;
1192 /* Multiple polygons in one shape don't overlap correctly,
1193 so we better start a new shape here if the polygon is filled
1195 if(i->shapeid>=0 && i->fill && !config_ignoredraworder) {
1208 drawpath(obj, outline,m, 0);
1211 void swfoutput_drawpath2poly(struct swfoutput*obj, SWF_OUTLINE*outline, struct swfmatrix*m, int line_join, int line_cap, double line_width, double miter_limit)
1213 swfoutput_internal*i = (swfoutput_internal*)obj->internal;
1218 assert(i->shapeid<0);
1222 drawpath2poly(obj, outline, m, 0, line_join, line_cap, line_width, miter_limit);
1225 int getCharID(SWFFONT *font, int charnr, char *charname, int u)
1228 if(charname && font->glyphnames) {
1229 for(t=0;t<font->numchars;t++) {
1230 if(font->glyphnames[t] && !strcmp(font->glyphnames[t],charname)) {
1231 msg("<debug> Char [%d,>%s<,%d] maps to %d\n", charnr, charname, u, t);
1235 /* if we didn't find the character, maybe
1236 we can find the capitalized version */
1237 for(t=0;t<font->numchars;t++) {
1238 if(font->glyphnames[t] && !strcasecmp(font->glyphnames[t],charname)) {
1239 msg("<debug> Char [%d,>>%s<<,%d] maps to %d\n", charnr, charname, u, t);
1245 if(u>0 && font->encoding != 255) {
1246 /* try to use the unicode id */
1247 if(u>=0 && u<font->maxascii && font->ascii2glyph[u]>=0) {
1248 msg("<debug> Char [%d,%s,>%d<] maps to %d\n", charnr, charname, u, font->ascii2glyph[u]);
1249 return font->ascii2glyph[u];
1253 if(font->encoding != FONT_ENCODING_UNICODE) {
1254 /* the following only works if the font encoding
1255 is US-ASCII based. It's needed for fonts which return broken unicode
1257 if(charnr>=0 && charnr<font->maxascii && font->ascii2glyph[charnr]>=0) {
1258 msg("<debug> Char [>%d<,%s,%d] maps to %d\n", charnr, charname, u, font->ascii2glyph[charnr]);
1259 return font->ascii2glyph[charnr];
1263 if(charnr>=0 && charnr<font->numchars) {
1264 msg("<debug> Char [>%d<,%s,%d] maps to %d\n", charnr, charname, u, charnr);
1272 /* set's the t1 font index of the font to use for swfoutput_drawchar(). */
1273 void swfoutput_setfont(struct swfoutput*obj, char*fontid, char*filename)
1275 swfoutput_internal*i = (swfoutput_internal*)obj->internal;
1276 fontlist_t*last=0,*iterator;
1278 msg("<error> No fontid");
1282 if(obj->swffont && obj->swffont->name && !strcmp((char*)obj->swffont->name,fontid))
1285 /* TODO: remove the need for this (enhance getcharacterbbox so that it can cope
1286 with multiple fonts */
1289 iterator = i->fontlist;
1291 if(!strcmp((char*)iterator->swffont->name,fontid)) {
1292 obj->swffont = iterator->swffont;
1296 iterator = iterator->next;
1300 msg("<error> No filename given for font- internal error?");
1304 swf_SetLoadFontParameters(64,/*skip unused*/0,/*full unicode*/1);
1305 SWFFONT*swffont = swf_LoadFont(filename);
1308 msg("<warning> Couldn't load font %s (%s)", fontid, filename);
1309 swffont = swf_LoadFont(0);
1312 if(swffont->glyph2ascii) {
1315 /* check whether the Unicode indices look o.k.
1316 If they don't, disable the unicode lookup by setting
1317 the encoding to 255 */
1318 for(t=0;t<swffont->numchars;t++) {
1319 int c = swffont->glyph2ascii[t];
1320 if(c && c < 32 && swffont->glyph[t].shape->bitlen > 16) {
1321 // the character maps into the unicode control character range
1322 // between 0001-001f. Yet it is not empty. Treat the one
1323 // mapping as broken, and look how many of those we find.
1328 msg("<warning> Font %s has bad unicode mapping", swffont->name);
1329 swffont->encoding = 255;
1333 swf_FontSetID(swffont, ++i->currentswfid);
1335 if(getScreenLogLevel() >= LOGLEVEL_DEBUG) {
1336 // print font information
1337 msg("<debug> Font %s (%s)",swffont->name, filename);
1338 msg("<debug> | ID: %d", swffont->id);
1339 msg("<debug> | Version: %d", swffont->version);
1340 msg("<debug> | Name: %s", fontid);
1341 msg("<debug> | Numchars: %d", swffont->numchars);
1342 msg("<debug> | Maxascii: %d", swffont->maxascii);
1343 msg("<debug> | Style: %d", swffont->style);
1344 msg("<debug> | Encoding: %d", swffont->encoding);
1345 for(int iii=0; iii<swffont->numchars;iii++) {
1346 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,
1347 swffont->layout->bounds[iii].xmin/20.0,
1348 swffont->layout->bounds[iii].ymin/20.0,
1349 swffont->layout->bounds[iii].xmax/20.0,
1350 swffont->layout->bounds[iii].ymax/20.0
1353 for(t=0;t<swffont->maxascii;t++) {
1354 if(swffont->ascii2glyph[t] == iii)
1355 msg("<debug> | - maps to %d",t);
1360 /* set the font name to the ID we use here */
1361 if(swffont->name) free(swffont->name);
1362 swffont->name = (U8*)strdup(fontid);
1364 iterator = new fontlist_t;
1365 iterator->swffont = swffont;
1369 last->next = iterator;
1371 i->fontlist = iterator;
1373 obj->swffont = swffont;
1376 int swfoutput_queryfont(struct swfoutput*obj, char*fontid)
1378 swfoutput_internal*i = (swfoutput_internal*)obj->internal;
1379 fontlist_t *iterator = i->fontlist;
1381 if(!strcmp((char*)iterator->swffont->name,fontid))
1383 iterator = iterator->next;
1388 /* set's the matrix which is to be applied to characters drawn by
1389 swfoutput_drawchar() */
1390 void swfoutput_setfontmatrix(struct swfoutput*obj,double m11,double m12,
1391 double m21,double m22)
1393 swfoutput_internal*i = (swfoutput_internal*)obj->internal;
1394 if(obj->fontm11 == m11 &&
1395 obj->fontm12 == m12 &&
1396 obj->fontm21 == m21 &&
1397 obj->fontm22 == m22)
1407 m.sx = (U32)(((obj->fontm11)*65536)/FONT_INTERNAL_SIZE); m.r1 = (U32)(((obj->fontm12)*65536)/FONT_INTERNAL_SIZE);
1408 m.r0 = (U32)(((obj->fontm21)*65536)/FONT_INTERNAL_SIZE); m.sy = (U32)(((obj->fontm22)*65536)/FONT_INTERNAL_SIZE);
1411 obj->fontmatrix = m;
1414 /* draws a character at x,y. */
1415 int swfoutput_drawchar(struct swfoutput* obj,double x,double y,char*character, int charnr, int u)
1417 swfoutput_internal*i = (swfoutput_internal*)obj->internal;
1419 m.m11 = obj->fontm11;
1420 m.m12 = obj->fontm12;
1421 m.m21 = obj->fontm21;
1422 m.m22 = obj->fontm22;
1425 return drawchar(obj, obj->swffont, character, charnr, u, &m);
1428 static void endpage(struct swfoutput*obj)
1430 swfoutput_internal*i = (swfoutput_internal*)obj->internal;
1436 swfoutput_endclip(obj);
1437 i->pagefinished = 1;
1440 void swfoutput_pagefeed(struct swfoutput*obj)
1442 swfoutput_internal*i = (swfoutput_internal*)obj->internal;
1444 if(!i->pagefinished)
1447 if(config_insertstoptag) {
1449 atag = action_Stop(atag);
1450 atag = action_End(atag);
1451 i->tag = swf_InsertTag(i->tag,ST_DOACTION);
1452 swf_ActionSet(i->tag,atag);
1454 i->tag = swf_InsertTag(i->tag,ST_SHOWFRAME);
1457 for(i->depth--;i->depth>=i->startdepth;i->depth--) {
1458 i->tag = swf_InsertTag(i->tag,ST_REMOVEOBJECT2);
1459 swf_SetU16(i->tag,i->depth);
1461 i->depth = i->startdepth;
1464 static void setBackground(struct swfoutput*obj, int x1, int y1, int x2, int y2)
1466 swfoutput_internal*i = (swfoutput_internal*)obj->internal;
1468 rgb.a = rgb.r = rgb.g = rgb.b = 0xff;
1472 int shapeid = ++i->currentswfid;
1477 i->tag = swf_InsertTag(i->tag, ST_DEFINESHAPE);
1479 fs1 = swf_ShapeAddSolidFillStyle(s, &rgb);
1480 swf_SetU16(i->tag,shapeid);
1481 swf_SetRect(i->tag,&r);
1482 swf_SetShapeHeader(i->tag,s);
1483 swf_ShapeSetAll(i->tag,s,x1,y1,ls1,fs1,0);
1484 swf_ShapeSetLine(i->tag,s,(x2-x1),0);
1485 swf_ShapeSetLine(i->tag,s,0,(y2-y1));
1486 swf_ShapeSetLine(i->tag,s,(x1-x2),0);
1487 swf_ShapeSetLine(i->tag,s,0,(y1-y2));
1488 swf_ShapeSetEnd(i->tag);
1490 i->tag = swf_InsertTag(i->tag, ST_PLACEOBJECT2);
1491 swf_ObjectPlace(i->tag,shapeid,i->depth++,0,0,0);
1492 i->tag = swf_InsertTag(i->tag, ST_PLACEOBJECT2);
1493 swf_ObjectPlaceClip(i->tag,shapeid,i->depth++,0,0,0,65535);
1494 i->cliptag = i->tag;
1497 void swfoutput_newpage(struct swfoutput*obj, int pageNum, int movex, int movey, int x1, int y1, int x2, int y2)
1499 swfoutput_internal*i = (swfoutput_internal*)obj->internal;
1500 if(!i->firstpage && !i->pagefinished)
1503 swf_GetMatrix(0, &i->page_matrix);
1504 i->page_matrix.tx = movex*20;
1505 i->page_matrix.ty = movey*20;
1507 if(i->cliptag && i->frameno == i->lastframeno) {
1509 swf_GetPlaceObject(i->cliptag, &obj);
1510 obj.clipdepth = i->depth++;
1511 swf_ResetTag(i->cliptag, i->cliptag->id);
1512 swf_SetPlaceObject(i->cliptag, &obj);
1513 swf_PlaceObjectFree(&obj);
1521 msg("<notice> processing page %d (%dx%d:%d:%d)", pageNum,x2-x1,y2-y1, x1, y1);
1523 x1*=20;y1*=20;x2*=20;y2*=20;
1525 /* set clipping/background rectangle */
1526 /* TODO: this should all be done in SWFOutputDev */
1527 setBackground(obj, x1, y1, x2, y2);
1529 /* increase SWF's bounding box */
1535 swf_ExpandRect2(&i->swf.movieSize, &r);
1537 i->lastframeno = i->frameno;
1539 i->pagefinished = 0;
1542 /* initialize the swf writer */
1543 void swfoutput_init(struct swfoutput* obj)
1545 memset(obj, 0, sizeof(struct swfoutput));
1546 obj->internal = init_internal_struct();
1548 swfoutput_internal*i = (swfoutput_internal*)obj->internal;
1553 msg("<verbose> initializing swf output for size %d*%d\n", i->max_x,i->max_y);
1558 memset(&i->swf,0x00,sizeof(SWF));
1560 i->swf.fileVersion = config_flashversion;
1561 i->swf.frameRate = 0x0040; // 1 frame per 4 seconds
1562 i->swf.movieSize.xmin = 0;
1563 i->swf.movieSize.ymin = 0;
1564 i->swf.movieSize.xmax = 0;
1565 i->swf.movieSize.ymax = 0;
1567 i->swf.firstTag = swf_InsertTag(NULL,ST_SETBACKGROUNDCOLOR);
1568 i->tag = i->swf.firstTag;
1569 rgb.a = rgb.r = rgb.g = rgb.b = 0xff;
1570 swf_SetRGB(i->tag,&rgb);
1572 i->startdepth = i->depth = 3; /* leave room for clip and background rectangle */
1575 i->tag = swf_InsertTag(i->tag, ST_PROTECT);
1578 static void startshape(struct swfoutput*obj)
1580 swfoutput_internal*i = (swfoutput_internal*)obj->internal;
1587 i->tag = swf_InsertTag(i->tag,ST_DEFINESHAPE);
1589 swf_ShapeNew(&i->shape);
1590 i->linestyleid = swf_ShapeAddLineStyle(i->shape,i->linewidth,&obj->strokergb);
1591 rgb.r = obj->fillrgb.r;
1592 rgb.g = obj->fillrgb.g;
1593 rgb.b = obj->fillrgb.b;
1594 i->fillstyleid = swf_ShapeAddSolidFillStyle(i->shape,&obj->fillrgb);
1596 i->shapeid = ++i->currentswfid;
1597 swf_SetU16(i->tag,i->shapeid); // ID
1599 i->bboxrectpos = i->tag->len;
1602 r.xmax = 20*i->max_x;
1603 r.ymax = 20*i->max_y;
1604 swf_SetRect(i->tag,&r);
1606 memset(&i->bboxrect, 0, sizeof(i->bboxrect));
1608 swf_SetShapeStyles(i->tag,i->shape);
1609 swf_ShapeCountBits(i->shape,NULL,NULL);
1610 swf_SetShapeBits(i->tag,i->shape);
1612 /* TODO: do we really need this? */
1613 swf_ShapeSetAll(i->tag,i->shape,/*x*/0,/*y*/0,i->linestyleid,0,0);
1614 i->swflastx=i->swflasty=0;
1616 i->shapeisempty = 1;
1619 static void starttext(struct swfoutput*obj)
1621 swfoutput_internal*i = (swfoutput_internal*)obj->internal;
1625 i->textid = ++i->currentswfid;
1627 i->swflastx=i->swflasty=0;
1631 /* TODO: move to ../lib/rfxswf */
1632 void changeRect(struct swfoutput*obj, TAG*tag, int pos, SRECT*newrect)
1634 swfoutput_internal*i = (swfoutput_internal*)obj->internal;
1635 /* determine length of old rect */
1639 swf_GetRect(tag, &old);
1640 swf_ResetReadBits(tag);
1641 int pos_end = tag->pos;
1643 int len = tag->len - pos_end;
1644 U8*data = (U8*)malloc(len);
1645 memcpy(data, &tag->data[pos_end], len);
1648 swf_SetRect(tag, newrect);
1649 swf_SetBlock(tag, data, len);
1651 tag->pos = tag->readBit = 0;
1654 void cancelshape(swfoutput*obj)
1656 swfoutput_internal*i = (swfoutput_internal*)obj->internal;
1657 /* delete old shape tag */
1659 i->tag = i->tag->prev;
1660 swf_DeleteTag(todel);
1661 if(i->shape) {swf_ShapeFree(i->shape);i->shape=0;}
1663 i->bboxrectpos = -1;
1666 void fixAreas(swfoutput*obj)
1668 swfoutput_internal*i = (swfoutput_internal*)obj->internal;
1669 if(!i->shapeisempty && i->fill &&
1670 (i->bboxrect.xmin == i->bboxrect.xmax ||
1671 i->bboxrect.ymin == i->bboxrect.ymax) &&
1672 config_minlinewidth >= 0.001
1674 msg("<debug> Shape has size 0: width=%.2f height=%.2f",
1675 (i->bboxrect.xmax-i->bboxrect.xmin)/20.0,
1676 (i->bboxrect.ymax-i->bboxrect.ymin)/20.0
1679 SRECT r = i->bboxrect;
1681 if(r.xmin == r.xmax && r.ymin == r.ymax) {
1682 /* this thing comes down to a single dot- nothing to fix here */
1688 RGBA save_col = obj->strokergb;
1689 int save_width = i->linewidth;
1691 obj->strokergb = obj->fillrgb;
1692 i->linewidth = (int)(config_minlinewidth*20);
1693 if(i->linewidth==0) i->linewidth = 1;
1697 moveto(obj, i->tag, r.xmin/20.0,r.ymin/20.0);
1698 lineto(obj, i->tag, r.xmax/20.0,r.ymax/20.0);
1700 obj->strokergb = save_col;
1701 i->linewidth = save_width;
1706 static void endshape_noput(swfoutput*obj)
1708 swfoutput_internal*i = (swfoutput_internal*)obj->internal;
1711 //changeRect(obj, i->tag, i->bboxrectpos, &i->bboxrect);
1714 swf_ShapeFree(i->shape);
1719 static void endshape(swfoutput*obj, int clipdepth)
1721 swfoutput_internal*i = (swfoutput_internal*)obj->internal;
1728 if(i->shapeisempty ||
1730 (i->bboxrect.xmin == i->bboxrect.xmax &&
1731 i->bboxrect.ymin == i->bboxrect.ymax))
1733 // delete the shape again, we didn't do anything
1738 swf_ShapeSetEnd(i->tag);
1740 changeRect(obj, i->tag, i->bboxrectpos, &i->bboxrect);
1742 i->tag = swf_InsertTag(i->tag,ST_PLACEOBJECT2);
1745 swf_ObjectPlaceClip(i->tag,i->shapeid,i->depth++,&i->page_matrix,NULL,NULL,clipdepth);
1747 swf_ObjectPlace(i->tag,i->shapeid,/*depth*/i->depth++,&i->page_matrix,NULL,NULL);
1749 swf_ShapeFree(i->shape);
1752 i->bboxrectpos = -1;
1755 void swfoutput_finalize(struct swfoutput*obj)
1757 swfoutput_internal*i = (swfoutput_internal*)obj->internal;
1759 if(i->tag && i->tag->id == ST_END)
1760 return; //already done
1762 if(i->frameno == i->lastframeno) // fix: add missing pagefeed
1763 swfoutput_pagefeed(obj);
1766 fontlist_t *tmp,*iterator = i->fontlist;
1768 TAG*mtag = i->swf.firstTag;
1769 if(iterator->swffont) {
1770 mtag = swf_InsertTag(mtag, ST_DEFINEFONT2);
1771 /*if(!storeallcharacters)
1772 swf_FontReduce(iterator->swffont);*/
1773 swf_FontSetDefine2(mtag, iterator->swffont);
1776 iterator = iterator->next;
1778 i->tag = swf_InsertTag(i->tag,ST_END);
1779 TAG* tag = i->tag->prev;
1781 /* remove the removeobject2 tags between the last ST_SHOWFRAME
1782 and the ST_END- they confuse the flash player */
1783 while(tag->id == ST_REMOVEOBJECT2) {
1784 TAG* prev = tag->prev;
1790 SWF* swfoutput_get(struct swfoutput*obj)
1792 swfoutput_internal*i = (swfoutput_internal*)obj->internal;
1794 swfoutput_finalize(obj);
1796 return swf_CopySWF(&i->swf);
1799 void swfoutput_getdimensions(struct swfoutput*obj, int*x1, int*y1, int*x2, int*y2)
1801 swfoutput_internal*i = (swfoutput_internal*)obj->internal;
1802 if(x1) *x1 = i->swf.movieSize.xmin/20;
1803 if(y1) *y1 = i->swf.movieSize.ymin/20;
1804 if(x2) *x2 = i->swf.movieSize.xmax/20;
1805 if(y2) *y2 = i->swf.movieSize.ymax/20;
1808 int swfoutput_save(struct swfoutput* obj, char*filename)
1810 swfoutput_internal*i = (swfoutput_internal*)obj->internal;
1811 swfoutput_finalize(obj);
1815 fi = open(filename, O_BINARY|O_CREAT|O_TRUNC|O_WRONLY, 0777);
1820 msg("<fatal> Could not create \"%s\". ", FIXNULL(filename));
1824 if(config_enablezlib || config_flashversion>=6) {
1825 if FAILED(swf_WriteSWC(fi,&i->swf))
1826 msg("<error> WriteSWC() failed.\n");
1828 if FAILED(swf_WriteSWF(fi,&i->swf))
1829 msg("<error> WriteSWF() failed.\n");
1834 msg("<notice> SWF written\n");
1838 /* Perform cleaning up, complete the swf, and write it out. */
1839 void swfoutput_destroy(struct swfoutput* obj)
1841 swfoutput_internal*i = (swfoutput_internal*)obj->internal;
1843 /* not initialized yet- nothing to destroy */
1847 fontlist_t *tmp,*iterator = i->fontlist;
1849 if(iterator->swffont) {
1850 swf_FontFree(iterator->swffont);iterator->swffont=0;
1853 iterator = iterator->next;
1856 swf_FreeTags(&i->swf);
1859 memset(obj, 0, sizeof(swfoutput));
1862 void swfoutput_setdrawmode(swfoutput* obj, int mode)
1864 swfoutput_internal*i = (swfoutput_internal*)obj->internal;
1865 obj->drawmode = mode;
1866 if(mode == DRAWMODE_FILL)
1868 else if(mode == DRAWMODE_EOFILL)
1870 else if(mode == DRAWMODE_STROKE)
1872 else if(mode == DRAWMODE_CLIP)
1874 else if(mode == DRAWMODE_EOCLIP)
1878 void swfoutput_setfillcolor(swfoutput* obj, U8 r, U8 g, U8 b, U8 a)
1880 swfoutput_internal*i = (swfoutput_internal*)obj->internal;
1881 if(obj->fillrgb.r == r &&
1882 obj->fillrgb.g == g &&
1883 obj->fillrgb.b == b &&
1884 obj->fillrgb.a == a) return;
1894 void swfoutput_setstrokecolor(swfoutput* obj, U8 r, U8 g, U8 b, U8 a)
1896 swfoutput_internal*i = (swfoutput_internal*)obj->internal;
1897 if(obj->strokergb.r == r &&
1898 obj->strokergb.g == g &&
1899 obj->strokergb.b == b &&
1900 obj->strokergb.a == a) return;
1904 obj->strokergb.r = r;
1905 obj->strokergb.g = g;
1906 obj->strokergb.b = b;
1907 obj->strokergb.a = a;
1910 void swfoutput_setlinewidth(struct swfoutput*obj, double _linewidth)
1912 swfoutput_internal*i = (swfoutput_internal*)obj->internal;
1913 if(i->linewidth == (U16)(_linewidth*20))
1918 i->linewidth = (U16)(_linewidth*20);
1922 void swfoutput_startclip(swfoutput*obj, SWF_OUTLINE*outline, struct swfmatrix*m)
1924 swfoutput_internal*i = (swfoutput_internal*)obj->internal;
1930 if(i->clippos >= 127)
1932 msg("<warning> Too many clip levels.");
1937 int olddrawmode = obj->drawmode;
1938 swfoutput_setdrawmode(obj, DRAWMODE_CLIP);
1939 swfoutput_drawpath(obj, outline, m);
1940 swf_ShapeSetEnd(i->tag);
1941 swfoutput_setdrawmode(obj, olddrawmode);
1943 i->tag = swf_InsertTag(i->tag,ST_PLACEOBJECT2);
1944 i->cliptags[i->clippos] = i->tag;
1945 i->clipshapes[i->clippos] = i->shapeid;
1946 i->clipdepths[i->clippos] = i->depth++;
1949 endshape_noput(obj);
1952 void swfoutput_endclip(swfoutput*obj)
1954 swfoutput_internal*i = (swfoutput_internal*)obj->internal;
1961 msg("<error> Invalid end of clipping region");
1965 swf_ObjectPlaceClip(i->cliptags[i->clippos],i->clipshapes[i->clippos],i->clipdepths[i->clippos],&i->page_matrix,NULL,NULL,i->depth++);
1968 static void drawlink(struct swfoutput*obj, ActionTAG*,ActionTAG*, swfcoord*points, char mouseover);
1970 void swfoutput_linktourl(struct swfoutput*obj, char*url, swfcoord*points)
1972 swfoutput_internal*i = (swfoutput_internal*)obj->internal;
1974 if(!strncmp("http://pdf2swf:", url, 15)) {
1975 char*tmp = strdup(url);
1976 int l = strlen(tmp);
1979 swfoutput_namedlink(obj, tmp+15, points);
1989 if(config_opennewwindow)
1990 actions = action_GetUrl(0, url, "_parent");
1992 actions = action_GetUrl(0, url, "_this");
1993 actions = action_End(actions);
1995 drawlink(obj, actions, 0, points,0);
1997 void swfoutput_linktopage(struct swfoutput*obj, int page, swfcoord*points)
1999 swfoutput_internal*i = (swfoutput_internal*)obj->internal;
2007 actions = action_GotoFrame(0, page);
2008 actions = action_End(actions);
2010 drawlink(obj, actions, 0, points,0);
2013 /* Named Links (a.k.a. Acrobatmenu) are used to implement various gadgets
2014 of the viewer objects, like subtitles, index elements etc.
2016 void swfoutput_namedlink(struct swfoutput*obj, char*name, swfcoord*points)
2018 swfoutput_internal*i = (swfoutput_internal*)obj->internal;
2019 ActionTAG *actions1,*actions2;
2020 char*tmp = strdup(name);
2028 if(!strncmp(tmp, "call:", 5))
2030 char*x = strchr(&tmp[5], ':');
2032 actions1 = action_PushInt(0, 0); //number of parameters (0)
2033 actions1 = action_PushString(actions1, &tmp[5]); //function name
2034 actions1 = action_CallFunction(actions1);
2037 actions1 = action_PushString(0, x+1); //parameter
2038 actions1 = action_PushInt(actions1, 1); //number of parameters (1)
2039 actions1 = action_PushString(actions1, &tmp[5]); //function name
2040 actions1 = action_CallFunction(actions1);
2042 actions2 = action_End(0);
2047 actions1 = action_PushString(0, "/:subtitle");
2048 actions1 = action_PushString(actions1, name);
2049 actions1 = action_SetVariable(actions1);
2050 actions1 = action_End(actions1);
2052 actions2 = action_PushString(0, "/:subtitle");
2053 actions2 = action_PushString(actions2, "");
2054 actions2 = action_SetVariable(actions2);
2055 actions2 = action_End(actions2);
2058 drawlink(obj, actions1, actions2, points,mouseover);
2060 swf_ActionFree(actions1);
2061 swf_ActionFree(actions2);
2065 static void drawlink(struct swfoutput*obj, ActionTAG*actions1, ActionTAG*actions2, swfcoord*points, char mouseover)
2067 swfoutput_internal*i = (swfoutput_internal*)obj->internal;
2072 struct plotxy p1,p2,p3,p4;
2076 double xmax=xmin=points[0].x,ymax=ymin=points[0].y;
2080 int buttonid = ++i->currentswfid;
2083 if(points[t].x>xmax) xmax=points[t].x;
2084 if(points[t].y>ymax) ymax=points[t].y;
2085 if(points[t].x<xmin) xmin=points[t].x;
2086 if(points[t].y<ymin) ymin=points[t].y;
2089 p1.x=points[0].x; p1.y=points[0].y; p2.x=points[1].x; p2.y=points[1].y;
2090 p3.x=points[2].x; p3.y=points[2].y; p4.x=points[3].x; p4.y=points[3].y;
2092 /* the following code subtracts the upper left edge from all coordinates,
2093 and set's posx,posy so that ST_PLACEOBJECT is used with a matrix.
2094 Necessary for preprocessing with swfcombine. */
2095 posx = xmin; posy = ymin;
2096 p1.x-=posx;p2.x-=posx;p3.x-=posx;p4.x-=posx;
2097 p1.y-=posy;p2.y-=posy;p3.y-=posy;p4.y-=posy;
2098 xmin -= posx; ymin -= posy;
2099 xmax -= posx; ymax -= posy;
2102 myshapeid = ++i->currentswfid;
2103 i->tag = swf_InsertTag(i->tag,ST_DEFINESHAPE3);
2104 swf_ShapeNew(&i->shape);
2105 rgb.r = rgb.b = rgb.a = rgb.g = 0;
2106 fsid = swf_ShapeAddSolidFillStyle(i->shape,&rgb);
2107 swf_SetU16(i->tag, myshapeid);
2108 r.xmin = (int)(xmin*20);
2109 r.ymin = (int)(ymin*20);
2110 r.xmax = (int)(xmax*20);
2111 r.ymax = (int)(ymax*20);
2112 swf_SetRect(i->tag,&r);
2113 swf_SetShapeStyles(i->tag,i->shape);
2114 swf_ShapeCountBits(i->shape,NULL,NULL);
2115 swf_SetShapeBits(i->tag,i->shape);
2116 swf_ShapeSetAll(i->tag,i->shape,/*x*/0,/*y*/0,0,fsid,0);
2117 i->swflastx = i->swflasty = 0;
2118 moveto(obj, i->tag, p1);
2119 lineto(obj, i->tag, p2);
2120 lineto(obj, i->tag, p3);
2121 lineto(obj, i->tag, p4);
2122 lineto(obj, i->tag, p1);
2123 swf_ShapeSetEnd(i->tag);
2126 myshapeid2 = ++i->currentswfid;
2127 i->tag = swf_InsertTag(i->tag,ST_DEFINESHAPE3);
2128 swf_ShapeNew(&i->shape);
2129 rgb.r = rgb.b = rgb.a = rgb.g = 255;
2131 fsid = swf_ShapeAddSolidFillStyle(i->shape,&rgb);
2132 swf_SetU16(i->tag, myshapeid2);
2133 r.xmin = (int)(xmin*20);
2134 r.ymin = (int)(ymin*20);
2135 r.xmax = (int)(xmax*20);
2136 r.ymax = (int)(ymax*20);
2137 swf_SetRect(i->tag,&r);
2138 swf_SetShapeStyles(i->tag,i->shape);
2139 swf_ShapeCountBits(i->shape,NULL,NULL);
2140 swf_SetShapeBits(i->tag,i->shape);
2141 swf_ShapeSetAll(i->tag,i->shape,/*x*/0,/*y*/0,0,fsid,0);
2142 i->swflastx = i->swflasty = 0;
2143 moveto(obj, i->tag, p1);
2144 lineto(obj, i->tag, p2);
2145 lineto(obj, i->tag, p3);
2146 lineto(obj, i->tag, p4);
2147 lineto(obj, i->tag, p1);
2148 swf_ShapeSetEnd(i->tag);
2152 i->tag = swf_InsertTag(i->tag,ST_DEFINEBUTTON);
2153 swf_SetU16(i->tag,buttonid); //id
2154 swf_ButtonSetFlags(i->tag, 0); //menu=no
2155 swf_ButtonSetRecord(i->tag,0x01,myshapeid,i->depth,0,0);
2156 swf_ButtonSetRecord(i->tag,0x02,myshapeid2,i->depth,0,0);
2157 swf_ButtonSetRecord(i->tag,0x04,myshapeid2,i->depth,0,0);
2158 swf_ButtonSetRecord(i->tag,0x08,myshapeid,i->depth,0,0);
2159 swf_SetU8(i->tag,0);
2160 swf_ActionSet(i->tag,actions1);
2161 swf_SetU8(i->tag,0);
2165 i->tag = swf_InsertTag(i->tag,ST_DEFINEBUTTON2);
2166 swf_SetU16(i->tag,buttonid); //id
2167 swf_ButtonSetFlags(i->tag, 0); //menu=no
2168 swf_ButtonSetRecord(i->tag,0x01,myshapeid,i->depth,0,0);
2169 swf_ButtonSetRecord(i->tag,0x02,myshapeid2,i->depth,0,0);
2170 swf_ButtonSetRecord(i->tag,0x04,myshapeid2,i->depth,0,0);
2171 swf_ButtonSetRecord(i->tag,0x08,myshapeid,i->depth,0,0);
2172 swf_SetU8(i->tag,0); // end of button records
2173 swf_ButtonSetCondition(i->tag, BC_IDLE_OVERUP);
2174 swf_ActionSet(i->tag,actions1);
2176 swf_ButtonSetCondition(i->tag, BC_OVERUP_IDLE);
2177 swf_ActionSet(i->tag,actions2);
2178 swf_SetU8(i->tag,0);
2179 swf_ButtonPostProcess(i->tag, 2);
2181 swf_SetU8(i->tag,0);
2182 swf_ButtonPostProcess(i->tag, 1);
2186 i->tag = swf_InsertTag(i->tag,ST_PLACEOBJECT2);
2188 if(posx!=0 || posy!=0) {
2190 p.x = (int)(posx*20);
2191 p.y = (int)(posy*20);
2192 p = swf_TurnPoint(p, &i->page_matrix);
2197 swf_ObjectPlace(i->tag, buttonid, i->depth++,&m,0,0);
2200 swf_ObjectPlace(i->tag, buttonid, i->depth++,&i->page_matrix,0,0);
2204 static void drawimage(struct swfoutput*obj, int bitid, int sizex,int sizey,
2205 double x1,double y1,
2206 double x2,double y2,
2207 double x3,double y3,
2208 double x4,double y4)
2210 swfoutput_internal*i = (swfoutput_internal*)obj->internal;
2215 struct plotxy p1,p2,p3,p4;
2217 double xmax=x1,ymax=y1,xmin=x1,ymin=y1;
2218 if(x2>xmax) xmax=x2;
2219 if(y2>ymax) ymax=y2;
2220 if(x2<xmin) xmin=x2;
2221 if(y2<ymin) ymin=y2;
2222 if(x3>xmax) xmax=x3;
2223 if(y3>ymax) ymax=y3;
2224 if(x3<xmin) xmin=x3;
2225 if(y3<ymin) ymin=y3;
2226 if(x4>xmax) xmax=x4;
2227 if(y4>ymax) ymax=y4;
2228 if(x4<xmin) xmin=x4;
2229 if(y4<ymin) ymin=y4;
2235 {p1.x = (int)(p1.x*20)/20.0;
2236 p1.y = (int)(p1.y*20)/20.0;
2237 p2.x = (int)(p2.x*20)/20.0;
2238 p2.y = (int)(p2.y*20)/20.0;
2239 p3.x = (int)(p3.x*20)/20.0;
2240 p3.y = (int)(p3.y*20)/20.0;
2241 p4.x = (int)(p4.x*20)/20.0;
2242 p4.y = (int)(p4.y*20)/20.0;}
2245 m.sx = (int)(65536*20*(p4.x-p1.x)/sizex);
2246 m.r1 = -(int)(65536*20*(p4.y-p1.y)/sizex);
2247 m.r0 = (int)(65536*20*(p1.x-p2.x)/sizey);
2248 m.sy = -(int)(65536*20*(p1.y-p2.y)/sizey);
2250 m.tx = (int)(p1.x*20) - 10;
2251 m.ty = (int)(p1.y*20) - 10;
2254 myshapeid = ++i->currentswfid;
2255 i->tag = swf_InsertTag(i->tag,ST_DEFINESHAPE);
2257 swf_ShapeNew(&shape);
2258 //lsid = ShapeAddLineStyle(shape,linewidth,&obj->strokergb);
2259 //fsid = ShapeAddSolidFillStyle(shape,&obj->fillrgb);
2260 fsid = swf_ShapeAddBitmapFillStyle(shape,&m,bitid,1);
2261 swf_SetU16(i->tag, myshapeid);
2262 r.xmin = (int)(xmin*20);
2263 r.ymin = (int)(ymin*20);
2264 r.xmax = (int)(xmax*20);
2265 r.ymax = (int)(ymax*20);
2266 swf_SetRect(i->tag,&r);
2267 swf_SetShapeStyles(i->tag,shape);
2268 swf_ShapeCountBits(shape,NULL,NULL);
2269 swf_SetShapeBits(i->tag,shape);
2270 swf_ShapeSetAll(i->tag,shape,/*x*/0,/*y*/0,lsid,fsid,0);
2271 i->swflastx = i->swflasty = 0;
2272 moveto(obj, i->tag, p1);
2273 lineto(obj, i->tag, p2);
2274 lineto(obj, i->tag, p3);
2275 lineto(obj, i->tag, p4);
2276 lineto(obj, i->tag, p1);
2278 ShapeMoveTo (tag, shape, (int)(x1*20),(int)(y1*20));
2279 ShapeSetLine (tag, shape, (int)(x1*20);
2280 ShapeSetLine (tag, shape, x*20,0);
2281 ShapeSetLine (tag, shape, 0,-y*20);
2282 ShapeSetLine (tag, shape, -x*20,0);*/
2283 swf_ShapeSetEnd(i->tag);
2284 swf_ShapeFree(shape);
2287 i->tag = swf_InsertTag(i->tag,ST_PLACEOBJECT2);
2289 swf_ObjectPlace(i->tag,myshapeid,/*depth*/i->depth++,&i->page_matrix,NULL,NULL);
2292 int swfoutput_drawimagejpeg_old(struct swfoutput*obj, char*filename, int sizex,int sizey,
2293 double x1,double y1,
2294 double x2,double y2,
2295 double x3,double y3,
2296 double x4,double y4)
2298 swfoutput_internal*i = (swfoutput_internal*)obj->internal;
2305 int bitid = ++i->currentswfid;
2307 i->tag = swf_InsertTag(i->tag,ST_DEFINEBITSJPEG2);
2308 swf_SetU16(i->tag, bitid);
2309 if(swf_SetJPEGBits(i->tag, filename, config_jpegquality)<0) {
2310 swf_DeleteTag(i->tag);
2315 drawimage(obj, bitid, sizex, sizey, x1,y1,x2,y2,x3,y3,x4,y4);
2319 int swfoutput_drawimagejpeg(struct swfoutput*obj, RGBA*mem, int sizex,int sizey,
2320 double x1,double y1,
2321 double x2,double y2,
2322 double x3,double y3,
2323 double x4,double y4)
2325 swfoutput_internal*i = (swfoutput_internal*)obj->internal;
2334 int bitid = ++i->currentswfid;
2336 i->tag = swf_InsertTag(i->tag,ST_DEFINEBITSJPEG2);
2337 swf_SetU16(i->tag, bitid);
2338 swf_SetJPEGBits2(i->tag,sizex,sizey,mem,config_jpegquality);
2339 drawimage(obj, bitid, sizex, sizey, x1,y1,x2,y2,x3,y3,x4,y4);
2343 int swfoutput_drawimagelossless(struct swfoutput*obj, RGBA*mem, int sizex,int sizey,
2344 double x1,double y1,
2345 double x2,double y2,
2346 double x3,double y3,
2347 double x4,double y4)
2349 swfoutput_internal*i = (swfoutput_internal*)obj->internal;
2356 int bitid = ++i->currentswfid;
2358 i->tag = swf_InsertTag(i->tag,ST_DEFINEBITSLOSSLESS);
2359 swf_SetU16(i->tag, bitid);
2360 if(swf_SetLosslessBits(i->tag,sizex,sizey,mem, BMF_32BIT)<0) {
2361 swf_DeleteTag(i->tag);
2366 drawimage(obj, bitid, sizex, sizey, x1,y1,x2,y2,x3,y3,x4,y4);
2370 int swfoutput_drawimagelosslessN(struct swfoutput*obj, U8*mem, RGBA*pal, int sizex,int sizey,
2371 double x1,double y1,
2372 double x2,double y2,
2373 double x3,double y3,
2374 double x4,double y4, int n)
2376 swfoutput_internal*i = (swfoutput_internal*)obj->internal;
2386 /* SWF expects scanlines to be 4 byte aligned */
2389 mem2 = (U8*)malloc(BYTES_PER_SCANLINE(sizex)*sizey);
2391 for(y=0;y<sizey;y++)
2393 for(x=0;x<sizex;x++)
2394 *ptr++ = mem[y*sizex+x];
2395 ptr+= BYTES_PER_SCANLINE(sizex)-sizex;
2400 int bitid = ++i->currentswfid;
2402 i->tag = swf_InsertTag(i->tag,ST_DEFINEBITSLOSSLESS2);
2403 swf_SetU16(i->tag, bitid);
2404 if(swf_SetLosslessBitsIndexed(i->tag,sizex,sizey,mem, pal, n)<0) {
2405 swf_DeleteTag(i->tag);
2412 drawimage(obj, bitid, sizex, sizey, x1,y1,x2,y2,x3,y3,x4,y4);
2416 void swfoutput_drawimageagain(struct swfoutput*obj, int id, int sizex,int sizey,
2417 double x1,double y1,
2418 double x2,double y2,
2419 double x3,double y3,
2420 double x4,double y4)
2422 swfoutput_internal*i = (swfoutput_internal*)obj->internal;
2429 drawimage(obj, id, sizex, sizey, x1,y1,x2,y2,x3,y3,x4,y4);
2432 void swfoutput_setparameter(char*name, char*value)
2434 if(!strcmp(name, "drawonlyshapes")) {
2435 config_drawonlyshapes = atoi(value);
2436 } else if(!strcmp(name, "ignoredraworder")) {
2437 config_ignoredraworder = atoi(value);
2438 } else if(!strcmp(name, "filloverlap")) {
2439 config_filloverlap = atoi(value);
2440 } else if(!strcmp(name, "linksopennewwindow")) {
2441 config_opennewwindow = atoi(value);
2442 } else if(!strcmp(name, "opennewwindow")) {
2443 config_opennewwindow = atoi(value);
2444 } else if(!strcmp(name, "storeallcharacters")) {
2445 config_storeallcharacters = atoi(value);
2446 } else if(!strcmp(name, "enablezlib")) {
2447 config_enablezlib = atoi(value);
2448 } else if(!strcmp(name, "insertstop")) {
2449 config_insertstoptag = atoi(value);
2450 } else if(!strcmp(name, "protected")) {
2451 config_protect = atoi(value);
2452 } else if(!strcmp(name, "flashversion")) {
2453 config_flashversion = atoi(value);
2454 } else if(!strcmp(name, "minlinewidth")) {
2455 config_minlinewidth = atof(value);
2456 } else if(!strcmp(name, "jpegquality")) {
2457 int val = atoi(value);
2459 if(val>100) val=100;
2460 config_jpegquality = val;
2461 } else if(!strcmp(name, "splinequality")) {
2462 int v = atoi(value);
2463 v = 500-(v*5); // 100% = 0.25 pixel, 0% = 25 pixel
2465 config_splinemaxerror = v;
2466 } else if(!strcmp(name, "fontquality")) {
2467 int v = atoi(value);
2468 v = 500-(v*5); // 100% = 0.25 pixel, 0% = 25 pixel
2470 config_fontsplinemaxerror = v;
2472 fprintf(stderr, "unknown parameter: %s (=%s)\n", name, value);