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
108 char fillstylechanged;
113 chardata_t chardata[CHARDATAMAX];
117 } swfoutput_internal;
119 static swfoutput_internal* init_internal_struct()
121 swfoutput_internal*i = (swfoutput_internal*)malloc(sizeof(swfoutput_internal));
122 memset(i, 0, sizeof(swfoutput_internal));
142 i->fillstylechanged = 0;
152 static void startshape(struct swfoutput* obj);
153 static void starttext(struct swfoutput* obj);
154 static void endshape(struct swfoutput* obj,int clip);
155 static void endtext(struct swfoutput* obj);
157 // matrix multiplication. changes p0
158 static void transform (plotxy*p0,struct swfmatrix*m)
161 x = m->m11*p0->x+m->m12*p0->y;
162 y = m->m21*p0->x+m->m22*p0->y;
167 // write a move-to command into the swf
168 static int moveto(struct swfoutput*obj, TAG*tag, plotxy p0)
170 swfoutput_internal*i = (swfoutput_internal*)obj->internal;
171 int rx = (int)(p0.x*20);
172 int ry = (int)(p0.y*20);
173 if(rx!=i->swflastx || ry!=i->swflasty || i->fillstylechanged) {
174 swf_ShapeSetMove (tag, i->shape, rx,ry);
175 i->fillstylechanged = 0;
182 static int moveto(struct swfoutput*obj, TAG*tag, float x, float y)
184 swfoutput_internal*i = (swfoutput_internal*)obj->internal;
188 return moveto(obj, tag, p);
190 static void addPointToBBox(struct swfoutput*obj, int px, int py)
192 swfoutput_internal*i = (swfoutput_internal*)obj->internal;
198 swf_ExpandRect(&i->bboxrect, p);
200 swf_ExpandRect3(&i->bboxrect, p, i->linewidth*3/2);
204 // write a line-to command into the swf
205 static void lineto(struct swfoutput*obj, TAG*tag, plotxy p0)
207 swfoutput_internal*i = (swfoutput_internal*)obj->internal;
208 int px = (int)(p0.x*20);
209 int py = (int)(p0.y*20);
210 int rx = (px-i->swflastx);
211 int ry = (py-i->swflasty);
212 /* we can't skip this for rx=0,ry=0, those
214 swf_ShapeSetLine (tag, i->shape, rx,ry);
216 addPointToBBox(obj, i->swflastx,i->swflasty);
217 addPointToBBox(obj, px,py);
223 static void lineto(struct swfoutput*obj, TAG*tag, double x, double y)
231 // write a spline-to command into the swf
232 static void splineto(struct swfoutput*obj, TAG*tag, plotxy control,plotxy end)
234 swfoutput_internal*i = (swfoutput_internal*)obj->internal;
235 int lastlastx = i->swflastx;
236 int lastlasty = i->swflasty;
238 int cx = ((int)(control.x*20)-i->swflastx);
239 int cy = ((int)(control.y*20)-i->swflasty);
242 int ex = ((int)(end.x*20)-i->swflastx);
243 int ey = ((int)(end.y*20)-i->swflasty);
247 if(cx || cy || ex || ey) {
248 swf_ShapeSetCurve(tag, i->shape, cx,cy,ex,ey);
249 addPointToBBox(obj, lastlastx ,lastlasty );
250 addPointToBBox(obj, lastlastx+cx,lastlasty+cy);
251 addPointToBBox(obj, lastlastx+cx+ex,lastlasty+cy+ey);
256 /* write a line, given two points and the transformation
258 static void line(struct swfoutput*obj, TAG*tag, plotxy p0, plotxy p1, struct swfmatrix*m)
262 moveto(obj, tag, p0);
263 lineto(obj, tag, p1);
266 /* write a cubic (!) spline. This involves calling the approximate()
267 function out of spline.cc to convert it to a quadratic spline. */
268 static void spline(struct swfoutput*obj, TAG*tag,plotxy p0,plotxy p1,plotxy p2,plotxy p3,struct swfmatrix*m)
270 swfoutput_internal*i = (swfoutput_internal*)obj->internal;
272 struct qspline q[128];
286 /* fonts use a different approximation than shapes */
287 num = cspline_approximate(&c, q, config_fontsplinemaxerror/20.0, APPROXIMATE_RECURSIVE_BINARY);
288 //num = cspline_approximate(&c, q, 10.0, APPROXIMATE_INFLECTION);
290 num = cspline_approximate(&c, q, config_splinemaxerror/20.0, APPROXIMATE_RECURSIVE_BINARY);
294 moveto(obj, tag,q[t].start);
295 splineto(obj, tag,q[t].control, q[t].end);
299 void resetdrawer(struct swfoutput*obj)
301 swfoutput_internal*i = (swfoutput_internal*)obj->internal;
306 static void stopFill(struct swfoutput*obj)
308 swfoutput_internal*i = (swfoutput_internal*)obj->internal;
311 swf_ShapeSetStyle(i->tag,i->shape,i->linestyleid,0x8000,0);
312 i->fillstylechanged = 1;
316 static void startFill(struct swfoutput*obj)
318 swfoutput_internal*i = (swfoutput_internal*)obj->internal;
321 swf_ShapeSetStyle(i->tag,i->shape,0x8000,i->fillstyleid,0);
322 i->fillstylechanged = 1;
327 /* draw an outline. These are generated by pdf2swf and by t1lib
328 (representing characters). */
329 void drawpath(struct swfoutput*obj, SWF_OUTLINE*outline, struct swfmatrix*m, int log)
331 swfoutput_internal*i = (swfoutput_internal*)obj->internal;
332 if( i->tag->id != ST_DEFINESHAPE &&
333 i->tag->id != ST_DEFINESHAPE2 &&
334 i->tag->id != ST_DEFINESHAPE3)
336 msg("<error> internal error: drawpath needs a shape tag, not %d\n",i->tag->id);
340 double lastx=0,lasty=0;
341 double firstx=0,firsty=0;
346 x += (outline->dest.x/(float)0xffff);
347 y += (outline->dest.y/(float)0xffff);
348 if(outline->type == SWF_PATHTYPE_MOVE)
350 //if(!init && fill && obj->drawmode != DRAWMODE_EOFILL && !ignoredraworder) {
351 if(config_filloverlap && !init && i->fill && obj->drawmode != DRAWMODE_EOFILL) {
352 /* drawmode=FILL (not EOFILL) means that
353 seperate shapes do not cancel each other out.
354 On SWF side, we need to start a new shape for each
355 closed polygon, because SWF only knows EOFILL.
362 if(((int)(lastx*20) != (int)(firstx*20) ||
363 (int)(lasty*20) != (int)(firsty*20)) &&
372 if(log) printf("fix: %f,%f -> %f,%f\n",p0.x,p0.y,p1.x,p1.y);
373 line(obj,i->tag, p0, p1, m);
379 else if(outline->type == SWF_PATHTYPE_LINE)
387 if(log) printf("line: %f,%f -> %f,%f\n",p0.x,p0.y,p1.x,p1.y);
388 line(obj,i->tag, p0,p1,m);
390 else if(outline->type == SWF_PATHTYPE_BEZIER)
396 SWF_BEZIERSEGMENT*o2 = (SWF_BEZIERSEGMENT*)outline;
399 p1.x=o2->C.x/(float)0xffff+lastx;
400 p1.y=o2->C.y/(float)0xffff+lasty;
401 p2.x=o2->B.x/(float)0xffff+lastx;
402 p2.y=o2->B.y/(float)0xffff+lasty;
405 if(log) printf("spline: %f,%f -> %f,%f\n",p3.x,p3.y,p0.x,p0.y);
406 spline(obj,i->tag,p0,p1,p2,p3,m);
409 msg("<error> drawpath: unknown outline type:%d\n", outline->type);
413 outline = outline->link;
415 if(((int)(lastx*20) != (int)(firstx*20) ||
416 (int)(lasty*20) != (int)(firsty*20)) &&
425 if(log) printf("fix: %f,%f -> %f,%f\n",p0.x,p0.y,p1.x,p1.y);
426 line(obj, i->tag, p0, p1, m);
430 plotxy getPivot(struct swfoutput*obj, SWF_OUTLINE*outline, int dir, double line_width, int end, int trytwo)
432 swfoutput_internal*i = (swfoutput_internal*)obj->internal;
433 SWF_PATHPOINT next, next2;
434 double xv=0,yv=0, xv2=0, yv2=0;
439 if(outline->type == SWF_PATHTYPE_LINE) {
440 next = outline->dest;
442 next = ((SWF_BEZIERSEGMENT*)outline)->B;
443 if(next.x==0 && next.y==0) {
444 next = ((SWF_BEZIERSEGMENT*)outline)->C;
446 if(next.x==0 && next.y==0) {
447 next = ((SWF_BEZIERSEGMENT*)outline)->dest;
451 if(trytwo && outline->last && outline->last->type != SWF_PATHTYPE_MOVE) {
452 if(outline->type == SWF_PATHTYPE_LINE) {
453 next2 = outline->last->dest;
455 SWF_PATHPOINT c = ((SWF_BEZIERSEGMENT*)(outline->last))->C;
456 SWF_PATHPOINT b = ((SWF_BEZIERSEGMENT*)(outline->last))->B;
457 next2.x = outline->last->dest.x - c.x;
458 next2.y = outline->last->dest.y - c.y;
459 if(next2.x==0 && next2.y==0) {
460 next2.x = outline->last->dest.x - b.x;
461 next2.y = outline->last->dest.y - b.y;
463 if(next2.x==0 && next2.y==0) {
464 next2.x = outline->last->dest.x;
465 next2.y = outline->last->dest.y;
471 if(outline->type == SWF_PATHTYPE_LINE) {
472 next = outline->dest;
474 SWF_PATHPOINT c = ((SWF_BEZIERSEGMENT*)outline)->C;
475 SWF_PATHPOINT b = ((SWF_BEZIERSEGMENT*)outline)->B;
476 next.x = outline->dest.x - c.x;
477 next.y = outline->dest.y - c.y;
478 if(next.x==0 && next.y==0) {
479 next.x = outline->dest.x - b.x;
480 next.y = outline->dest.y - b.y;
482 if(next.x==0 && next.y==0) {
483 next.x = outline->dest.x;
484 next.y = outline->dest.y;
488 if(trytwo && outline->link && outline->link->type != SWF_PATHTYPE_MOVE) {
489 if(outline->type == SWF_PATHTYPE_LINE) {
490 next2 = outline->link->dest;
492 next2 = ((SWF_BEZIERSEGMENT*)(outline->link))->B;
493 if(next2.x==0 && next2.y==0) {
494 next2 = ((SWF_BEZIERSEGMENT*)outline->link)->C;
496 if(next2.x==0 && next2.y==0) {
497 next2 = ((SWF_BEZIERSEGMENT*)outline->link)->dest;
505 xv = next.y/(float)0xffff;
506 yv = -next.x/(float)0xffff;
508 xv = -next.y/(float)0xffff;
509 yv = next.x/(float)0xffff;
512 double r = (line_width/2)/sqrt(xv*xv+yv*yv);
518 xv2 = next2.y/(float)0xffff;
519 yv2 = -next2.x/(float)0xffff;
521 xv2 = -next2.y/(float)0xffff;
522 yv2 = next2.x/(float)0xffff;
525 double r2 = (line_width/2)/sqrt(xv2*xv2+yv2*yv2);
530 double r3 = (line_width/2)/sqrt(xv*xv+yv*yv);
540 void drawShortPath(struct swfoutput*obj, double x, double y, struct swfmatrix* m, SWF_OUTLINE*outline)
542 swfoutput_internal*i = (swfoutput_internal*)obj->internal;
543 double lastx=x, lasty=y;
544 while (outline && outline->type != SWF_PATHTYPE_MOVE)
546 x += (outline->dest.x/(float)0xffff);
547 y += (outline->dest.y/(float)0xffff);
549 if(outline->type == SWF_PATHTYPE_LINE)
556 line(obj, i->tag, p0, p1, m);
558 else if(outline->type == SWF_PATHTYPE_BEZIER)
561 SWF_BEZIERSEGMENT*o2 = (SWF_BEZIERSEGMENT*)outline;
564 p1.x=o2->C.x/(float)0xffff+lastx;
565 p1.y=o2->C.y/(float)0xffff+lasty;
566 p2.x=o2->B.x/(float)0xffff+lastx;
567 p2.y=o2->B.y/(float)0xffff+lasty;
570 spline(obj, i->tag,p0,p1,p2,p3,m);
574 outline = outline->link;
578 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)
580 swfoutput_internal*i = (swfoutput_internal*)obj->internal;
584 if(line_cap == LINE_CAP_BUTT || line_cap == LINE_CAP_SQUARE) {
587 SWF_OUTLINE *last, *tmp=outline;
588 plotxy s,e,p0,p1,p2,p3,m0,m1,m2,m3;
594 while(tmp && tmp->type != SWF_PATHTYPE_MOVE) {
596 lx += (tmp->dest.x/(float)0xffff);
597 ly += (tmp->dest.y/(float)0xffff);
600 s = getPivot(obj, outline, 0, line_width, 0, 0);
601 e = getPivot(obj, last, 0, line_width, 1, 0);
603 if(line_cap == LINE_CAP_BUTT) {
604 /* make the clipping rectangle slighly bigger
605 than the line ending, so that it get's clipped
615 p2.x = x2 - s.y - s.x*ee;
616 p2.y = y2 + s.x - s.y*ee;
617 p3.x = x2 - s.y + s.x*ee;
618 p3.y = y2 + s.x + s.y*ee;
623 m2.x = lx + e.y - e.x*ee;
624 m2.y = ly - e.x - e.y*ee;
625 m3.x = lx + e.y + e.x*ee;
626 m3.y = ly - e.x + e.y*ee;
628 for(nr=0;nr<2;nr++) {
630 struct plotxy q0,q1,q2,q3,q4,q5;
633 if(line_cap == LINE_CAP_BUTT) {
635 /* FIXME: box should be smaller */
637 q1.x = i->sizex; q1.y = 0;
638 q2.x = i->sizex; q2.y = i->sizey;
639 q3.x = 0; q3.y = i->sizey;
641 /* FIXME: box should be smaller */
642 q0.x = i->sizex; q0.y = i->sizey;
643 q1.x = 0; q1.y = i->sizey;
645 q3.x = i->sizex; q3.y = 0;
649 moveto(obj, i->tag, q0);
650 lineto(obj, i->tag, q1);
651 lineto(obj, i->tag, q2);
652 lineto(obj, i->tag, q3);
653 lineto(obj, i->tag, q0);
656 lineto(obj, i->tag, q4);
659 line(obj, i->tag, p0, p1, m);
660 line(obj, i->tag, p1, p2, m);
661 line(obj, i->tag, p2, p3, m);
662 line(obj, i->tag, p3, p0, m);
664 if(line_cap == LINE_CAP_BUTT) {
665 lineto(obj, i->tag, q0);
666 endshape(obj, i->depth+2-nr);
678 drawShortPath(obj,x,y,m,outline);
680 if(line_cap == LINE_CAP_BUTT) {
686 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)
688 swfoutput_internal*i = (swfoutput_internal*)obj->internal;
689 plotxy d1,d2,p1,p2,p3,p4;
691 d1.x = (outline->dest.x/(float)0xffff);
692 d1.y = (outline->dest.y/(float)0xffff);
693 d2 = getPivot(obj, outline, 0, line_width, 0, 0);
695 assert(line_cap != LINE_CAP_ROUND);
696 if(line_cap == LINE_CAP_SQUARE) {
705 p2.x = x + d2.x + d1.x;
706 p2.y = y + d2.y + d1.y;
707 p3.x = x - d2.x + d1.x;
708 p3.y = y - d2.y + d1.y;
712 line(obj, i->tag, p1,p2, m);
713 line(obj, i->tag, p2,p3, m);
714 line(obj, i->tag, p3,p4, m);
715 line(obj, i->tag, p4,p1, m);
718 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)
720 swfoutput_internal*i = (swfoutput_internal*)obj->internal;
721 SWF_OUTLINE*tmp=outline;
724 assert(i->shapeid>=0);
727 drawT1toRect(obj, x, y, m,outline, num, line_cap, line_join, line_width);
729 while(tmp->link && tmp->link->type!=SWF_PATHTYPE_MOVE) {
730 xx += (tmp->dest.x/(float)0xffff);
731 yy += (tmp->dest.y/(float)0xffff);
735 assert(tmp->type == SWF_PATHTYPE_LINE);
736 assert(outline->type == SWF_PATHTYPE_LINE);
740 if(outline->link == tmp) {
741 /* the two straight line segments (which are everything we
742 need to draw) are very likely to overlap. To avoid that
743 they cancel each other out at the end points, start a new
744 shape for the second one */
745 endshape(obj,0);startshape(obj);
749 drawT1toRect(obj, xx, yy, m, tmp, num, line_cap, line_join, line_width);
751 if(outline->link != tmp)
753 stopFill(obj);stop=1;
755 tmp->type = SWF_PATHTYPE_MOVE;
756 x += (outline->dest.x/(float)0xffff);
757 y += (outline->dest.y/(float)0xffff);
758 outline = outline->link;
759 drawShortPath(obj, x, y, m, outline);
767 static int t1len(struct swfoutput*obj, SWF_OUTLINE*line)
769 swfoutput_internal*i = (swfoutput_internal*)obj->internal;
771 while(line && line->type != SWF_PATHTYPE_MOVE) {
778 static float t1linelen(struct swfoutput*obj, SWF_OUTLINE*line)
780 swfoutput_internal*i = (swfoutput_internal*)obj->internal;
782 x = (line->dest.x/(float)0xffff);
783 y = (line->dest.y/(float)0xffff);
784 return sqrt(x*x+y*y);
787 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)
789 swfoutput_internal*i = (swfoutput_internal*)obj->internal;
790 if( i->tag->id != ST_DEFINESHAPE &&
791 i->tag->id != ST_DEFINESHAPE2 &&
792 i->tag->id != ST_DEFINESHAPE3) {
793 msg("<error> internal error: drawpath needs a shape tag, not %d\n",i->tag->id);
796 assert(i->shapeid>=0);
798 double lastx=0,lasty=0;
801 SWF_OUTLINE*tmp = outline, *last = 0;
806 x += (tmp->dest.x/(float)0xffff);
807 y += (tmp->dest.y/(float)0xffff);
809 if(!tmp || tmp->type == SWF_PATHTYPE_MOVE) {
811 if(last->type == SWF_PATHTYPE_LINE && t1linelen(obj,last)>line_width*2 &&
812 lastwasline && line_cap != LINE_CAP_ROUND)
813 drawShortPathWithStraightEnds(obj, lastx, lasty, m, last, valid, line_cap, line_join, line_width);
815 drawShortPathWithEnds(obj, lastx, lasty, m, last, valid, line_cap, line_join, line_width);
829 if(tmp && tmp->type == SWF_PATHTYPE_LINE && t1linelen(obj,tmp)>line_width*2)
835 tmp->link->last = tmp; // make sure list is properly linked in both directions
840 static inline int colorcompare(struct swfoutput*obj, RGBA*a,RGBA*b)
852 static SRECT getcharacterbbox(struct swfoutput*obj, SWFFONT*font)
854 swfoutput_internal*i = (swfoutput_internal*)obj->internal;
857 memset(&r, 0, sizeof(r));
860 if(debug) printf("\n");
861 for(t=0;t<i->chardatapos;t++)
863 if(i->chardata[t].fontid != font->id) {
864 msg("<error> Internal error: fontid %d != fontid %d", i->chardata[t].fontid, font->id);
867 SRECT b = font->layout->bounds[i->chardata[t].charid];
868 b.xmin *= i->chardata[t].size;
869 b.ymin *= i->chardata[t].size;
870 b.xmax *= i->chardata[t].size;
871 b.ymax *= i->chardata[t].size;
876 b.xmin += i->chardata[t].x;
877 b.ymin += i->chardata[t].y;
878 b.xmax += i->chardata[t].x;
879 b.ymax += i->chardata[t].y;
881 /* until we solve the INTERNAL_SCALING problem (see below)
882 make sure the bounding box is big enough */
888 if(debug) printf("(%f,%f,%f,%f) -> (%f,%f,%f,%f) [font %d/%d, char %d]\n",
889 font->layout->bounds[i->chardata[t].charid].xmin/20.0,
890 font->layout->bounds[i->chardata[t].charid].ymin/20.0,
891 font->layout->bounds[i->chardata[t].charid].xmax/20.0,
892 font->layout->bounds[i->chardata[t].charid].ymax/20.0,
897 i->chardata[t].fontid,
899 i->chardata[t].charid
901 swf_ExpandRect2(&r, &b);
903 if(debug) printf("-----> (%f,%f,%f,%f)\n",
911 static void putcharacters(struct swfoutput*obj, TAG*tag)
913 swfoutput_internal*i = (swfoutput_internal*)obj->internal;
917 color.r = i->chardata[0].color.r^255;
926 int charadvance[128];
929 int glyphbits=1; //TODO: can this be zero?
932 if(tag->id != ST_DEFINETEXT &&
933 tag->id != ST_DEFINETEXT2) {
934 msg("<error> internal error: putcharacters needs an text tag, not %d\n",tag->id);
937 if(!i->chardatapos) {
938 msg("<warning> putcharacters called with zero characters");
941 for(pass = 0; pass < 2; pass++)
951 advancebits++; // add sign bit
952 swf_SetU8(tag, glyphbits);
953 swf_SetU8(tag, advancebits);
956 for(t=0;t<=i->chardatapos;t++)
958 if(lastfontid != i->chardata[t].fontid ||
959 lastx!=i->chardata[t].x ||
960 lasty!=i->chardata[t].y ||
961 !colorcompare(obj,&color, &i->chardata[t].color) ||
963 lastsize != i->chardata[t].size ||
966 if(charstorepos && pass==0)
969 for(s=0;s<charstorepos;s++)
971 while(charids[s]>=(1<<glyphbits))
973 while(charadvance[s]>=(1<<advancebits))
977 if(charstorepos && pass==1)
979 tag->writeBit = 0; // Q&D
980 swf_SetBits(tag, 0, 1); // GLYPH Record
981 swf_SetBits(tag, charstorepos, 7); // number of glyphs
983 for(s=0;s<charstorepos;s++)
985 swf_SetBits(tag, charids[s], glyphbits);
986 swf_SetBits(tag, charadvance[s], advancebits);
991 if(pass == 1 && t<i->chardatapos)
997 if(lastx != i->chardata[t].x ||
998 lasty != i->chardata[t].y)
1000 newx = i->chardata[t].x;
1001 newy = i->chardata[t].y;
1007 if(!colorcompare(obj,&color, &i->chardata[t].color))
1009 color = i->chardata[t].color;
1012 font.id = i->chardata[t].fontid;
1013 if(lastfontid != i->chardata[t].fontid || lastsize != i->chardata[t].size)
1016 tag->writeBit = 0; // Q&D
1017 swf_TextSetInfoRecord(tag, newfont, i->chardata[t].size, newcolor, newx,newy);
1020 lastfontid = i->chardata[t].fontid;
1021 lastx = i->chardata[t].x;
1022 lasty = i->chardata[t].y;
1023 lastsize = i->chardata[t].size;
1026 if(t==i->chardatapos)
1030 int nextt = t==i->chardatapos-1?t:t+1;
1031 int rel = i->chardata[nextt].x-i->chardata[t].x;
1032 if(rel>=0 && (rel<(1<<(advancebits-1)) || pass==0)) {
1034 lastx=i->chardata[nextt].x;
1038 lastx=i->chardata[t].x;
1040 charids[charstorepos] = i->chardata[t].charid;
1041 charadvance[charstorepos] = advance;
1048 static void putcharacter(struct swfoutput*obj, int fontid, int charid,
1049 int x,int y, int size)
1051 swfoutput_internal*i = (swfoutput_internal*)obj->internal;
1052 if(i->chardatapos == CHARDATAMAX)
1054 msg("<warning> Character buffer too small. SWF will be slightly bigger");
1058 i->chardata[i->chardatapos].fontid = fontid;
1059 i->chardata[i->chardatapos].charid = charid;
1060 i->chardata[i->chardatapos].x = x;
1061 i->chardata[i->chardatapos].y = y;
1062 i->chardata[i->chardatapos].color = obj->fillrgb;
1063 i->chardata[i->chardatapos].size = size;
1067 /* Notice: we can only put chars in the range -1639,1638 (-32768/20,32768/20).
1068 So if we set this value to high, the char coordinates will overflow.
1069 If we set it to low, however, the char positions will be inaccurate */
1070 #define FONT_INTERNAL_SIZE 4
1072 /* process a character. */
1073 static int drawchar(struct swfoutput*obj, SWFFONT *swffont, char*character, int charnr, int u, swfmatrix*m)
1075 swfoutput_internal*i = (swfoutput_internal*)obj->internal;
1077 msg("<warning> Font is NULL");
1081 int charid = getCharID(swffont, charnr, character, u);
1084 msg("<warning> Didn't find character '%s' (c=%d,u=%d) in current charset (%s, %d characters)",
1085 FIXNULL(character),charnr, u, FIXNULL((char*)swffont->name), swffont->numchars);
1096 float det = ((m->m11*m->m22)-(m->m21*m->m12));
1097 if(fabs(det) < 0.0005) {
1098 /* x direction equals y direction- the text is invisible */
1101 det = 20*FONT_INTERNAL_SIZE / det;
1104 p.x = (SCOORD)(( x * m->m22 - y * m->m12)*det);
1105 p.y = (SCOORD)((- x * m->m21 + y * m->m11)*det);
1107 putcharacter(obj, swffont->id, charid,p.x,p.y,FONT_INTERNAL_SIZE);
1108 swf_FontUseGlyph(swffont, charid);
1113 SWF_OUTLINE*outline = font->getOutline(character, charnr);
1114 char* charname = character;
1117 msg("<warning> Didn't find character '%s' (%d) in current charset (%s)",
1118 FIXNULL(character),charnr,FIXNULL(font->getName()));
1137 drawpath(tag, outline, &m2, 0);
1142 static void endtext(swfoutput*obj)
1144 swfoutput_internal*i = (swfoutput_internal*)obj->internal;
1148 i->tag = swf_InsertTag(i->tag,ST_DEFINETEXT);
1149 swf_SetU16(i->tag, i->textid);
1152 r = getcharacterbbox(obj, obj->swffont);
1154 swf_SetRect(i->tag,&r);
1157 swf_GetMatrix(0, &m);
1158 swf_SetMatrix(i->tag,&m);
1160 putcharacters(obj, i->tag);
1161 swf_SetU8(i->tag,0);
1162 i->tag = swf_InsertTag(i->tag,ST_PLACEOBJECT2);
1163 //swf_ObjectPlace(i->tag,i->textid,/*depth*/i->depth++,&i->page_matrix,NULL,NULL);
1165 swf_MatrixJoin(&m2,&obj->fontmatrix, &i->page_matrix);
1167 swf_ObjectPlace(i->tag,i->textid,/*depth*/i->depth++,&m2,NULL,NULL);
1172 /* draw a curved polygon. */
1173 void swfoutput_drawpath(swfoutput*obj, SWF_OUTLINE*outline,
1176 swfoutput_internal*i = (swfoutput_internal*)obj->internal;
1180 /* Multiple polygons in one shape don't overlap correctly,
1181 so we better start a new shape here if the polygon is filled
1183 if(i->shapeid>=0 && i->fill && !config_ignoredraworder) {
1196 drawpath(obj, outline,m, 0);
1199 void swfoutput_drawpath2poly(struct swfoutput*obj, SWF_OUTLINE*outline, struct swfmatrix*m, int line_join, int line_cap, double line_width, double miter_limit)
1201 swfoutput_internal*i = (swfoutput_internal*)obj->internal;
1206 assert(i->shapeid<0);
1210 drawpath2poly(obj, outline, m, 0, line_join, line_cap, line_width, miter_limit);
1213 int getCharID(SWFFONT *font, int charnr, char *charname, int u)
1216 if(charname && font->glyphnames) {
1217 for(t=0;t<font->numchars;t++) {
1218 if(font->glyphnames[t] && !strcmp(font->glyphnames[t],charname)) {
1219 msg("<debug> Char [%d,>%s<,%d] maps to %d\n", charnr, charname, u, t);
1223 /* if we didn't find the character, maybe
1224 we can find the capitalized version */
1225 for(t=0;t<font->numchars;t++) {
1226 if(font->glyphnames[t] && !strcasecmp(font->glyphnames[t],charname)) {
1227 msg("<debug> Char [%d,>>%s<<,%d] maps to %d\n", charnr, charname, u, t);
1233 if(u>0 && font->encoding != 255) {
1234 /* try to use the unicode id */
1235 if(u>=0 && u<font->maxascii && font->ascii2glyph[u]>=0) {
1236 msg("<debug> Char [%d,%s,>%d<] maps to %d\n", charnr, charname, u, font->ascii2glyph[u]);
1237 return font->ascii2glyph[u];
1241 if(font->encoding != FONT_ENCODING_UNICODE) {
1242 /* the following only works if the font encoding
1243 is US-ASCII based. It's needed for fonts which return broken unicode
1245 if(charnr>=0 && charnr<font->maxascii && font->ascii2glyph[charnr]>=0) {
1246 msg("<debug> Char [>%d<,%s,%d] maps to %d\n", charnr, charname, u, font->ascii2glyph[charnr]);
1247 return font->ascii2glyph[charnr];
1251 if(charnr>=0 && charnr<font->numchars) {
1252 msg("<debug> Char [>%d<,%s,%d] maps to %d\n", charnr, charname, u, charnr);
1260 /* set's the t1 font index of the font to use for swfoutput_drawchar(). */
1261 void swfoutput_setfont(struct swfoutput*obj, char*fontid, char*filename)
1263 swfoutput_internal*i = (swfoutput_internal*)obj->internal;
1264 fontlist_t*last=0,*iterator;
1266 msg("<error> No fontid");
1270 if(obj->swffont && obj->swffont->name && !strcmp((char*)obj->swffont->name,fontid))
1273 /* TODO: remove the need for this (enhance getcharacterbbox so that it can cope
1274 with multiple fonts */
1277 iterator = i->fontlist;
1279 if(!strcmp((char*)iterator->swffont->name,fontid)) {
1280 obj->swffont = iterator->swffont;
1284 iterator = iterator->next;
1288 msg("<error> No filename given for font- internal error?");
1292 swf_SetLoadFontParameters(64,/*skip unused*/0,/*full unicode*/1);
1293 SWFFONT*swffont = swf_LoadFont(filename);
1296 msg("<warning> Couldn't load font %s (%s)", fontid, filename);
1297 swffont = swf_LoadFont(0);
1300 if(swffont->glyph2ascii) {
1303 /* check whether the Unicode indices look o.k.
1304 If they don't, disable the unicode lookup by setting
1305 the encoding to 255 */
1306 for(t=0;t<swffont->numchars;t++) {
1307 int c = swffont->glyph2ascii[t];
1308 if(c && c < 32 && swffont->glyph[t].shape->bitlen > 16) {
1309 // the character maps into the unicode control character range
1310 // between 0001-001f. Yet it is not empty. Treat the one
1311 // mapping as broken, and look how many of those we find.
1316 msg("<warning> Font %s has bad unicode mapping", swffont->name);
1317 swffont->encoding = 255;
1321 swf_FontSetID(swffont, ++i->currentswfid);
1323 if(getScreenLogLevel() >= LOGLEVEL_DEBUG) {
1324 // print font information
1325 msg("<debug> Font %s (%s)",swffont->name, filename);
1326 msg("<debug> | ID: %d", swffont->id);
1327 msg("<debug> | Version: %d", swffont->version);
1328 msg("<debug> | Name: %s", fontid);
1329 msg("<debug> | Numchars: %d", swffont->numchars);
1330 msg("<debug> | Maxascii: %d", swffont->maxascii);
1331 msg("<debug> | Style: %d", swffont->style);
1332 msg("<debug> | Encoding: %d", swffont->encoding);
1333 for(int iii=0; iii<swffont->numchars;iii++) {
1334 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,
1335 swffont->layout->bounds[iii].xmin/20.0,
1336 swffont->layout->bounds[iii].ymin/20.0,
1337 swffont->layout->bounds[iii].xmax/20.0,
1338 swffont->layout->bounds[iii].ymax/20.0
1341 for(t=0;t<swffont->maxascii;t++) {
1342 if(swffont->ascii2glyph[t] == iii)
1343 msg("<debug> | - maps to %d",t);
1348 /* set the font name to the ID we use here */
1349 if(swffont->name) free(swffont->name);
1350 swffont->name = (U8*)strdup(fontid);
1352 iterator = new fontlist_t;
1353 iterator->swffont = swffont;
1357 last->next = iterator;
1359 i->fontlist = iterator;
1361 obj->swffont = swffont;
1364 int swfoutput_queryfont(struct swfoutput*obj, char*fontid)
1366 swfoutput_internal*i = (swfoutput_internal*)obj->internal;
1367 fontlist_t *iterator = i->fontlist;
1369 if(!strcmp((char*)iterator->swffont->name,fontid))
1371 iterator = iterator->next;
1376 /* set's the matrix which is to be applied to characters drawn by
1377 swfoutput_drawchar() */
1378 void swfoutput_setfontmatrix(struct swfoutput*obj,double m11,double m12,
1379 double m21,double m22)
1381 swfoutput_internal*i = (swfoutput_internal*)obj->internal;
1382 if(obj->fontm11 == m11 &&
1383 obj->fontm12 == m12 &&
1384 obj->fontm21 == m21 &&
1385 obj->fontm22 == m22)
1395 m.sx = (U32)(((obj->fontm11)*65536)/FONT_INTERNAL_SIZE); m.r1 = (U32)(((obj->fontm12)*65536)/FONT_INTERNAL_SIZE);
1396 m.r0 = (U32)(((obj->fontm21)*65536)/FONT_INTERNAL_SIZE); m.sy = (U32)(((obj->fontm22)*65536)/FONT_INTERNAL_SIZE);
1399 obj->fontmatrix = m;
1402 /* draws a character at x,y. */
1403 int swfoutput_drawchar(struct swfoutput* obj,double x,double y,char*character, int charnr, int u)
1405 swfoutput_internal*i = (swfoutput_internal*)obj->internal;
1407 m.m11 = obj->fontm11;
1408 m.m12 = obj->fontm12;
1409 m.m21 = obj->fontm21;
1410 m.m22 = obj->fontm22;
1413 return drawchar(obj, obj->swffont, character, charnr, u, &m);
1416 static void endpage(struct swfoutput*obj)
1418 swfoutput_internal*i = (swfoutput_internal*)obj->internal;
1424 swfoutput_endclip(obj);
1425 i->pagefinished = 1;
1428 void swfoutput_pagefeed(struct swfoutput*obj)
1430 swfoutput_internal*i = (swfoutput_internal*)obj->internal;
1432 if(!i->pagefinished)
1435 if(config_insertstoptag) {
1437 atag = action_Stop(atag);
1438 atag = action_End(atag);
1439 i->tag = swf_InsertTag(i->tag,ST_DOACTION);
1440 swf_ActionSet(i->tag,atag);
1442 i->tag = swf_InsertTag(i->tag,ST_SHOWFRAME);
1445 void swfoutput_newpage(struct swfoutput*obj, int pageNum, int movex, int movey, int x1, int y1, int x2, int y2)
1447 swfoutput_internal*i = (swfoutput_internal*)obj->internal;
1448 if(!i->firstpage && !i->pagefinished)
1451 swf_GetMatrix(0, &i->page_matrix);
1452 i->page_matrix.tx = movex*20;
1453 i->page_matrix.ty = movey*20;
1455 for(i->depth--;i->depth>=i->startdepth;i->depth--) {
1456 i->tag = swf_InsertTag(i->tag,ST_REMOVEOBJECT2);
1457 swf_SetU16(i->tag,i->depth);
1459 /* TODO: this should all be done in SWFOutputDev */
1461 i->depth = i->startdepth = 3; /* leave room for clip and background rectangle */
1465 x1*=20;y1*=20;x2*=20;y2*=20;
1467 if(i->lastpagesize.xmin != x1 ||
1468 i->lastpagesize.xmax != x2 ||
1469 i->lastpagesize.ymin != y1 ||
1470 i->lastpagesize.ymax != y2)
1471 {/* add white clipping rectangle */
1472 msg("<notice> processing page %d (%dx%d)", pageNum,i->sizex,i->sizey);
1475 msg("<notice> Page has a different size than previous ones");
1476 i->tag = swf_InsertTag(i->tag,ST_REMOVEOBJECT2);
1477 swf_SetU16(i->tag,1);
1478 i->tag = swf_InsertTag(i->tag,ST_REMOVEOBJECT2);
1479 swf_SetU16(i->tag,2);
1483 rgb.a = rgb.r = rgb.g = rgb.b = 0xff;
1487 int shapeid = ++i->currentswfid;
1492 i->tag = swf_InsertTag(i->tag, ST_DEFINESHAPE);
1494 fs1 = swf_ShapeAddSolidFillStyle(s, &rgb);
1495 swf_SetU16(i->tag,shapeid);
1496 swf_SetRect(i->tag,&r);
1497 swf_SetShapeHeader(i->tag,s);
1498 swf_ShapeSetAll(i->tag,s,x1,y1,ls1,fs1,0);
1499 swf_ShapeSetLine(i->tag,s,(x2-x1),0);
1500 swf_ShapeSetLine(i->tag,s,0,(y2-y1));
1501 swf_ShapeSetLine(i->tag,s,(x1-x2),0);
1502 swf_ShapeSetLine(i->tag,s,0,(y1-y2));
1503 swf_ShapeSetEnd(i->tag);
1505 i->tag = swf_InsertTag(i->tag, ST_PLACEOBJECT2);
1506 swf_ObjectPlace(i->tag,shapeid,/*depth*/1,0,0,0);
1507 i->tag = swf_InsertTag(i->tag, ST_PLACEOBJECT2);
1508 swf_ObjectPlaceClip(i->tag,shapeid,/*depth*/2,0,0,0,65535);
1510 msg("<notice> processing page %d", pageNum);
1513 i->lastpagesize.xmin = x1;
1514 i->lastpagesize.xmax = x2;
1515 i->lastpagesize.ymin = y1;
1516 i->lastpagesize.ymax = y2;
1517 swf_ExpandRect2(&i->swf.movieSize, &i->lastpagesize);
1520 i->pagefinished = 0;
1523 /* initialize the swf writer */
1524 void swfoutput_init(struct swfoutput* obj)
1526 memset(obj, 0, sizeof(struct swfoutput));
1527 obj->internal = init_internal_struct();
1529 swfoutput_internal*i = (swfoutput_internal*)obj->internal;
1534 msg("<verbose> initializing swf output for size %d*%d\n", i->sizex,i->sizey);
1539 memset(&i->swf,0x00,sizeof(SWF));
1540 memset(&i->lastpagesize,0x00,sizeof(SRECT));
1542 i->swf.fileVersion = config_flashversion;
1543 i->swf.frameRate = 0x0040; // 1 frame per 4 seconds
1544 i->swf.movieSize.xmin = 0;
1545 i->swf.movieSize.ymin = 0;
1546 i->swf.movieSize.xmax = 0;
1547 i->swf.movieSize.ymax = 0;
1549 i->swf.firstTag = swf_InsertTag(NULL,ST_SETBACKGROUNDCOLOR);
1550 i->tag = i->swf.firstTag;
1551 rgb.a = rgb.r = rgb.g = rgb.b = 0xff;
1552 swf_SetRGB(i->tag,&rgb);
1554 i->startdepth = i->depth = 0;
1557 i->tag = swf_InsertTag(i->tag, ST_PROTECT);
1560 static void startshape(struct swfoutput*obj)
1562 swfoutput_internal*i = (swfoutput_internal*)obj->internal;
1569 i->tag = swf_InsertTag(i->tag,ST_DEFINESHAPE);
1571 swf_ShapeNew(&i->shape);
1572 i->linestyleid = swf_ShapeAddLineStyle(i->shape,i->linewidth,&obj->strokergb);
1573 rgb.r = obj->fillrgb.r;
1574 rgb.g = obj->fillrgb.g;
1575 rgb.b = obj->fillrgb.b;
1576 i->fillstyleid = swf_ShapeAddSolidFillStyle(i->shape,&obj->fillrgb);
1578 i->shapeid = ++i->currentswfid;
1579 swf_SetU16(i->tag,i->shapeid); // ID
1581 i->bboxrectpos = i->tag->len;
1584 r.xmax = 20*i->sizex;
1585 r.ymax = 20*i->sizey;
1586 swf_SetRect(i->tag,&r);
1588 memset(&i->bboxrect, 0, sizeof(i->bboxrect));
1590 swf_SetShapeStyles(i->tag,i->shape);
1591 swf_ShapeCountBits(i->shape,NULL,NULL);
1592 swf_SetShapeBits(i->tag,i->shape);
1594 /* TODO: do we really need this? */
1595 swf_ShapeSetAll(i->tag,i->shape,/*x*/0,/*y*/0,i->linestyleid,0,0);
1596 i->swflastx=i->swflasty=0;
1598 i->shapeisempty = 1;
1601 static void starttext(struct swfoutput*obj)
1603 swfoutput_internal*i = (swfoutput_internal*)obj->internal;
1607 i->textid = ++i->currentswfid;
1609 i->swflastx=i->swflasty=0;
1613 /* TODO: move to ../lib/rfxswf */
1614 void changeRect(struct swfoutput*obj, TAG*tag, int pos, SRECT*newrect)
1616 swfoutput_internal*i = (swfoutput_internal*)obj->internal;
1617 /* determine length of old rect */
1621 swf_GetRect(tag, &old);
1622 swf_ResetReadBits(tag);
1623 int pos_end = tag->pos;
1625 int len = tag->len - pos_end;
1626 U8*data = (U8*)malloc(len);
1627 memcpy(data, &tag->data[pos_end], len);
1630 swf_SetRect(tag, newrect);
1631 swf_SetBlock(tag, data, len);
1633 tag->pos = tag->readBit = 0;
1636 void cancelshape(swfoutput*obj)
1638 swfoutput_internal*i = (swfoutput_internal*)obj->internal;
1639 /* delete old shape tag */
1641 i->tag = i->tag->prev;
1642 swf_DeleteTag(todel);
1643 if(i->shape) {swf_ShapeFree(i->shape);i->shape=0;}
1645 i->bboxrectpos = -1;
1648 void fixAreas(swfoutput*obj)
1650 swfoutput_internal*i = (swfoutput_internal*)obj->internal;
1651 if(!i->shapeisempty && i->fill &&
1652 (i->bboxrect.xmin == i->bboxrect.xmax ||
1653 i->bboxrect.ymin == i->bboxrect.ymax) &&
1654 config_minlinewidth >= 0.001
1656 msg("<debug> Shape has size 0: width=%.2f height=%.2f",
1657 (i->bboxrect.xmax-i->bboxrect.xmin)/20.0,
1658 (i->bboxrect.ymax-i->bboxrect.ymin)/20.0
1661 SRECT r = i->bboxrect;
1663 if(r.xmin == r.xmax && r.ymin == r.ymax) {
1664 /* this thing comes down to a single dot- nothing to fix here */
1670 RGBA save_col = obj->strokergb;
1671 int save_width = i->linewidth;
1673 obj->strokergb = obj->fillrgb;
1674 i->linewidth = (int)(config_minlinewidth*20);
1675 if(i->linewidth==0) i->linewidth = 1;
1679 moveto(obj, i->tag, r.xmin/20.0,r.ymin/20.0);
1680 lineto(obj, i->tag, r.xmax/20.0,r.ymax/20.0);
1682 obj->strokergb = save_col;
1683 i->linewidth = save_width;
1688 static void endshape_noput(swfoutput*obj)
1690 swfoutput_internal*i = (swfoutput_internal*)obj->internal;
1693 //changeRect(obj, i->tag, i->bboxrectpos, &i->bboxrect);
1696 swf_ShapeFree(i->shape);
1701 static void endshape(swfoutput*obj, int clipdepth)
1703 swfoutput_internal*i = (swfoutput_internal*)obj->internal;
1710 if(i->shapeisempty ||
1711 (i->bboxrect.xmin == i->bboxrect.xmax &&
1712 i->bboxrect.ymin == i->bboxrect.ymax))
1714 // delete the shape again, we didn't do anything
1719 swf_ShapeSetEnd(i->tag);
1721 changeRect(obj, i->tag, i->bboxrectpos, &i->bboxrect);
1723 i->tag = swf_InsertTag(i->tag,ST_PLACEOBJECT2);
1726 swf_ObjectPlaceClip(i->tag,i->shapeid,i->depth++,&i->page_matrix,NULL,NULL,clipdepth);
1728 swf_ObjectPlace(i->tag,i->shapeid,/*depth*/i->depth++,&i->page_matrix,NULL,NULL);
1730 swf_ShapeFree(i->shape);
1733 i->bboxrectpos = -1;
1736 void swfoutput_finalize(struct swfoutput*obj)
1738 swfoutput_internal*i = (swfoutput_internal*)obj->internal;
1740 if(i->tag && i->tag->id == ST_END)
1741 return; //already done
1744 fontlist_t *tmp,*iterator = i->fontlist;
1746 TAG*mtag = i->swf.firstTag;
1747 if(iterator->swffont) {
1748 mtag = swf_InsertTag(mtag, ST_DEFINEFONT2);
1749 /*if(!storeallcharacters)
1750 swf_FontReduce(iterator->swffont);*/
1751 swf_FontSetDefine2(mtag, iterator->swffont);
1754 iterator = iterator->next;
1756 i->tag = swf_InsertTag(i->tag,ST_END);
1759 SWF* swfoutput_get(struct swfoutput*obj)
1761 swfoutput_internal*i = (swfoutput_internal*)obj->internal;
1763 swfoutput_finalize(obj);
1765 return swf_CopySWF(&i->swf);
1768 void swfoutput_getdimensions(struct swfoutput*obj, int*x1, int*y1, int*x2, int*y2)
1770 swfoutput_internal*i = (swfoutput_internal*)obj->internal;
1771 if(x1) *x1 = i->swf.movieSize.xmin/20;
1772 if(y1) *y1 = i->swf.movieSize.ymin/20;
1773 if(x2) *x2 = i->swf.movieSize.xmax/20;
1774 if(y2) *y2 = i->swf.movieSize.ymax/20;
1777 int swfoutput_save(struct swfoutput* obj, char*filename)
1779 swfoutput_internal*i = (swfoutput_internal*)obj->internal;
1780 swfoutput_finalize(obj);
1784 fi = open(filename, O_BINARY|O_CREAT|O_TRUNC|O_WRONLY, 0777);
1789 msg("<fatal> Could not create \"%s\". ", FIXNULL(filename));
1793 if(config_enablezlib || config_flashversion>=6) {
1794 if FAILED(swf_WriteSWC(fi,&i->swf))
1795 msg("<error> WriteSWC() failed.\n");
1797 if FAILED(swf_WriteSWF(fi,&i->swf))
1798 msg("<error> WriteSWF() failed.\n");
1803 msg("<notice> SWF written\n");
1807 /* Perform cleaning up, complete the swf, and write it out. */
1808 void swfoutput_destroy(struct swfoutput* obj)
1810 swfoutput_internal*i = (swfoutput_internal*)obj->internal;
1812 /* not initialized yet- nothing to destroy */
1816 fontlist_t *tmp,*iterator = i->fontlist;
1818 if(iterator->swffont) {
1819 swf_FontFree(iterator->swffont);iterator->swffont=0;
1822 iterator = iterator->next;
1825 swf_FreeTags(&i->swf);
1828 memset(obj, 0, sizeof(swfoutput));
1831 void swfoutput_setdrawmode(swfoutput* obj, int mode)
1833 swfoutput_internal*i = (swfoutput_internal*)obj->internal;
1834 obj->drawmode = mode;
1835 if(mode == DRAWMODE_FILL)
1837 else if(mode == DRAWMODE_EOFILL)
1839 else if(mode == DRAWMODE_STROKE)
1841 else if(mode == DRAWMODE_CLIP)
1843 else if(mode == DRAWMODE_EOCLIP)
1847 void swfoutput_setfillcolor(swfoutput* obj, U8 r, U8 g, U8 b, U8 a)
1849 swfoutput_internal*i = (swfoutput_internal*)obj->internal;
1850 if(obj->fillrgb.r == r &&
1851 obj->fillrgb.g == g &&
1852 obj->fillrgb.b == b &&
1853 obj->fillrgb.a == a) return;
1863 void swfoutput_setstrokecolor(swfoutput* obj, U8 r, U8 g, U8 b, U8 a)
1865 swfoutput_internal*i = (swfoutput_internal*)obj->internal;
1866 if(obj->strokergb.r == r &&
1867 obj->strokergb.g == g &&
1868 obj->strokergb.b == b &&
1869 obj->strokergb.a == a) return;
1873 obj->strokergb.r = r;
1874 obj->strokergb.g = g;
1875 obj->strokergb.b = b;
1876 obj->strokergb.a = a;
1879 void swfoutput_setlinewidth(struct swfoutput*obj, double _linewidth)
1881 swfoutput_internal*i = (swfoutput_internal*)obj->internal;
1882 if(i->linewidth == (U16)(_linewidth*20))
1887 i->linewidth = (U16)(_linewidth*20);
1891 void swfoutput_startclip(swfoutput*obj, SWF_OUTLINE*outline, struct swfmatrix*m)
1893 swfoutput_internal*i = (swfoutput_internal*)obj->internal;
1899 if(i->clippos >= 127)
1901 msg("<warning> Too many clip levels.");
1906 int olddrawmode = obj->drawmode;
1907 swfoutput_setdrawmode(obj, DRAWMODE_CLIP);
1908 swfoutput_drawpath(obj, outline, m);
1909 swf_ShapeSetEnd(i->tag);
1910 swfoutput_setdrawmode(obj, olddrawmode);
1912 i->tag = swf_InsertTag(i->tag,ST_PLACEOBJECT2);
1913 i->cliptags[i->clippos] = i->tag;
1914 i->clipshapes[i->clippos] = i->shapeid;
1915 i->clipdepths[i->clippos] = i->depth++;
1918 endshape_noput(obj);
1921 void swfoutput_endclip(swfoutput*obj)
1923 swfoutput_internal*i = (swfoutput_internal*)obj->internal;
1930 msg("<error> Invalid end of clipping region");
1934 swf_ObjectPlaceClip(i->cliptags[i->clippos],i->clipshapes[i->clippos],i->clipdepths[i->clippos],&i->page_matrix,NULL,NULL,i->depth++);
1937 static void drawlink(struct swfoutput*obj, ActionTAG*,ActionTAG*, swfcoord*points, char mouseover);
1939 void swfoutput_linktourl(struct swfoutput*obj, char*url, swfcoord*points)
1941 swfoutput_internal*i = (swfoutput_internal*)obj->internal;
1943 if(!strncmp("http://pdf2swf:", url, 15)) {
1944 char*tmp = strdup(url);
1945 int l = strlen(tmp);
1948 swfoutput_namedlink(obj, tmp+15, points);
1958 if(config_opennewwindow)
1959 actions = action_GetUrl(0, url, "_parent");
1961 actions = action_GetUrl(0, url, "_this");
1962 actions = action_End(actions);
1964 drawlink(obj, actions, 0, points,0);
1966 void swfoutput_linktopage(struct swfoutput*obj, int page, swfcoord*points)
1968 swfoutput_internal*i = (swfoutput_internal*)obj->internal;
1976 actions = action_GotoFrame(0, page);
1977 actions = action_End(actions);
1979 drawlink(obj, actions, 0, points,0);
1982 /* Named Links (a.k.a. Acrobatmenu) are used to implement various gadgets
1983 of the viewer objects, like subtitles, index elements etc.
1985 void swfoutput_namedlink(struct swfoutput*obj, char*name, swfcoord*points)
1987 swfoutput_internal*i = (swfoutput_internal*)obj->internal;
1988 ActionTAG *actions1,*actions2;
1989 char*tmp = strdup(name);
1997 if(!strncmp(tmp, "call:", 5))
1999 char*x = strchr(&tmp[5], ':');
2001 actions1 = action_PushInt(0, 0); //number of parameters (0)
2002 actions1 = action_PushString(actions1, &tmp[5]); //function name
2003 actions1 = action_CallFunction(actions1);
2006 actions1 = action_PushString(0, x+1); //parameter
2007 actions1 = action_PushInt(actions1, 1); //number of parameters (1)
2008 actions1 = action_PushString(actions1, &tmp[5]); //function name
2009 actions1 = action_CallFunction(actions1);
2011 actions2 = action_End(0);
2016 actions1 = action_PushString(0, "/:subtitle");
2017 actions1 = action_PushString(actions1, name);
2018 actions1 = action_SetVariable(actions1);
2019 actions1 = action_End(actions1);
2021 actions2 = action_PushString(0, "/:subtitle");
2022 actions2 = action_PushString(actions2, "");
2023 actions2 = action_SetVariable(actions2);
2024 actions2 = action_End(actions2);
2027 drawlink(obj, actions1, actions2, points,mouseover);
2029 swf_ActionFree(actions1);
2030 swf_ActionFree(actions2);
2034 static void drawlink(struct swfoutput*obj, ActionTAG*actions1, ActionTAG*actions2, swfcoord*points, char mouseover)
2036 swfoutput_internal*i = (swfoutput_internal*)obj->internal;
2041 struct plotxy p1,p2,p3,p4;
2045 double xmax=xmin=points[0].x,ymax=ymin=points[0].y;
2049 int buttonid = ++i->currentswfid;
2052 if(points[t].x>xmax) xmax=points[t].x;
2053 if(points[t].y>ymax) ymax=points[t].y;
2054 if(points[t].x<xmin) xmin=points[t].x;
2055 if(points[t].y<ymin) ymin=points[t].y;
2058 p1.x=points[0].x; p1.y=points[0].y; p2.x=points[1].x; p2.y=points[1].y;
2059 p3.x=points[2].x; p3.y=points[2].y; p4.x=points[3].x; p4.y=points[3].y;
2061 /* the following code subtracts the upper left edge from all coordinates,
2062 and set's posx,posy so that ST_PLACEOBJECT is used with a matrix.
2063 Necessary for preprocessing with swfcombine. */
2064 posx = xmin; posy = ymin;
2065 p1.x-=posx;p2.x-=posx;p3.x-=posx;p4.x-=posx;
2066 p1.y-=posy;p2.y-=posy;p3.y-=posy;p4.y-=posy;
2067 xmin -= posx; ymin -= posy;
2068 xmax -= posx; ymax -= posy;
2071 myshapeid = ++i->currentswfid;
2072 i->tag = swf_InsertTag(i->tag,ST_DEFINESHAPE3);
2073 swf_ShapeNew(&i->shape);
2074 rgb.r = rgb.b = rgb.a = rgb.g = 0;
2075 fsid = swf_ShapeAddSolidFillStyle(i->shape,&rgb);
2076 swf_SetU16(i->tag, myshapeid);
2077 r.xmin = (int)(xmin*20);
2078 r.ymin = (int)(ymin*20);
2079 r.xmax = (int)(xmax*20);
2080 r.ymax = (int)(ymax*20);
2081 swf_SetRect(i->tag,&r);
2082 swf_SetShapeStyles(i->tag,i->shape);
2083 swf_ShapeCountBits(i->shape,NULL,NULL);
2084 swf_SetShapeBits(i->tag,i->shape);
2085 swf_ShapeSetAll(i->tag,i->shape,/*x*/0,/*y*/0,0,fsid,0);
2086 i->swflastx = i->swflasty = 0;
2087 moveto(obj, i->tag, p1);
2088 lineto(obj, i->tag, p2);
2089 lineto(obj, i->tag, p3);
2090 lineto(obj, i->tag, p4);
2091 lineto(obj, i->tag, p1);
2092 swf_ShapeSetEnd(i->tag);
2095 myshapeid2 = ++i->currentswfid;
2096 i->tag = swf_InsertTag(i->tag,ST_DEFINESHAPE3);
2097 swf_ShapeNew(&i->shape);
2098 rgb.r = rgb.b = rgb.a = rgb.g = 255;
2100 fsid = swf_ShapeAddSolidFillStyle(i->shape,&rgb);
2101 swf_SetU16(i->tag, myshapeid2);
2102 r.xmin = (int)(xmin*20);
2103 r.ymin = (int)(ymin*20);
2104 r.xmax = (int)(xmax*20);
2105 r.ymax = (int)(ymax*20);
2106 swf_SetRect(i->tag,&r);
2107 swf_SetShapeStyles(i->tag,i->shape);
2108 swf_ShapeCountBits(i->shape,NULL,NULL);
2109 swf_SetShapeBits(i->tag,i->shape);
2110 swf_ShapeSetAll(i->tag,i->shape,/*x*/0,/*y*/0,0,fsid,0);
2111 i->swflastx = i->swflasty = 0;
2112 moveto(obj, i->tag, p1);
2113 lineto(obj, i->tag, p2);
2114 lineto(obj, i->tag, p3);
2115 lineto(obj, i->tag, p4);
2116 lineto(obj, i->tag, p1);
2117 swf_ShapeSetEnd(i->tag);
2121 i->tag = swf_InsertTag(i->tag,ST_DEFINEBUTTON);
2122 swf_SetU16(i->tag,buttonid); //id
2123 swf_ButtonSetFlags(i->tag, 0); //menu=no
2124 swf_ButtonSetRecord(i->tag,0x01,myshapeid,i->depth,0,0);
2125 swf_ButtonSetRecord(i->tag,0x02,myshapeid2,i->depth,0,0);
2126 swf_ButtonSetRecord(i->tag,0x04,myshapeid2,i->depth,0,0);
2127 swf_ButtonSetRecord(i->tag,0x08,myshapeid,i->depth,0,0);
2128 swf_SetU8(i->tag,0);
2129 swf_ActionSet(i->tag,actions1);
2130 swf_SetU8(i->tag,0);
2134 i->tag = swf_InsertTag(i->tag,ST_DEFINEBUTTON2);
2135 swf_SetU16(i->tag,buttonid); //id
2136 swf_ButtonSetFlags(i->tag, 0); //menu=no
2137 swf_ButtonSetRecord(i->tag,0x01,myshapeid,i->depth,0,0);
2138 swf_ButtonSetRecord(i->tag,0x02,myshapeid2,i->depth,0,0);
2139 swf_ButtonSetRecord(i->tag,0x04,myshapeid2,i->depth,0,0);
2140 swf_ButtonSetRecord(i->tag,0x08,myshapeid,i->depth,0,0);
2141 swf_SetU8(i->tag,0); // end of button records
2142 swf_ButtonSetCondition(i->tag, BC_IDLE_OVERUP);
2143 swf_ActionSet(i->tag,actions1);
2145 swf_ButtonSetCondition(i->tag, BC_OVERUP_IDLE);
2146 swf_ActionSet(i->tag,actions2);
2147 swf_SetU8(i->tag,0);
2148 swf_ButtonPostProcess(i->tag, 2);
2150 swf_SetU8(i->tag,0);
2151 swf_ButtonPostProcess(i->tag, 1);
2155 i->tag = swf_InsertTag(i->tag,ST_PLACEOBJECT2);
2157 if(posx!=0 || posy!=0) {
2159 p.x = (int)(posx*20);
2160 p.y = (int)(posy*20);
2161 p = swf_TurnPoint(p, &i->page_matrix);
2166 swf_ObjectPlace(i->tag, buttonid, i->depth++,&m,0,0);
2169 swf_ObjectPlace(i->tag, buttonid, i->depth++,&i->page_matrix,0,0);
2173 static void drawimage(struct swfoutput*obj, int bitid, int sizex,int sizey,
2174 double x1,double y1,
2175 double x2,double y2,
2176 double x3,double y3,
2177 double x4,double y4)
2179 swfoutput_internal*i = (swfoutput_internal*)obj->internal;
2184 struct plotxy p1,p2,p3,p4;
2186 double xmax=x1,ymax=y1,xmin=x1,ymin=y1;
2187 if(x2>xmax) xmax=x2;
2188 if(y2>ymax) ymax=y2;
2189 if(x2<xmin) xmin=x2;
2190 if(y2<ymin) ymin=y2;
2191 if(x3>xmax) xmax=x3;
2192 if(y3>ymax) ymax=y3;
2193 if(x3<xmin) xmin=x3;
2194 if(y3<ymin) ymin=y3;
2195 if(x4>xmax) xmax=x4;
2196 if(y4>ymax) ymax=y4;
2197 if(x4<xmin) xmin=x4;
2198 if(y4<ymin) ymin=y4;
2204 {p1.x = (int)(p1.x*20)/20.0;
2205 p1.y = (int)(p1.y*20)/20.0;
2206 p2.x = (int)(p2.x*20)/20.0;
2207 p2.y = (int)(p2.y*20)/20.0;
2208 p3.x = (int)(p3.x*20)/20.0;
2209 p3.y = (int)(p3.y*20)/20.0;
2210 p4.x = (int)(p4.x*20)/20.0;
2211 p4.y = (int)(p4.y*20)/20.0;}
2214 m.sx = (int)(65536*20*(p4.x-p1.x)/sizex);
2215 m.r1 = -(int)(65536*20*(p4.y-p1.y)/sizex);
2216 m.r0 = (int)(65536*20*(p1.x-p2.x)/sizey);
2217 m.sy = -(int)(65536*20*(p1.y-p2.y)/sizey);
2219 m.tx = (int)(p1.x*20);
2220 m.ty = (int)(p1.y*20);
2223 myshapeid = ++i->currentswfid;
2224 i->tag = swf_InsertTag(i->tag,ST_DEFINESHAPE);
2226 swf_ShapeNew(&shape);
2227 //lsid = ShapeAddLineStyle(shape,linewidth,&obj->strokergb);
2228 //fsid = ShapeAddSolidFillStyle(shape,&obj->fillrgb);
2229 fsid = swf_ShapeAddBitmapFillStyle(shape,&m,bitid,1);
2230 swf_SetU16(i->tag, myshapeid);
2231 r.xmin = (int)(xmin*20);
2232 r.ymin = (int)(ymin*20);
2233 r.xmax = (int)(xmax*20);
2234 r.ymax = (int)(ymax*20);
2235 swf_SetRect(i->tag,&r);
2236 swf_SetShapeStyles(i->tag,shape);
2237 swf_ShapeCountBits(shape,NULL,NULL);
2238 swf_SetShapeBits(i->tag,shape);
2239 swf_ShapeSetAll(i->tag,shape,/*x*/0,/*y*/0,lsid,fsid,0);
2240 i->swflastx = i->swflasty = 0;
2241 moveto(obj, i->tag, p1);
2242 lineto(obj, i->tag, p2);
2243 lineto(obj, i->tag, p3);
2244 lineto(obj, i->tag, p4);
2245 lineto(obj, i->tag, p1);
2247 ShapeMoveTo (tag, shape, (int)(x1*20),(int)(y1*20));
2248 ShapeSetLine (tag, shape, (int)(x1*20);
2249 ShapeSetLine (tag, shape, x*20,0);
2250 ShapeSetLine (tag, shape, 0,-y*20);
2251 ShapeSetLine (tag, shape, -x*20,0);*/
2252 swf_ShapeSetEnd(i->tag);
2253 swf_ShapeFree(shape);
2256 i->tag = swf_InsertTag(i->tag,ST_PLACEOBJECT2);
2258 swf_ObjectPlace(i->tag,myshapeid,/*depth*/i->depth++,&i->page_matrix,NULL,NULL);
2261 int swfoutput_drawimagejpeg_old(struct swfoutput*obj, char*filename, int sizex,int sizey,
2262 double x1,double y1,
2263 double x2,double y2,
2264 double x3,double y3,
2265 double x4,double y4)
2267 swfoutput_internal*i = (swfoutput_internal*)obj->internal;
2274 int bitid = ++i->currentswfid;
2276 i->tag = swf_InsertTag(i->tag,ST_DEFINEBITSJPEG2);
2277 swf_SetU16(i->tag, bitid);
2278 if(swf_SetJPEGBits(i->tag, filename, config_jpegquality)<0) {
2279 swf_DeleteTag(i->tag);
2284 drawimage(obj, bitid, sizex, sizey, x1,y1,x2,y2,x3,y3,x4,y4);
2288 int swfoutput_drawimagejpeg(struct swfoutput*obj, RGBA*mem, int sizex,int sizey,
2289 double x1,double y1,
2290 double x2,double y2,
2291 double x3,double y3,
2292 double x4,double y4)
2294 swfoutput_internal*i = (swfoutput_internal*)obj->internal;
2303 int bitid = ++i->currentswfid;
2305 i->tag = swf_InsertTag(i->tag,ST_DEFINEBITSJPEG2);
2306 swf_SetU16(i->tag, bitid);
2307 swf_SetJPEGBits2(i->tag,sizex,sizey,mem,config_jpegquality);
2308 drawimage(obj, bitid, sizex, sizey, x1,y1,x2,y2,x3,y3,x4,y4);
2312 int swfoutput_drawimagelossless(struct swfoutput*obj, RGBA*mem, int sizex,int sizey,
2313 double x1,double y1,
2314 double x2,double y2,
2315 double x3,double y3,
2316 double x4,double y4)
2318 swfoutput_internal*i = (swfoutput_internal*)obj->internal;
2325 int bitid = ++i->currentswfid;
2327 i->tag = swf_InsertTag(i->tag,ST_DEFINEBITSLOSSLESS);
2328 swf_SetU16(i->tag, bitid);
2329 if(swf_SetLosslessBits(i->tag,sizex,sizey,mem, BMF_32BIT)<0) {
2330 swf_DeleteTag(i->tag);
2335 drawimage(obj, bitid, sizex, sizey, x1,y1,x2,y2,x3,y3,x4,y4);
2339 int swfoutput_drawimagelosslessN(struct swfoutput*obj, U8*mem, RGBA*pal, int sizex,int sizey,
2340 double x1,double y1,
2341 double x2,double y2,
2342 double x3,double y3,
2343 double x4,double y4, int n)
2345 swfoutput_internal*i = (swfoutput_internal*)obj->internal;
2355 /* SWF expects scanlines to be 4 byte aligned */
2358 mem2 = (U8*)malloc(BYTES_PER_SCANLINE(sizex)*sizey);
2360 for(y=0;y<sizey;y++)
2362 for(x=0;x<sizex;x++)
2363 *ptr++ = mem[y*sizex+x];
2364 ptr+= BYTES_PER_SCANLINE(sizex)-sizex;
2369 int bitid = ++i->currentswfid;
2371 i->tag = swf_InsertTag(i->tag,ST_DEFINEBITSLOSSLESS2);
2372 swf_SetU16(i->tag, bitid);
2373 if(swf_SetLosslessBitsIndexed(i->tag,sizex,sizey,mem, pal, n)<0) {
2374 swf_DeleteTag(i->tag);
2381 drawimage(obj, bitid, sizex, sizey, x1,y1,x2,y2,x3,y3,x4,y4);
2385 void swfoutput_drawimageagain(struct swfoutput*obj, int id, int sizex,int sizey,
2386 double x1,double y1,
2387 double x2,double y2,
2388 double x3,double y3,
2389 double x4,double y4)
2391 swfoutput_internal*i = (swfoutput_internal*)obj->internal;
2398 drawimage(obj, id, sizex, sizey, x1,y1,x2,y2,x3,y3,x4,y4);
2401 void swfoutput_setparameter(char*name, char*value)
2403 if(!strcmp(name, "drawonlyshapes")) {
2404 config_drawonlyshapes = atoi(value);
2405 } else if(!strcmp(name, "ignoredraworder")) {
2406 config_ignoredraworder = atoi(value);
2407 } else if(!strcmp(name, "filloverlap")) {
2408 config_filloverlap = atoi(value);
2409 } else if(!strcmp(name, "linksopennewwindow")) {
2410 config_opennewwindow = atoi(value);
2411 } else if(!strcmp(name, "opennewwindow")) {
2412 config_opennewwindow = atoi(value);
2413 } else if(!strcmp(name, "storeallcharacters")) {
2414 config_storeallcharacters = atoi(value);
2415 } else if(!strcmp(name, "enablezlib")) {
2416 config_enablezlib = atoi(value);
2417 } else if(!strcmp(name, "insertstop")) {
2418 config_insertstoptag = atoi(value);
2419 } else if(!strcmp(name, "protected")) {
2420 config_protect = atoi(value);
2421 } else if(!strcmp(name, "flashversion")) {
2422 config_flashversion = atoi(value);
2423 } else if(!strcmp(name, "minlinewidth")) {
2424 config_minlinewidth = atof(value);
2425 } else if(!strcmp(name, "jpegquality")) {
2426 int val = atoi(value);
2428 if(val>100) val=100;
2429 config_jpegquality = val;
2430 } else if(!strcmp(name, "splinequality")) {
2431 int v = atoi(value);
2432 v = 500-(v*5); // 100% = 0.25 pixel, 0% = 25 pixel
2434 config_splinemaxerror = v;
2435 } else if(!strcmp(name, "fontquality")) {
2436 int v = atoi(value);
2437 v = 500-(v*5); // 100% = 0.25 pixel, 0% = 25 pixel
2439 config_fontsplinemaxerror = v;
2441 fprintf(stderr, "unknown parameter: %s (=%s)\n", name, value);