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) ||
1732 /*bbox outside page?*/
1733 (i->bboxrect.xmax <= i->min_x*20 ||
1734 i->bboxrect.ymax <= i->min_y*20 ||
1735 i->bboxrect.xmin >= i->max_x*20 ||
1736 i->bboxrect.ymin >= i->max_y*20)
1739 // delete the shape again, we didn't do anything
1744 swf_ShapeSetEnd(i->tag);
1746 changeRect(obj, i->tag, i->bboxrectpos, &i->bboxrect);
1748 i->tag = swf_InsertTag(i->tag,ST_PLACEOBJECT2);
1751 swf_ObjectPlaceClip(i->tag,i->shapeid,i->depth++,&i->page_matrix,NULL,NULL,clipdepth);
1753 swf_ObjectPlace(i->tag,i->shapeid,/*depth*/i->depth++,&i->page_matrix,NULL,NULL);
1755 swf_ShapeFree(i->shape);
1758 i->bboxrectpos = -1;
1761 void swfoutput_finalize(struct swfoutput*obj)
1763 swfoutput_internal*i = (swfoutput_internal*)obj->internal;
1765 if(i->tag && i->tag->id == ST_END)
1766 return; //already done
1768 if(i->frameno == i->lastframeno) // fix: add missing pagefeed
1769 swfoutput_pagefeed(obj);
1772 fontlist_t *tmp,*iterator = i->fontlist;
1774 TAG*mtag = i->swf.firstTag;
1775 if(iterator->swffont) {
1776 mtag = swf_InsertTag(mtag, ST_DEFINEFONT2);
1777 /*if(!storeallcharacters)
1778 swf_FontReduce(iterator->swffont);*/
1779 swf_FontSetDefine2(mtag, iterator->swffont);
1782 iterator = iterator->next;
1784 i->tag = swf_InsertTag(i->tag,ST_END);
1785 TAG* tag = i->tag->prev;
1787 /* remove the removeobject2 tags between the last ST_SHOWFRAME
1788 and the ST_END- they confuse the flash player */
1789 while(tag->id == ST_REMOVEOBJECT2) {
1790 TAG* prev = tag->prev;
1796 SWF* swfoutput_get(struct swfoutput*obj)
1798 swfoutput_internal*i = (swfoutput_internal*)obj->internal;
1800 swfoutput_finalize(obj);
1802 return swf_CopySWF(&i->swf);
1805 void swfoutput_getdimensions(struct swfoutput*obj, int*x1, int*y1, int*x2, int*y2)
1807 swfoutput_internal*i = (swfoutput_internal*)obj->internal;
1808 if(x1) *x1 = i->swf.movieSize.xmin/20;
1809 if(y1) *y1 = i->swf.movieSize.ymin/20;
1810 if(x2) *x2 = i->swf.movieSize.xmax/20;
1811 if(y2) *y2 = i->swf.movieSize.ymax/20;
1814 int swfoutput_save(struct swfoutput* obj, char*filename)
1816 swfoutput_internal*i = (swfoutput_internal*)obj->internal;
1817 swfoutput_finalize(obj);
1821 fi = open(filename, O_BINARY|O_CREAT|O_TRUNC|O_WRONLY, 0777);
1826 msg("<fatal> Could not create \"%s\". ", FIXNULL(filename));
1830 if(config_enablezlib || config_flashversion>=6) {
1831 if FAILED(swf_WriteSWC(fi,&i->swf))
1832 msg("<error> WriteSWC() failed.\n");
1834 if FAILED(swf_WriteSWF(fi,&i->swf))
1835 msg("<error> WriteSWF() failed.\n");
1840 msg("<notice> SWF written\n");
1844 /* Perform cleaning up, complete the swf, and write it out. */
1845 void swfoutput_destroy(struct swfoutput* obj)
1847 swfoutput_internal*i = (swfoutput_internal*)obj->internal;
1849 /* not initialized yet- nothing to destroy */
1853 fontlist_t *tmp,*iterator = i->fontlist;
1855 if(iterator->swffont) {
1856 swf_FontFree(iterator->swffont);iterator->swffont=0;
1859 iterator = iterator->next;
1862 swf_FreeTags(&i->swf);
1865 memset(obj, 0, sizeof(swfoutput));
1868 void swfoutput_setdrawmode(swfoutput* obj, int mode)
1870 swfoutput_internal*i = (swfoutput_internal*)obj->internal;
1871 obj->drawmode = mode;
1872 if(mode == DRAWMODE_FILL)
1874 else if(mode == DRAWMODE_EOFILL)
1876 else if(mode == DRAWMODE_STROKE)
1878 else if(mode == DRAWMODE_CLIP)
1880 else if(mode == DRAWMODE_EOCLIP)
1884 void swfoutput_setfillcolor(swfoutput* obj, U8 r, U8 g, U8 b, U8 a)
1886 swfoutput_internal*i = (swfoutput_internal*)obj->internal;
1887 if(obj->fillrgb.r == r &&
1888 obj->fillrgb.g == g &&
1889 obj->fillrgb.b == b &&
1890 obj->fillrgb.a == a) return;
1900 void swfoutput_setstrokecolor(swfoutput* obj, U8 r, U8 g, U8 b, U8 a)
1902 swfoutput_internal*i = (swfoutput_internal*)obj->internal;
1903 if(obj->strokergb.r == r &&
1904 obj->strokergb.g == g &&
1905 obj->strokergb.b == b &&
1906 obj->strokergb.a == a) return;
1910 obj->strokergb.r = r;
1911 obj->strokergb.g = g;
1912 obj->strokergb.b = b;
1913 obj->strokergb.a = a;
1916 void swfoutput_setlinewidth(struct swfoutput*obj, double _linewidth)
1918 swfoutput_internal*i = (swfoutput_internal*)obj->internal;
1919 if(i->linewidth == (U16)(_linewidth*20))
1924 i->linewidth = (U16)(_linewidth*20);
1928 void swfoutput_startclip(swfoutput*obj, SWF_OUTLINE*outline, struct swfmatrix*m)
1930 swfoutput_internal*i = (swfoutput_internal*)obj->internal;
1936 if(i->clippos >= 127)
1938 msg("<warning> Too many clip levels.");
1943 int olddrawmode = obj->drawmode;
1944 swfoutput_setdrawmode(obj, DRAWMODE_CLIP);
1945 swfoutput_drawpath(obj, outline, m);
1946 swf_ShapeSetEnd(i->tag);
1947 swfoutput_setdrawmode(obj, olddrawmode);
1949 i->tag = swf_InsertTag(i->tag,ST_PLACEOBJECT2);
1950 i->cliptags[i->clippos] = i->tag;
1951 i->clipshapes[i->clippos] = i->shapeid;
1952 i->clipdepths[i->clippos] = i->depth++;
1955 endshape_noput(obj);
1958 void swfoutput_endclip(swfoutput*obj)
1960 swfoutput_internal*i = (swfoutput_internal*)obj->internal;
1967 msg("<error> Invalid end of clipping region");
1971 swf_ObjectPlaceClip(i->cliptags[i->clippos],i->clipshapes[i->clippos],i->clipdepths[i->clippos],&i->page_matrix,NULL,NULL,i->depth++);
1974 static void drawlink(struct swfoutput*obj, ActionTAG*,ActionTAG*, swfcoord*points, char mouseover);
1976 void swfoutput_linktourl(struct swfoutput*obj, char*url, swfcoord*points)
1978 swfoutput_internal*i = (swfoutput_internal*)obj->internal;
1980 if(!strncmp("http://pdf2swf:", url, 15)) {
1981 char*tmp = strdup(url);
1982 int l = strlen(tmp);
1985 swfoutput_namedlink(obj, tmp+15, points);
1995 if(config_opennewwindow)
1996 actions = action_GetUrl(0, url, "_parent");
1998 actions = action_GetUrl(0, url, "_this");
1999 actions = action_End(actions);
2001 drawlink(obj, actions, 0, points,0);
2003 void swfoutput_linktopage(struct swfoutput*obj, int page, swfcoord*points)
2005 swfoutput_internal*i = (swfoutput_internal*)obj->internal;
2013 actions = action_GotoFrame(0, page);
2014 actions = action_End(actions);
2016 drawlink(obj, actions, 0, points,0);
2019 /* Named Links (a.k.a. Acrobatmenu) are used to implement various gadgets
2020 of the viewer objects, like subtitles, index elements etc.
2022 void swfoutput_namedlink(struct swfoutput*obj, char*name, swfcoord*points)
2024 swfoutput_internal*i = (swfoutput_internal*)obj->internal;
2025 ActionTAG *actions1,*actions2;
2026 char*tmp = strdup(name);
2034 if(!strncmp(tmp, "call:", 5))
2036 char*x = strchr(&tmp[5], ':');
2038 actions1 = action_PushInt(0, 0); //number of parameters (0)
2039 actions1 = action_PushString(actions1, &tmp[5]); //function name
2040 actions1 = action_CallFunction(actions1);
2043 actions1 = action_PushString(0, x+1); //parameter
2044 actions1 = action_PushInt(actions1, 1); //number of parameters (1)
2045 actions1 = action_PushString(actions1, &tmp[5]); //function name
2046 actions1 = action_CallFunction(actions1);
2048 actions2 = action_End(0);
2053 actions1 = action_PushString(0, "/:subtitle");
2054 actions1 = action_PushString(actions1, name);
2055 actions1 = action_SetVariable(actions1);
2056 actions1 = action_End(actions1);
2058 actions2 = action_PushString(0, "/:subtitle");
2059 actions2 = action_PushString(actions2, "");
2060 actions2 = action_SetVariable(actions2);
2061 actions2 = action_End(actions2);
2064 drawlink(obj, actions1, actions2, points,mouseover);
2066 swf_ActionFree(actions1);
2067 swf_ActionFree(actions2);
2071 static void drawlink(struct swfoutput*obj, ActionTAG*actions1, ActionTAG*actions2, swfcoord*points, char mouseover)
2073 swfoutput_internal*i = (swfoutput_internal*)obj->internal;
2078 struct plotxy p1,p2,p3,p4;
2082 double xmax=xmin=points[0].x,ymax=ymin=points[0].y;
2086 int buttonid = ++i->currentswfid;
2089 if(points[t].x>xmax) xmax=points[t].x;
2090 if(points[t].y>ymax) ymax=points[t].y;
2091 if(points[t].x<xmin) xmin=points[t].x;
2092 if(points[t].y<ymin) ymin=points[t].y;
2095 p1.x=points[0].x; p1.y=points[0].y; p2.x=points[1].x; p2.y=points[1].y;
2096 p3.x=points[2].x; p3.y=points[2].y; p4.x=points[3].x; p4.y=points[3].y;
2098 /* the following code subtracts the upper left edge from all coordinates,
2099 and set's posx,posy so that ST_PLACEOBJECT is used with a matrix.
2100 Necessary for preprocessing with swfcombine. */
2101 posx = xmin; posy = ymin;
2102 p1.x-=posx;p2.x-=posx;p3.x-=posx;p4.x-=posx;
2103 p1.y-=posy;p2.y-=posy;p3.y-=posy;p4.y-=posy;
2104 xmin -= posx; ymin -= posy;
2105 xmax -= posx; ymax -= posy;
2108 myshapeid = ++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 = 0;
2112 fsid = swf_ShapeAddSolidFillStyle(i->shape,&rgb);
2113 swf_SetU16(i->tag, myshapeid);
2114 r.xmin = (int)(xmin*20);
2115 r.ymin = (int)(ymin*20);
2116 r.xmax = (int)(xmax*20);
2117 r.ymax = (int)(ymax*20);
2118 swf_SetRect(i->tag,&r);
2119 swf_SetShapeStyles(i->tag,i->shape);
2120 swf_ShapeCountBits(i->shape,NULL,NULL);
2121 swf_SetShapeBits(i->tag,i->shape);
2122 swf_ShapeSetAll(i->tag,i->shape,/*x*/0,/*y*/0,0,fsid,0);
2123 i->swflastx = i->swflasty = 0;
2124 moveto(obj, i->tag, p1);
2125 lineto(obj, i->tag, p2);
2126 lineto(obj, i->tag, p3);
2127 lineto(obj, i->tag, p4);
2128 lineto(obj, i->tag, p1);
2129 swf_ShapeSetEnd(i->tag);
2132 myshapeid2 = ++i->currentswfid;
2133 i->tag = swf_InsertTag(i->tag,ST_DEFINESHAPE3);
2134 swf_ShapeNew(&i->shape);
2135 rgb.r = rgb.b = rgb.a = rgb.g = 255;
2137 fsid = swf_ShapeAddSolidFillStyle(i->shape,&rgb);
2138 swf_SetU16(i->tag, myshapeid2);
2139 r.xmin = (int)(xmin*20);
2140 r.ymin = (int)(ymin*20);
2141 r.xmax = (int)(xmax*20);
2142 r.ymax = (int)(ymax*20);
2143 swf_SetRect(i->tag,&r);
2144 swf_SetShapeStyles(i->tag,i->shape);
2145 swf_ShapeCountBits(i->shape,NULL,NULL);
2146 swf_SetShapeBits(i->tag,i->shape);
2147 swf_ShapeSetAll(i->tag,i->shape,/*x*/0,/*y*/0,0,fsid,0);
2148 i->swflastx = i->swflasty = 0;
2149 moveto(obj, i->tag, p1);
2150 lineto(obj, i->tag, p2);
2151 lineto(obj, i->tag, p3);
2152 lineto(obj, i->tag, p4);
2153 lineto(obj, i->tag, p1);
2154 swf_ShapeSetEnd(i->tag);
2158 i->tag = swf_InsertTag(i->tag,ST_DEFINEBUTTON);
2159 swf_SetU16(i->tag,buttonid); //id
2160 swf_ButtonSetFlags(i->tag, 0); //menu=no
2161 swf_ButtonSetRecord(i->tag,0x01,myshapeid,i->depth,0,0);
2162 swf_ButtonSetRecord(i->tag,0x02,myshapeid2,i->depth,0,0);
2163 swf_ButtonSetRecord(i->tag,0x04,myshapeid2,i->depth,0,0);
2164 swf_ButtonSetRecord(i->tag,0x08,myshapeid,i->depth,0,0);
2165 swf_SetU8(i->tag,0);
2166 swf_ActionSet(i->tag,actions1);
2167 swf_SetU8(i->tag,0);
2171 i->tag = swf_InsertTag(i->tag,ST_DEFINEBUTTON2);
2172 swf_SetU16(i->tag,buttonid); //id
2173 swf_ButtonSetFlags(i->tag, 0); //menu=no
2174 swf_ButtonSetRecord(i->tag,0x01,myshapeid,i->depth,0,0);
2175 swf_ButtonSetRecord(i->tag,0x02,myshapeid2,i->depth,0,0);
2176 swf_ButtonSetRecord(i->tag,0x04,myshapeid2,i->depth,0,0);
2177 swf_ButtonSetRecord(i->tag,0x08,myshapeid,i->depth,0,0);
2178 swf_SetU8(i->tag,0); // end of button records
2179 swf_ButtonSetCondition(i->tag, BC_IDLE_OVERUP);
2180 swf_ActionSet(i->tag,actions1);
2182 swf_ButtonSetCondition(i->tag, BC_OVERUP_IDLE);
2183 swf_ActionSet(i->tag,actions2);
2184 swf_SetU8(i->tag,0);
2185 swf_ButtonPostProcess(i->tag, 2);
2187 swf_SetU8(i->tag,0);
2188 swf_ButtonPostProcess(i->tag, 1);
2192 i->tag = swf_InsertTag(i->tag,ST_PLACEOBJECT2);
2194 if(posx!=0 || posy!=0) {
2196 p.x = (int)(posx*20);
2197 p.y = (int)(posy*20);
2198 p = swf_TurnPoint(p, &i->page_matrix);
2203 swf_ObjectPlace(i->tag, buttonid, i->depth++,&m,0,0);
2206 swf_ObjectPlace(i->tag, buttonid, i->depth++,&i->page_matrix,0,0);
2210 static void drawimage(struct swfoutput*obj, int bitid, int sizex,int sizey,
2211 double x1,double y1,
2212 double x2,double y2,
2213 double x3,double y3,
2214 double x4,double y4)
2216 swfoutput_internal*i = (swfoutput_internal*)obj->internal;
2221 struct plotxy p1,p2,p3,p4;
2223 double xmax=x1,ymax=y1,xmin=x1,ymin=y1;
2224 if(x2>xmax) xmax=x2;
2225 if(y2>ymax) ymax=y2;
2226 if(x2<xmin) xmin=x2;
2227 if(y2<ymin) ymin=y2;
2228 if(x3>xmax) xmax=x3;
2229 if(y3>ymax) ymax=y3;
2230 if(x3<xmin) xmin=x3;
2231 if(y3<ymin) ymin=y3;
2232 if(x4>xmax) xmax=x4;
2233 if(y4>ymax) ymax=y4;
2234 if(x4<xmin) xmin=x4;
2235 if(y4<ymin) ymin=y4;
2241 {p1.x = (int)(p1.x*20)/20.0;
2242 p1.y = (int)(p1.y*20)/20.0;
2243 p2.x = (int)(p2.x*20)/20.0;
2244 p2.y = (int)(p2.y*20)/20.0;
2245 p3.x = (int)(p3.x*20)/20.0;
2246 p3.y = (int)(p3.y*20)/20.0;
2247 p4.x = (int)(p4.x*20)/20.0;
2248 p4.y = (int)(p4.y*20)/20.0;}
2251 m.sx = (int)(65536*20*(p4.x-p1.x)/sizex);
2252 m.r1 = -(int)(65536*20*(p4.y-p1.y)/sizex);
2253 m.r0 = (int)(65536*20*(p1.x-p2.x)/sizey);
2254 m.sy = -(int)(65536*20*(p1.y-p2.y)/sizey);
2256 m.tx = (int)(p1.x*20);
2257 m.ty = (int)(p1.y*20);
2260 myshapeid = ++i->currentswfid;
2261 i->tag = swf_InsertTag(i->tag,ST_DEFINESHAPE);
2263 swf_ShapeNew(&shape);
2264 //lsid = ShapeAddLineStyle(shape,linewidth,&obj->strokergb);
2265 //fsid = ShapeAddSolidFillStyle(shape,&obj->fillrgb);
2266 fsid = swf_ShapeAddBitmapFillStyle(shape,&m,bitid,1);
2267 swf_SetU16(i->tag, myshapeid);
2268 r.xmin = (int)(xmin*20);
2269 r.ymin = (int)(ymin*20);
2270 r.xmax = (int)(xmax*20);
2271 r.ymax = (int)(ymax*20);
2272 swf_SetRect(i->tag,&r);
2273 swf_SetShapeStyles(i->tag,shape);
2274 swf_ShapeCountBits(shape,NULL,NULL);
2275 swf_SetShapeBits(i->tag,shape);
2276 swf_ShapeSetAll(i->tag,shape,/*x*/0,/*y*/0,lsid,fsid,0);
2277 i->swflastx = i->swflasty = 0;
2278 moveto(obj, i->tag, p1);
2279 lineto(obj, i->tag, p2);
2280 lineto(obj, i->tag, p3);
2281 lineto(obj, i->tag, p4);
2282 lineto(obj, i->tag, p1);
2284 ShapeMoveTo (tag, shape, (int)(x1*20),(int)(y1*20));
2285 ShapeSetLine (tag, shape, (int)(x1*20);
2286 ShapeSetLine (tag, shape, x*20,0);
2287 ShapeSetLine (tag, shape, 0,-y*20);
2288 ShapeSetLine (tag, shape, -x*20,0);*/
2289 swf_ShapeSetEnd(i->tag);
2290 swf_ShapeFree(shape);
2293 i->tag = swf_InsertTag(i->tag,ST_PLACEOBJECT2);
2295 swf_ObjectPlace(i->tag,myshapeid,/*depth*/i->depth++,&i->page_matrix,NULL,NULL);
2298 int swfoutput_drawimagejpeg_old(struct swfoutput*obj, char*filename, int sizex,int sizey,
2299 double x1,double y1,
2300 double x2,double y2,
2301 double x3,double y3,
2302 double x4,double y4)
2304 swfoutput_internal*i = (swfoutput_internal*)obj->internal;
2311 int bitid = ++i->currentswfid;
2313 i->tag = swf_InsertTag(i->tag,ST_DEFINEBITSJPEG2);
2314 swf_SetU16(i->tag, bitid);
2315 if(swf_SetJPEGBits(i->tag, filename, config_jpegquality)<0) {
2316 swf_DeleteTag(i->tag);
2321 drawimage(obj, bitid, sizex, sizey, x1,y1,x2,y2,x3,y3,x4,y4);
2325 int swfoutput_drawimagejpeg(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;
2340 int bitid = ++i->currentswfid;
2342 i->tag = swf_InsertTag(i->tag,ST_DEFINEBITSJPEG2);
2343 swf_SetU16(i->tag, bitid);
2344 swf_SetJPEGBits2(i->tag,sizex,sizey,mem,config_jpegquality);
2345 drawimage(obj, bitid, sizex, sizey, x1,y1,x2,y2,x3,y3,x4,y4);
2349 int swfoutput_drawimagelossless(struct swfoutput*obj, RGBA*mem, int sizex,int sizey,
2350 double x1,double y1,
2351 double x2,double y2,
2352 double x3,double y3,
2353 double x4,double y4)
2355 swfoutput_internal*i = (swfoutput_internal*)obj->internal;
2362 int bitid = ++i->currentswfid;
2364 i->tag = swf_InsertTag(i->tag,ST_DEFINEBITSLOSSLESS);
2365 swf_SetU16(i->tag, bitid);
2366 if(swf_SetLosslessBits(i->tag,sizex,sizey,mem, BMF_32BIT)<0) {
2367 swf_DeleteTag(i->tag);
2372 drawimage(obj, bitid, sizex, sizey, x1,y1,x2,y2,x3,y3,x4,y4);
2376 int swfoutput_drawimagelosslessN(struct swfoutput*obj, U8*mem, RGBA*pal, int sizex,int sizey,
2377 double x1,double y1,
2378 double x2,double y2,
2379 double x3,double y3,
2380 double x4,double y4, int n)
2382 swfoutput_internal*i = (swfoutput_internal*)obj->internal;
2392 /* SWF expects scanlines to be 4 byte aligned */
2395 mem2 = (U8*)malloc(BYTES_PER_SCANLINE(sizex)*sizey);
2397 for(y=0;y<sizey;y++)
2399 for(x=0;x<sizex;x++)
2400 *ptr++ = mem[y*sizex+x];
2401 ptr+= BYTES_PER_SCANLINE(sizex)-sizex;
2406 int bitid = ++i->currentswfid;
2408 i->tag = swf_InsertTag(i->tag,ST_DEFINEBITSLOSSLESS2);
2409 swf_SetU16(i->tag, bitid);
2410 if(swf_SetLosslessBitsIndexed(i->tag,sizex,sizey,mem, pal, n)<0) {
2411 swf_DeleteTag(i->tag);
2418 drawimage(obj, bitid, sizex, sizey, x1,y1,x2,y2,x3,y3,x4,y4);
2422 void swfoutput_drawimageagain(struct swfoutput*obj, int id, int sizex,int sizey,
2423 double x1,double y1,
2424 double x2,double y2,
2425 double x3,double y3,
2426 double x4,double y4)
2428 swfoutput_internal*i = (swfoutput_internal*)obj->internal;
2435 drawimage(obj, id, sizex, sizey, x1,y1,x2,y2,x3,y3,x4,y4);
2438 void swfoutput_setparameter(char*name, char*value)
2440 if(!strcmp(name, "drawonlyshapes")) {
2441 config_drawonlyshapes = atoi(value);
2442 } else if(!strcmp(name, "ignoredraworder")) {
2443 config_ignoredraworder = atoi(value);
2444 } else if(!strcmp(name, "filloverlap")) {
2445 config_filloverlap = atoi(value);
2446 } else if(!strcmp(name, "linksopennewwindow")) {
2447 config_opennewwindow = atoi(value);
2448 } else if(!strcmp(name, "opennewwindow")) {
2449 config_opennewwindow = atoi(value);
2450 } else if(!strcmp(name, "storeallcharacters")) {
2451 config_storeallcharacters = atoi(value);
2452 } else if(!strcmp(name, "enablezlib")) {
2453 config_enablezlib = atoi(value);
2454 } else if(!strcmp(name, "insertstop")) {
2455 config_insertstoptag = atoi(value);
2456 } else if(!strcmp(name, "protected")) {
2457 config_protect = atoi(value);
2458 } else if(!strcmp(name, "flashversion")) {
2459 config_flashversion = atoi(value);
2460 } else if(!strcmp(name, "minlinewidth")) {
2461 config_minlinewidth = atof(value);
2462 } else if(!strcmp(name, "jpegquality")) {
2463 int val = atoi(value);
2465 if(val>100) val=100;
2466 config_jpegquality = val;
2467 } else if(!strcmp(name, "splinequality")) {
2468 int v = atoi(value);
2469 v = 500-(v*5); // 100% = 0.25 pixel, 0% = 25 pixel
2471 config_splinemaxerror = v;
2472 } else if(!strcmp(name, "fontquality")) {
2473 int v = atoi(value);
2474 v = 500-(v*5); // 100% = 0.25 pixel, 0% = 25 pixel
2476 config_fontsplinemaxerror = v;
2478 fprintf(stderr, "unknown parameter: %s (=%s)\n", name, value);