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
106 char fillstylechanged;
111 chardata_t chardata[CHARDATAMAX];
114 } swfoutput_internal;
116 static swfoutput_internal* init_internal_struct()
118 swfoutput_internal*i = (swfoutput_internal*)malloc(sizeof(swfoutput_internal));
119 memset(i, 0, sizeof(swfoutput_internal));
139 i->fillstylechanged = 0;
148 static void startshape(struct swfoutput* obj);
149 static void starttext(struct swfoutput* obj);
150 static void endshape(struct swfoutput* obj,int clip);
151 static void endtext(struct swfoutput* obj);
153 // matrix multiplication. changes p0
154 static void transform (plotxy*p0,struct swfmatrix*m)
157 x = m->m11*p0->x+m->m12*p0->y;
158 y = m->m21*p0->x+m->m22*p0->y;
163 // write a move-to command into the swf
164 static int moveto(struct swfoutput*obj, TAG*tag, plotxy p0)
166 swfoutput_internal*i = (swfoutput_internal*)obj->internal;
167 int rx = (int)(p0.x*20);
168 int ry = (int)(p0.y*20);
169 if(rx!=i->swflastx || ry!=i->swflasty || i->fillstylechanged) {
170 swf_ShapeSetMove (tag, i->shape, rx,ry);
171 i->fillstylechanged = 0;
178 static int moveto(struct swfoutput*obj, TAG*tag, float x, float y)
180 swfoutput_internal*i = (swfoutput_internal*)obj->internal;
184 return moveto(obj, tag, p);
186 static void addPointToBBox(struct swfoutput*obj, int px, int py)
188 swfoutput_internal*i = (swfoutput_internal*)obj->internal;
194 swf_ExpandRect(&i->bboxrect, p);
196 swf_ExpandRect3(&i->bboxrect, p, i->linewidth*3/2);
200 // write a line-to command into the swf
201 static void lineto(struct swfoutput*obj, TAG*tag, plotxy p0)
203 swfoutput_internal*i = (swfoutput_internal*)obj->internal;
204 int px = (int)(p0.x*20);
205 int py = (int)(p0.y*20);
206 int rx = (px-i->swflastx);
207 int ry = (py-i->swflasty);
208 /* we can't skip this for rx=0,ry=0, those
210 swf_ShapeSetLine (tag, i->shape, rx,ry);
212 addPointToBBox(obj, i->swflastx,i->swflasty);
213 addPointToBBox(obj, px,py);
219 static void lineto(struct swfoutput*obj, TAG*tag, double x, double y)
227 // write a spline-to command into the swf
228 static void splineto(struct swfoutput*obj, TAG*tag, plotxy control,plotxy end)
230 swfoutput_internal*i = (swfoutput_internal*)obj->internal;
231 int lastlastx = i->swflastx;
232 int lastlasty = i->swflasty;
234 int cx = ((int)(control.x*20)-i->swflastx);
235 int cy = ((int)(control.y*20)-i->swflasty);
238 int ex = ((int)(end.x*20)-i->swflastx);
239 int ey = ((int)(end.y*20)-i->swflasty);
243 if(cx || cy || ex || ey) {
244 swf_ShapeSetCurve(tag, i->shape, cx,cy,ex,ey);
245 addPointToBBox(obj, lastlastx ,lastlasty );
246 addPointToBBox(obj, lastlastx+cx,lastlasty+cy);
247 addPointToBBox(obj, lastlastx+cx+ex,lastlasty+cy+ey);
252 /* write a line, given two points and the transformation
254 static void line(struct swfoutput*obj, TAG*tag, plotxy p0, plotxy p1, struct swfmatrix*m)
258 moveto(obj, tag, p0);
259 lineto(obj, tag, p1);
262 /* write a cubic (!) spline. This involves calling the approximate()
263 function out of spline.cc to convert it to a quadratic spline. */
264 static void spline(struct swfoutput*obj, TAG*tag,plotxy p0,plotxy p1,plotxy p2,plotxy p3,struct swfmatrix*m)
266 swfoutput_internal*i = (swfoutput_internal*)obj->internal;
268 struct qspline q[128];
282 /* fonts use a different approximation than shapes */
283 num = cspline_approximate(&c, q, config_fontsplinemaxerror/20.0, APPROXIMATE_RECURSIVE_BINARY);
284 //num = cspline_approximate(&c, q, 10.0, APPROXIMATE_INFLECTION);
286 num = cspline_approximate(&c, q, config_splinemaxerror/20.0, APPROXIMATE_RECURSIVE_BINARY);
290 moveto(obj, tag,q[t].start);
291 splineto(obj, tag,q[t].control, q[t].end);
295 void resetdrawer(struct swfoutput*obj)
297 swfoutput_internal*i = (swfoutput_internal*)obj->internal;
302 static void stopFill(struct swfoutput*obj)
304 swfoutput_internal*i = (swfoutput_internal*)obj->internal;
307 swf_ShapeSetStyle(i->tag,i->shape,i->linestyleid,0x8000,0);
308 i->fillstylechanged = 1;
312 static void startFill(struct swfoutput*obj)
314 swfoutput_internal*i = (swfoutput_internal*)obj->internal;
317 swf_ShapeSetStyle(i->tag,i->shape,0x8000,i->fillstyleid,0);
318 i->fillstylechanged = 1;
323 /* draw an outline. These are generated by pdf2swf and by t1lib
324 (representing characters). */
325 void drawpath(struct swfoutput*obj, SWF_OUTLINE*outline, struct swfmatrix*m, int log)
327 swfoutput_internal*i = (swfoutput_internal*)obj->internal;
328 if( i->tag->id != ST_DEFINESHAPE &&
329 i->tag->id != ST_DEFINESHAPE2 &&
330 i->tag->id != ST_DEFINESHAPE3)
332 msg("<error> internal error: drawpath needs a shape tag, not %d\n",i->tag->id);
336 double lastx=0,lasty=0;
337 double firstx=0,firsty=0;
342 x += (outline->dest.x/(float)0xffff);
343 y += (outline->dest.y/(float)0xffff);
344 if(outline->type == SWF_PATHTYPE_MOVE)
346 //if(!init && fill && obj->drawmode != DRAWMODE_EOFILL && !ignoredraworder) {
347 if(config_filloverlap && !init && i->fill && obj->drawmode != DRAWMODE_EOFILL) {
348 /* drawmode=FILL (not EOFILL) means that
349 seperate shapes do not cancel each other out.
350 On SWF side, we need to start a new shape for each
351 closed polygon, because SWF only knows EOFILL.
358 if(((int)(lastx*20) != (int)(firstx*20) ||
359 (int)(lasty*20) != (int)(firsty*20)) &&
368 if(log) printf("fix: %f,%f -> %f,%f\n",p0.x,p0.y,p1.x,p1.y);
369 line(obj,i->tag, p0, p1, m);
375 else if(outline->type == SWF_PATHTYPE_LINE)
383 if(log) printf("line: %f,%f -> %f,%f\n",p0.x,p0.y,p1.x,p1.y);
384 line(obj,i->tag, p0,p1,m);
386 else if(outline->type == SWF_PATHTYPE_BEZIER)
392 SWF_BEZIERSEGMENT*o2 = (SWF_BEZIERSEGMENT*)outline;
395 p1.x=o2->C.x/(float)0xffff+lastx;
396 p1.y=o2->C.y/(float)0xffff+lasty;
397 p2.x=o2->B.x/(float)0xffff+lastx;
398 p2.y=o2->B.y/(float)0xffff+lasty;
401 if(log) printf("spline: %f,%f -> %f,%f\n",p3.x,p3.y,p0.x,p0.y);
402 spline(obj,i->tag,p0,p1,p2,p3,m);
405 msg("<error> drawpath: unknown outline type:%d\n", outline->type);
409 outline = outline->link;
411 if(((int)(lastx*20) != (int)(firstx*20) ||
412 (int)(lasty*20) != (int)(firsty*20)) &&
421 if(log) printf("fix: %f,%f -> %f,%f\n",p0.x,p0.y,p1.x,p1.y);
422 line(obj, i->tag, p0, p1, m);
426 plotxy getPivot(struct swfoutput*obj, SWF_OUTLINE*outline, int dir, double line_width, int end, int trytwo)
428 swfoutput_internal*i = (swfoutput_internal*)obj->internal;
429 SWF_PATHPOINT next, next2;
430 double xv=0,yv=0, xv2=0, yv2=0;
435 if(outline->type == SWF_PATHTYPE_LINE) {
436 next = outline->dest;
438 next = ((SWF_BEZIERSEGMENT*)outline)->B;
439 if(next.x==0 && next.y==0) {
440 next = ((SWF_BEZIERSEGMENT*)outline)->C;
442 if(next.x==0 && next.y==0) {
443 next = ((SWF_BEZIERSEGMENT*)outline)->dest;
447 if(trytwo && outline->last && outline->last->type != SWF_PATHTYPE_MOVE) {
448 if(outline->type == SWF_PATHTYPE_LINE) {
449 next2 = outline->last->dest;
451 SWF_PATHPOINT c = ((SWF_BEZIERSEGMENT*)(outline->last))->C;
452 SWF_PATHPOINT b = ((SWF_BEZIERSEGMENT*)(outline->last))->B;
453 next2.x = outline->last->dest.x - c.x;
454 next2.y = outline->last->dest.y - c.y;
455 if(next2.x==0 && next2.y==0) {
456 next2.x = outline->last->dest.x - b.x;
457 next2.y = outline->last->dest.y - b.y;
459 if(next2.x==0 && next2.y==0) {
460 next2.x = outline->last->dest.x;
461 next2.y = outline->last->dest.y;
467 if(outline->type == SWF_PATHTYPE_LINE) {
468 next = outline->dest;
470 SWF_PATHPOINT c = ((SWF_BEZIERSEGMENT*)outline)->C;
471 SWF_PATHPOINT b = ((SWF_BEZIERSEGMENT*)outline)->B;
472 next.x = outline->dest.x - c.x;
473 next.y = outline->dest.y - c.y;
474 if(next.x==0 && next.y==0) {
475 next.x = outline->dest.x - b.x;
476 next.y = outline->dest.y - b.y;
478 if(next.x==0 && next.y==0) {
479 next.x = outline->dest.x;
480 next.y = outline->dest.y;
484 if(trytwo && outline->link && outline->link->type != SWF_PATHTYPE_MOVE) {
485 if(outline->type == SWF_PATHTYPE_LINE) {
486 next2 = outline->link->dest;
488 next2 = ((SWF_BEZIERSEGMENT*)(outline->link))->B;
489 if(next2.x==0 && next2.y==0) {
490 next2 = ((SWF_BEZIERSEGMENT*)outline->link)->C;
492 if(next2.x==0 && next2.y==0) {
493 next2 = ((SWF_BEZIERSEGMENT*)outline->link)->dest;
501 xv = next.y/(float)0xffff;
502 yv = -next.x/(float)0xffff;
504 xv = -next.y/(float)0xffff;
505 yv = next.x/(float)0xffff;
508 double r = (line_width/2)/sqrt(xv*xv+yv*yv);
514 xv2 = next2.y/(float)0xffff;
515 yv2 = -next2.x/(float)0xffff;
517 xv2 = -next2.y/(float)0xffff;
518 yv2 = next2.x/(float)0xffff;
521 double r2 = (line_width/2)/sqrt(xv2*xv2+yv2*yv2);
526 double r3 = (line_width/2)/sqrt(xv*xv+yv*yv);
536 void drawShortPath(struct swfoutput*obj, double x, double y, struct swfmatrix* m, SWF_OUTLINE*outline)
538 swfoutput_internal*i = (swfoutput_internal*)obj->internal;
539 double lastx=x, lasty=y;
540 while (outline && outline->type != SWF_PATHTYPE_MOVE)
542 x += (outline->dest.x/(float)0xffff);
543 y += (outline->dest.y/(float)0xffff);
545 if(outline->type == SWF_PATHTYPE_LINE)
552 line(obj, i->tag, p0, p1, m);
554 else if(outline->type == SWF_PATHTYPE_BEZIER)
557 SWF_BEZIERSEGMENT*o2 = (SWF_BEZIERSEGMENT*)outline;
560 p1.x=o2->C.x/(float)0xffff+lastx;
561 p1.y=o2->C.y/(float)0xffff+lasty;
562 p2.x=o2->B.x/(float)0xffff+lastx;
563 p2.y=o2->B.y/(float)0xffff+lasty;
566 spline(obj, i->tag,p0,p1,p2,p3,m);
570 outline = outline->link;
574 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)
576 swfoutput_internal*i = (swfoutput_internal*)obj->internal;
580 if(line_cap == LINE_CAP_BUTT || line_cap == LINE_CAP_SQUARE) {
583 SWF_OUTLINE *last, *tmp=outline;
584 plotxy s,e,p0,p1,p2,p3,m0,m1,m2,m3;
590 while(tmp && tmp->type != SWF_PATHTYPE_MOVE) {
592 lx += (tmp->dest.x/(float)0xffff);
593 ly += (tmp->dest.y/(float)0xffff);
596 s = getPivot(obj, outline, 0, line_width, 0, 0);
597 e = getPivot(obj, last, 0, line_width, 1, 0);
599 if(line_cap == LINE_CAP_BUTT) {
600 /* make the clipping rectangle slighly bigger
601 than the line ending, so that it get's clipped
611 p2.x = x2 - s.y - s.x*ee;
612 p2.y = y2 + s.x - s.y*ee;
613 p3.x = x2 - s.y + s.x*ee;
614 p3.y = y2 + s.x + s.y*ee;
619 m2.x = lx + e.y - e.x*ee;
620 m2.y = ly - e.x - e.y*ee;
621 m3.x = lx + e.y + e.x*ee;
622 m3.y = ly - e.x + e.y*ee;
624 for(nr=0;nr<2;nr++) {
626 struct plotxy q0,q1,q2,q3,q4,q5;
629 if(line_cap == LINE_CAP_BUTT) {
631 /* FIXME: box should be smaller */
633 q1.x = i->sizex; q1.y = 0;
634 q2.x = i->sizex; q2.y = i->sizey;
635 q3.x = 0; q3.y = i->sizey;
637 /* FIXME: box should be smaller */
638 q0.x = i->sizex; q0.y = i->sizey;
639 q1.x = 0; q1.y = i->sizey;
641 q3.x = i->sizex; q3.y = 0;
645 moveto(obj, i->tag, q0);
646 lineto(obj, i->tag, q1);
647 lineto(obj, i->tag, q2);
648 lineto(obj, i->tag, q3);
649 lineto(obj, i->tag, q0);
652 lineto(obj, i->tag, q4);
655 line(obj, i->tag, p0, p1, m);
656 line(obj, i->tag, p1, p2, m);
657 line(obj, i->tag, p2, p3, m);
658 line(obj, i->tag, p3, p0, m);
660 if(line_cap == LINE_CAP_BUTT) {
661 lineto(obj, i->tag, q0);
662 endshape(obj, i->depth+2-nr);
674 drawShortPath(obj,x,y,m,outline);
676 if(line_cap == LINE_CAP_BUTT) {
682 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)
684 swfoutput_internal*i = (swfoutput_internal*)obj->internal;
685 plotxy d1,d2,p1,p2,p3,p4;
687 d1.x = (outline->dest.x/(float)0xffff);
688 d1.y = (outline->dest.y/(float)0xffff);
689 d2 = getPivot(obj, outline, 0, line_width, 0, 0);
691 assert(line_cap != LINE_CAP_ROUND);
692 if(line_cap == LINE_CAP_SQUARE) {
701 p2.x = x + d2.x + d1.x;
702 p2.y = y + d2.y + d1.y;
703 p3.x = x - d2.x + d1.x;
704 p3.y = y - d2.y + d1.y;
708 line(obj, i->tag, p1,p2, m);
709 line(obj, i->tag, p2,p3, m);
710 line(obj, i->tag, p3,p4, m);
711 line(obj, i->tag, p4,p1, m);
714 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)
716 swfoutput_internal*i = (swfoutput_internal*)obj->internal;
717 SWF_OUTLINE*tmp=outline;
720 assert(i->shapeid>=0);
723 drawT1toRect(obj, x, y, m,outline, num, line_cap, line_join, line_width);
725 while(tmp->link && tmp->link->type!=SWF_PATHTYPE_MOVE) {
726 xx += (tmp->dest.x/(float)0xffff);
727 yy += (tmp->dest.y/(float)0xffff);
731 assert(tmp->type == SWF_PATHTYPE_LINE);
732 assert(outline->type == SWF_PATHTYPE_LINE);
736 if(outline->link == tmp) {
737 /* the two straight line segments (which are everything we
738 need to draw) are very likely to overlap. To avoid that
739 they cancel each other out at the end points, start a new
740 shape for the second one */
741 endshape(obj,0);startshape(obj);
745 drawT1toRect(obj, xx, yy, m, tmp, num, line_cap, line_join, line_width);
747 if(outline->link != tmp)
749 stopFill(obj);stop=1;
751 tmp->type = SWF_PATHTYPE_MOVE;
752 x += (outline->dest.x/(float)0xffff);
753 y += (outline->dest.y/(float)0xffff);
754 outline = outline->link;
755 drawShortPath(obj, x, y, m, outline);
763 static int t1len(struct swfoutput*obj, SWF_OUTLINE*line)
765 swfoutput_internal*i = (swfoutput_internal*)obj->internal;
767 while(line && line->type != SWF_PATHTYPE_MOVE) {
774 static float t1linelen(struct swfoutput*obj, SWF_OUTLINE*line)
776 swfoutput_internal*i = (swfoutput_internal*)obj->internal;
778 x = (line->dest.x/(float)0xffff);
779 y = (line->dest.y/(float)0xffff);
780 return sqrt(x*x+y*y);
783 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)
785 swfoutput_internal*i = (swfoutput_internal*)obj->internal;
786 if( i->tag->id != ST_DEFINESHAPE &&
787 i->tag->id != ST_DEFINESHAPE2 &&
788 i->tag->id != ST_DEFINESHAPE3) {
789 msg("<error> internal error: drawpath needs a shape tag, not %d\n",i->tag->id);
792 assert(i->shapeid>=0);
794 double lastx=0,lasty=0;
797 SWF_OUTLINE*tmp = outline, *last = 0;
802 x += (tmp->dest.x/(float)0xffff);
803 y += (tmp->dest.y/(float)0xffff);
805 if(!tmp || tmp->type == SWF_PATHTYPE_MOVE) {
807 if(last->type == SWF_PATHTYPE_LINE && t1linelen(obj,last)>line_width*2 &&
808 lastwasline && line_cap != LINE_CAP_ROUND)
809 drawShortPathWithStraightEnds(obj, lastx, lasty, m, last, valid, line_cap, line_join, line_width);
811 drawShortPathWithEnds(obj, lastx, lasty, m, last, valid, line_cap, line_join, line_width);
825 if(tmp && tmp->type == SWF_PATHTYPE_LINE && t1linelen(obj,tmp)>line_width*2)
831 tmp->link->last = tmp; // make sure list is properly linked in both directions
836 static inline int colorcompare(struct swfoutput*obj, RGBA*a,RGBA*b)
848 static SRECT getcharacterbbox(struct swfoutput*obj, SWFFONT*font)
850 swfoutput_internal*i = (swfoutput_internal*)obj->internal;
853 memset(&r, 0, sizeof(r));
856 if(debug) printf("\n");
857 for(t=0;t<i->chardatapos;t++)
859 if(i->chardata[t].fontid != font->id) {
860 msg("<error> Internal error: fontid %d != fontid %d", i->chardata[t].fontid, font->id);
863 SRECT b = font->layout->bounds[i->chardata[t].charid];
864 b.xmin *= i->chardata[t].size;
865 b.ymin *= i->chardata[t].size;
866 b.xmax *= i->chardata[t].size;
867 b.ymax *= i->chardata[t].size;
872 b.xmin += i->chardata[t].x;
873 b.ymin += i->chardata[t].y;
874 b.xmax += i->chardata[t].x;
875 b.ymax += i->chardata[t].y;
877 /* until we solve the INTERNAL_SCALING problem (see below)
878 make sure the bounding box is big enough */
884 if(debug) printf("(%f,%f,%f,%f) -> (%f,%f,%f,%f) [font %d/%d, char %d]\n",
885 font->layout->bounds[i->chardata[t].charid].xmin/20.0,
886 font->layout->bounds[i->chardata[t].charid].ymin/20.0,
887 font->layout->bounds[i->chardata[t].charid].xmax/20.0,
888 font->layout->bounds[i->chardata[t].charid].ymax/20.0,
893 i->chardata[t].fontid,
895 i->chardata[t].charid
897 swf_ExpandRect2(&r, &b);
899 if(debug) printf("-----> (%f,%f,%f,%f)\n",
907 static void putcharacters(struct swfoutput*obj, TAG*tag)
909 swfoutput_internal*i = (swfoutput_internal*)obj->internal;
913 color.r = i->chardata[0].color.r^255;
922 int charadvance[128];
925 int glyphbits=1; //TODO: can this be zero?
928 if(tag->id != ST_DEFINETEXT &&
929 tag->id != ST_DEFINETEXT2) {
930 msg("<error> internal error: putcharacters needs an text tag, not %d\n",tag->id);
933 if(!i->chardatapos) {
934 msg("<warning> putcharacters called with zero characters");
937 for(pass = 0; pass < 2; pass++)
947 advancebits++; // add sign bit
948 swf_SetU8(tag, glyphbits);
949 swf_SetU8(tag, advancebits);
952 for(t=0;t<=i->chardatapos;t++)
954 if(lastfontid != i->chardata[t].fontid ||
955 lastx!=i->chardata[t].x ||
956 lasty!=i->chardata[t].y ||
957 !colorcompare(obj,&color, &i->chardata[t].color) ||
959 lastsize != i->chardata[t].size ||
962 if(charstorepos && pass==0)
965 for(s=0;s<charstorepos;s++)
967 while(charids[s]>=(1<<glyphbits))
969 while(charadvance[s]>=(1<<advancebits))
973 if(charstorepos && pass==1)
975 tag->writeBit = 0; // Q&D
976 swf_SetBits(tag, 0, 1); // GLYPH Record
977 swf_SetBits(tag, charstorepos, 7); // number of glyphs
979 for(s=0;s<charstorepos;s++)
981 swf_SetBits(tag, charids[s], glyphbits);
982 swf_SetBits(tag, charadvance[s], advancebits);
987 if(pass == 1 && t<i->chardatapos)
993 if(lastx != i->chardata[t].x ||
994 lasty != i->chardata[t].y)
996 newx = i->chardata[t].x;
997 newy = i->chardata[t].y;
1003 if(!colorcompare(obj,&color, &i->chardata[t].color))
1005 color = i->chardata[t].color;
1008 font.id = i->chardata[t].fontid;
1009 if(lastfontid != i->chardata[t].fontid || lastsize != i->chardata[t].size)
1012 tag->writeBit = 0; // Q&D
1013 swf_TextSetInfoRecord(tag, newfont, i->chardata[t].size, newcolor, newx,newy);
1016 lastfontid = i->chardata[t].fontid;
1017 lastx = i->chardata[t].x;
1018 lasty = i->chardata[t].y;
1019 lastsize = i->chardata[t].size;
1022 if(t==i->chardatapos)
1026 int nextt = t==i->chardatapos-1?t:t+1;
1027 int rel = i->chardata[nextt].x-i->chardata[t].x;
1028 if(rel>=0 && (rel<(1<<(advancebits-1)) || pass==0)) {
1030 lastx=i->chardata[nextt].x;
1034 lastx=i->chardata[t].x;
1036 charids[charstorepos] = i->chardata[t].charid;
1037 charadvance[charstorepos] = advance;
1044 static void putcharacter(struct swfoutput*obj, int fontid, int charid,
1045 int x,int y, int size)
1047 swfoutput_internal*i = (swfoutput_internal*)obj->internal;
1048 if(i->chardatapos == CHARDATAMAX)
1050 msg("<warning> Character buffer too small. SWF will be slightly bigger");
1054 i->chardata[i->chardatapos].fontid = fontid;
1055 i->chardata[i->chardatapos].charid = charid;
1056 i->chardata[i->chardatapos].x = x;
1057 i->chardata[i->chardatapos].y = y;
1058 i->chardata[i->chardatapos].color = obj->fillrgb;
1059 i->chardata[i->chardatapos].size = size;
1063 /* Notice: we can only put chars in the range -1639,1638 (-32768/20,32768/20).
1064 So if we set this value to high, the char coordinates will overflow.
1065 If we set it to low, however, the char positions will be inaccurate */
1066 #define FONT_INTERNAL_SIZE 4
1068 /* process a character. */
1069 static int drawchar(struct swfoutput*obj, SWFFONT *swffont, char*character, int charnr, int u, swfmatrix*m)
1071 swfoutput_internal*i = (swfoutput_internal*)obj->internal;
1073 msg("<warning> Font is NULL");
1077 int charid = getCharID(swffont, charnr, character, u);
1080 msg("<warning> Didn't find character '%s' (c=%d,u=%d) in current charset (%s, %d characters)",
1081 FIXNULL(character),charnr, u, FIXNULL((char*)swffont->name), swffont->numchars);
1092 float det = ((m->m11*m->m22)-(m->m21*m->m12));
1093 if(fabs(det) < 0.0005) {
1094 /* x direction equals y direction- the text is invisible */
1097 det = 20*FONT_INTERNAL_SIZE / det;
1100 p.x = (SCOORD)(( x * m->m22 - y * m->m12)*det);
1101 p.y = (SCOORD)((- x * m->m21 + y * m->m11)*det);
1103 putcharacter(obj, swffont->id, charid,p.x,p.y,FONT_INTERNAL_SIZE);
1104 swf_FontUseGlyph(swffont, charid);
1109 SWF_OUTLINE*outline = font->getOutline(character, charnr);
1110 char* charname = character;
1113 msg("<warning> Didn't find character '%s' (%d) in current charset (%s)",
1114 FIXNULL(character),charnr,FIXNULL(font->getName()));
1133 drawpath(tag, outline, &m2, 0);
1138 static void endtext(swfoutput*obj)
1140 swfoutput_internal*i = (swfoutput_internal*)obj->internal;
1144 i->tag = swf_InsertTag(i->tag,ST_DEFINETEXT);
1145 swf_SetU16(i->tag, i->textid);
1148 r = getcharacterbbox(obj, obj->swffont);
1150 swf_SetRect(i->tag,&r);
1153 swf_GetMatrix(0, &m);
1154 swf_SetMatrix(i->tag,&m);
1156 putcharacters(obj, i->tag);
1157 swf_SetU8(i->tag,0);
1158 i->tag = swf_InsertTag(i->tag,ST_PLACEOBJECT2);
1159 //swf_ObjectPlace(i->tag,i->textid,/*depth*/i->depth++,&i->page_matrix,NULL,NULL);
1161 swf_MatrixJoin(&m2,&obj->fontmatrix, &i->page_matrix);
1163 swf_ObjectPlace(i->tag,i->textid,/*depth*/i->depth++,&m2,NULL,NULL);
1168 /* draw a curved polygon. */
1169 void swfoutput_drawpath(swfoutput*obj, SWF_OUTLINE*outline,
1172 swfoutput_internal*i = (swfoutput_internal*)obj->internal;
1176 /* Multiple polygons in one shape don't overlap correctly,
1177 so we better start a new shape here if the polygon is filled
1179 if(i->shapeid>=0 && i->fill && !config_ignoredraworder) {
1192 drawpath(obj, outline,m, 0);
1195 void swfoutput_drawpath2poly(struct swfoutput*obj, SWF_OUTLINE*outline, struct swfmatrix*m, int line_join, int line_cap, double line_width, double miter_limit)
1197 swfoutput_internal*i = (swfoutput_internal*)obj->internal;
1202 assert(i->shapeid<0);
1206 drawpath2poly(obj, outline, m, 0, line_join, line_cap, line_width, miter_limit);
1209 int getCharID(SWFFONT *font, int charnr, char *charname, int u)
1212 if(charname && font->glyphnames) {
1213 for(t=0;t<font->numchars;t++) {
1214 if(font->glyphnames[t] && !strcmp(font->glyphnames[t],charname)) {
1215 msg("<debug> Char [%d,>%s<,%d] maps to %d\n", charnr, charname, u, t);
1219 /* if we didn't find the character, maybe
1220 we can find the capitalized version */
1221 for(t=0;t<font->numchars;t++) {
1222 if(font->glyphnames[t] && !strcasecmp(font->glyphnames[t],charname)) {
1223 msg("<debug> Char [%d,>>%s<<,%d] maps to %d\n", charnr, charname, u, t);
1229 if(u>0 && font->encoding != 255) {
1230 /* try to use the unicode id */
1231 if(u>=0 && u<font->maxascii && font->ascii2glyph[u]>=0) {
1232 msg("<debug> Char [%d,%s,>%d<] maps to %d\n", charnr, charname, u, font->ascii2glyph[u]);
1233 return font->ascii2glyph[u];
1237 if(font->encoding != FONT_ENCODING_UNICODE) {
1238 /* the following only works if the font encoding
1239 is US-ASCII based. It's needed for fonts which return broken unicode
1241 if(charnr>=0 && charnr<font->maxascii && font->ascii2glyph[charnr]>=0) {
1242 msg("<debug> Char [>%d<,%s,%d] maps to %d\n", charnr, charname, u, font->ascii2glyph[charnr]);
1243 return font->ascii2glyph[charnr];
1247 if(charnr>=0 && charnr<font->numchars) {
1248 msg("<debug> Char [>%d<,%s,%d] maps to %d\n", charnr, charname, u, charnr);
1256 /* set's the t1 font index of the font to use for swfoutput_drawchar(). */
1257 void swfoutput_setfont(struct swfoutput*obj, char*fontid, char*filename)
1259 swfoutput_internal*i = (swfoutput_internal*)obj->internal;
1260 fontlist_t*last=0,*iterator;
1262 msg("<error> No fontid");
1266 if(obj->swffont && obj->swffont->name && !strcmp((char*)obj->swffont->name,fontid))
1269 /* TODO: remove the need for this (enhance getcharacterbbox so that it can cope
1270 with multiple fonts */
1273 iterator = i->fontlist;
1275 if(!strcmp((char*)iterator->swffont->name,fontid)) {
1276 obj->swffont = iterator->swffont;
1280 iterator = iterator->next;
1284 msg("<error> No filename given for font- internal error?");
1288 swf_SetLoadFontParameters(64,/*skip unused*/0,/*full unicode*/1);
1289 SWFFONT*swffont = swf_LoadFont(filename);
1292 msg("<warning> Couldn't load font %s (%s)", fontid, filename);
1293 swffont = swf_LoadFont(0);
1296 if(swffont->glyph2ascii) {
1299 /* check whether the Unicode indices look o.k.
1300 If they don't, disable the unicode lookup by setting
1301 the encoding to 255 */
1302 for(t=0;t<swffont->numchars;t++) {
1303 int c = swffont->glyph2ascii[t];
1304 if(c && c < 32 && swffont->glyph[t].shape->bitlen > 16) {
1305 // the character maps into the unicode control character range
1306 // between 0001-001f. Yet it is not empty. Treat the one
1307 // mapping as broken, and look how many of those we find.
1312 msg("<warning> Font %s has bad unicode mapping", swffont->name);
1313 swffont->encoding = 255;
1317 swf_FontSetID(swffont, ++i->currentswfid);
1319 if(getScreenLogLevel() >= LOGLEVEL_DEBUG) {
1320 // print font information
1321 msg("<debug> Font %s (%s)",swffont->name, filename);
1322 msg("<debug> | ID: %d", swffont->id);
1323 msg("<debug> | Version: %d", swffont->version);
1324 msg("<debug> | Name: %s", fontid);
1325 msg("<debug> | Numchars: %d", swffont->numchars);
1326 msg("<debug> | Maxascii: %d", swffont->maxascii);
1327 msg("<debug> | Style: %d", swffont->style);
1328 msg("<debug> | Encoding: %d", swffont->encoding);
1329 for(int iii=0; iii<swffont->numchars;iii++) {
1330 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,
1331 swffont->layout->bounds[iii].xmin/20.0,
1332 swffont->layout->bounds[iii].ymin/20.0,
1333 swffont->layout->bounds[iii].xmax/20.0,
1334 swffont->layout->bounds[iii].ymax/20.0
1337 for(t=0;t<swffont->maxascii;t++) {
1338 if(swffont->ascii2glyph[t] == iii)
1339 msg("<debug> | - maps to %d",t);
1344 /* set the font name to the ID we use here */
1345 if(swffont->name) free(swffont->name);
1346 swffont->name = (U8*)strdup(fontid);
1348 iterator = new fontlist_t;
1349 iterator->swffont = swffont;
1353 last->next = iterator;
1355 i->fontlist = iterator;
1357 obj->swffont = swffont;
1360 int swfoutput_queryfont(struct swfoutput*obj, char*fontid)
1362 swfoutput_internal*i = (swfoutput_internal*)obj->internal;
1363 fontlist_t *iterator = i->fontlist;
1365 if(!strcmp((char*)iterator->swffont->name,fontid))
1367 iterator = iterator->next;
1372 /* set's the matrix which is to be applied to characters drawn by
1373 swfoutput_drawchar() */
1374 void swfoutput_setfontmatrix(struct swfoutput*obj,double m11,double m12,
1375 double m21,double m22)
1377 swfoutput_internal*i = (swfoutput_internal*)obj->internal;
1378 if(obj->fontm11 == m11 &&
1379 obj->fontm12 == m12 &&
1380 obj->fontm21 == m21 &&
1381 obj->fontm22 == m22)
1391 m.sx = (U32)(((obj->fontm11)*65536)/FONT_INTERNAL_SIZE); m.r1 = (U32)(((obj->fontm12)*65536)/FONT_INTERNAL_SIZE);
1392 m.r0 = (U32)(((obj->fontm21)*65536)/FONT_INTERNAL_SIZE); m.sy = (U32)(((obj->fontm22)*65536)/FONT_INTERNAL_SIZE);
1395 obj->fontmatrix = m;
1398 /* draws a character at x,y. */
1399 int swfoutput_drawchar(struct swfoutput* obj,double x,double y,char*character, int charnr, int u)
1401 swfoutput_internal*i = (swfoutput_internal*)obj->internal;
1403 m.m11 = obj->fontm11;
1404 m.m12 = obj->fontm12;
1405 m.m21 = obj->fontm21;
1406 m.m22 = obj->fontm22;
1409 return drawchar(obj, obj->swffont, character, charnr, u, &m);
1412 static void endpage(struct swfoutput*obj)
1414 swfoutput_internal*i = (swfoutput_internal*)obj->internal;
1420 swfoutput_endclip(obj);
1422 if(config_insertstoptag) {
1424 atag = action_Stop(atag);
1425 atag = action_End(atag);
1426 i->tag = swf_InsertTag(i->tag,ST_DOACTION);
1427 swf_ActionSet(i->tag,atag);
1429 i->tag = swf_InsertTag(i->tag,ST_SHOWFRAME);
1432 void swfoutput_newpage(struct swfoutput*obj, int pageNum, int movex, int movey, int x1, int y1, int x2, int y2)
1434 swfoutput_internal*i = (swfoutput_internal*)obj->internal;
1438 swf_GetMatrix(0, &i->page_matrix);
1439 i->page_matrix.tx = movex*20;
1440 i->page_matrix.ty = movey*20;
1442 for(i->depth--;i->depth>=i->startdepth;i->depth--) {
1443 i->tag = swf_InsertTag(i->tag,ST_REMOVEOBJECT2);
1444 swf_SetU16(i->tag,i->depth);
1446 i->depth = i->startdepth = 3; /* leave room for clip and background rectangle */
1450 x1*=20;y1*=20;x2*=20;y2*=20;
1452 if(i->lastpagesize.xmin != x1 ||
1453 i->lastpagesize.xmax != x2 ||
1454 i->lastpagesize.ymin != y1 ||
1455 i->lastpagesize.ymax != y2)
1456 {/* add white clipping rectangle */
1457 msg("<notice> processing page %d (%dx%d)", pageNum,i->sizex,i->sizey);
1460 msg("<notice> Page has a different size than previous ones");
1461 i->tag = swf_InsertTag(i->tag,ST_REMOVEOBJECT2);
1462 swf_SetU16(i->tag,1);
1463 i->tag = swf_InsertTag(i->tag,ST_REMOVEOBJECT2);
1464 swf_SetU16(i->tag,2);
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,/*depth*/1,0,0,0);
1492 i->tag = swf_InsertTag(i->tag, ST_PLACEOBJECT2);
1493 swf_ObjectPlaceClip(i->tag,shapeid,/*depth*/2,0,0,0,65535);
1495 msg("<notice> processing page %d", pageNum);
1498 i->lastpagesize.xmin = x1;
1499 i->lastpagesize.xmax = x2;
1500 i->lastpagesize.ymin = y1;
1501 i->lastpagesize.ymax = y2;
1502 swf_ExpandRect2(&obj->swf.movieSize, &i->lastpagesize);
1507 /* initialize the swf writer */
1508 void swfoutput_init(struct swfoutput* obj)
1510 memset(obj, 0, sizeof(struct swfoutput));
1511 obj->internal = init_internal_struct();
1513 swfoutput_internal*i = (swfoutput_internal*)obj->internal;
1518 msg("<verbose> initializing swf output for size %d*%d\n", i->sizex,i->sizey);
1523 memset(&obj->swf,0x00,sizeof(SWF));
1524 memset(&i->lastpagesize,0x00,sizeof(SRECT));
1526 obj->swf.fileVersion = config_flashversion;
1527 obj->swf.frameRate = 0x0040; // 1 frame per 4 seconds
1528 obj->swf.movieSize.xmin = 0;
1529 obj->swf.movieSize.ymin = 0;
1530 obj->swf.movieSize.xmax = 0;
1531 obj->swf.movieSize.ymax = 0;
1533 obj->swf.firstTag = swf_InsertTag(NULL,ST_SETBACKGROUNDCOLOR);
1534 i->tag = obj->swf.firstTag;
1535 rgb.a = rgb.r = rgb.g = rgb.b = 0xff;
1536 swf_SetRGB(i->tag,&rgb);
1538 i->startdepth = i->depth = 0;
1541 i->tag = swf_InsertTag(i->tag, ST_PROTECT);
1544 static void startshape(struct swfoutput*obj)
1546 swfoutput_internal*i = (swfoutput_internal*)obj->internal;
1553 i->tag = swf_InsertTag(i->tag,ST_DEFINESHAPE);
1555 swf_ShapeNew(&i->shape);
1556 i->linestyleid = swf_ShapeAddLineStyle(i->shape,i->linewidth,&obj->strokergb);
1557 rgb.r = obj->fillrgb.r;
1558 rgb.g = obj->fillrgb.g;
1559 rgb.b = obj->fillrgb.b;
1560 i->fillstyleid = swf_ShapeAddSolidFillStyle(i->shape,&obj->fillrgb);
1562 i->shapeid = ++i->currentswfid;
1563 swf_SetU16(i->tag,i->shapeid); // ID
1565 i->bboxrectpos = i->tag->len;
1568 r.xmax = 20*i->sizex;
1569 r.ymax = 20*i->sizey;
1570 swf_SetRect(i->tag,&r);
1572 memset(&i->bboxrect, 0, sizeof(i->bboxrect));
1574 swf_SetShapeStyles(i->tag,i->shape);
1575 swf_ShapeCountBits(i->shape,NULL,NULL);
1576 swf_SetShapeBits(i->tag,i->shape);
1578 /* TODO: do we really need this? */
1579 swf_ShapeSetAll(i->tag,i->shape,/*x*/0,/*y*/0,i->linestyleid,0,0);
1580 i->swflastx=i->swflasty=0;
1582 i->shapeisempty = 1;
1585 static void starttext(struct swfoutput*obj)
1587 swfoutput_internal*i = (swfoutput_internal*)obj->internal;
1591 i->textid = ++i->currentswfid;
1593 i->swflastx=i->swflasty=0;
1597 /* TODO: move to ../lib/rfxswf */
1598 void changeRect(struct swfoutput*obj, TAG*tag, int pos, SRECT*newrect)
1600 swfoutput_internal*i = (swfoutput_internal*)obj->internal;
1601 /* determine length of old rect */
1605 swf_GetRect(tag, &old);
1606 swf_ResetReadBits(tag);
1607 int pos_end = tag->pos;
1609 int len = tag->len - pos_end;
1610 U8*data = (U8*)malloc(len);
1611 memcpy(data, &tag->data[pos_end], len);
1614 swf_SetRect(tag, newrect);
1615 swf_SetBlock(tag, data, len);
1617 tag->pos = tag->readBit = 0;
1620 void cancelshape(swfoutput*obj)
1622 swfoutput_internal*i = (swfoutput_internal*)obj->internal;
1623 /* delete old shape tag */
1625 i->tag = i->tag->prev;
1626 swf_DeleteTag(todel);
1627 if(i->shape) {swf_ShapeFree(i->shape);i->shape=0;}
1629 i->bboxrectpos = -1;
1632 void fixAreas(swfoutput*obj)
1634 swfoutput_internal*i = (swfoutput_internal*)obj->internal;
1635 if(!i->shapeisempty && i->fill &&
1636 (i->bboxrect.xmin == i->bboxrect.xmax ||
1637 i->bboxrect.ymin == i->bboxrect.ymax) &&
1638 config_minlinewidth >= 0.001
1640 msg("<debug> Shape has size 0: width=%.2f height=%.2f",
1641 (i->bboxrect.xmax-i->bboxrect.xmin)/20.0,
1642 (i->bboxrect.ymax-i->bboxrect.ymin)/20.0
1645 SRECT r = i->bboxrect;
1647 if(r.xmin == r.xmax && r.ymin == r.ymax) {
1648 /* this thing comes down to a single dot- nothing to fix here */
1654 RGBA save_col = obj->strokergb;
1655 int save_width = i->linewidth;
1657 obj->strokergb = obj->fillrgb;
1658 i->linewidth = (int)(config_minlinewidth*20);
1659 if(i->linewidth==0) i->linewidth = 1;
1663 moveto(obj, i->tag, r.xmin/20.0,r.ymin/20.0);
1664 lineto(obj, i->tag, r.xmax/20.0,r.ymax/20.0);
1666 obj->strokergb = save_col;
1667 i->linewidth = save_width;
1672 static void endshape_noput(swfoutput*obj)
1674 swfoutput_internal*i = (swfoutput_internal*)obj->internal;
1677 //changeRect(obj, i->tag, i->bboxrectpos, &i->bboxrect);
1680 swf_ShapeFree(i->shape);
1685 static void endshape(swfoutput*obj, int clipdepth)
1687 swfoutput_internal*i = (swfoutput_internal*)obj->internal;
1694 if(i->shapeisempty ||
1695 (i->bboxrect.xmin == i->bboxrect.xmax &&
1696 i->bboxrect.ymin == i->bboxrect.ymax))
1698 // delete the shape again, we didn't do anything
1703 swf_ShapeSetEnd(i->tag);
1705 changeRect(obj, i->tag, i->bboxrectpos, &i->bboxrect);
1707 i->tag = swf_InsertTag(i->tag,ST_PLACEOBJECT2);
1710 swf_ObjectPlaceClip(i->tag,i->shapeid,i->depth++,&i->page_matrix,NULL,NULL,clipdepth);
1712 swf_ObjectPlace(i->tag,i->shapeid,/*depth*/i->depth++,&i->page_matrix,NULL,NULL);
1714 swf_ShapeFree(i->shape);
1717 i->bboxrectpos = -1;
1720 int swfoutput_save(struct swfoutput* obj, char*filename)
1722 swfoutput_internal*i = (swfoutput_internal*)obj->internal;
1724 fontlist_t *tmp,*iterator = i->fontlist;
1726 TAG*mtag = obj->swf.firstTag;
1727 if(iterator->swffont) {
1728 mtag = swf_InsertTag(mtag, ST_DEFINEFONT2);
1729 /*if(!storeallcharacters)
1730 swf_FontReduce(iterator->swffont);*/
1731 swf_FontSetDefine2(mtag, iterator->swffont);
1734 iterator = iterator->next;
1739 fi = open(filename, O_BINARY|O_CREAT|O_TRUNC|O_WRONLY, 0777);
1744 msg("<fatal> Could not create \"%s\". ", FIXNULL(filename));
1748 i->tag = swf_InsertTag(i->tag,ST_END);
1750 if(config_enablezlib || config_flashversion>=6) {
1751 if FAILED(swf_WriteSWC(fi,&obj->swf))
1752 msg("<error> WriteSWC() failed.\n");
1754 if FAILED(swf_WriteSWF(fi,&obj->swf))
1755 msg("<error> WriteSWF() failed.\n");
1760 msg("<notice> SWF written\n");
1764 /* Perform cleaning up, complete the swf, and write it out. */
1765 void swfoutput_destroy(struct swfoutput* obj)
1767 swfoutput_internal*i = (swfoutput_internal*)obj->internal;
1769 /* not initialized yet- nothing to destroy */
1773 fontlist_t *tmp,*iterator = i->fontlist;
1775 if(iterator->swffont) {
1776 swf_FontFree(iterator->swffont);iterator->swffont=0;
1779 iterator = iterator->next;
1782 swf_FreeTags(&obj->swf);
1785 memset(obj, 0, sizeof(swfoutput));
1788 void swfoutput_setdrawmode(swfoutput* obj, int mode)
1790 swfoutput_internal*i = (swfoutput_internal*)obj->internal;
1791 obj->drawmode = mode;
1792 if(mode == DRAWMODE_FILL)
1794 else if(mode == DRAWMODE_EOFILL)
1796 else if(mode == DRAWMODE_STROKE)
1798 else if(mode == DRAWMODE_CLIP)
1800 else if(mode == DRAWMODE_EOCLIP)
1804 void swfoutput_setfillcolor(swfoutput* obj, U8 r, U8 g, U8 b, U8 a)
1806 swfoutput_internal*i = (swfoutput_internal*)obj->internal;
1807 if(obj->fillrgb.r == r &&
1808 obj->fillrgb.g == g &&
1809 obj->fillrgb.b == b &&
1810 obj->fillrgb.a == a) return;
1820 void swfoutput_setstrokecolor(swfoutput* obj, U8 r, U8 g, U8 b, U8 a)
1822 swfoutput_internal*i = (swfoutput_internal*)obj->internal;
1823 if(obj->strokergb.r == r &&
1824 obj->strokergb.g == g &&
1825 obj->strokergb.b == b &&
1826 obj->strokergb.a == a) return;
1830 obj->strokergb.r = r;
1831 obj->strokergb.g = g;
1832 obj->strokergb.b = b;
1833 obj->strokergb.a = a;
1836 void swfoutput_setlinewidth(struct swfoutput*obj, double _linewidth)
1838 swfoutput_internal*i = (swfoutput_internal*)obj->internal;
1839 if(i->linewidth == (U16)(_linewidth*20))
1844 i->linewidth = (U16)(_linewidth*20);
1848 void swfoutput_startclip(swfoutput*obj, SWF_OUTLINE*outline, struct swfmatrix*m)
1850 swfoutput_internal*i = (swfoutput_internal*)obj->internal;
1856 if(i->clippos >= 127)
1858 msg("<warning> Too many clip levels.");
1863 int olddrawmode = obj->drawmode;
1864 swfoutput_setdrawmode(obj, DRAWMODE_CLIP);
1865 swfoutput_drawpath(obj, outline, m);
1866 swf_ShapeSetEnd(i->tag);
1867 swfoutput_setdrawmode(obj, olddrawmode);
1869 i->tag = swf_InsertTag(i->tag,ST_PLACEOBJECT2);
1870 i->cliptags[i->clippos] = i->tag;
1871 i->clipshapes[i->clippos] = i->shapeid;
1872 i->clipdepths[i->clippos] = i->depth++;
1875 endshape_noput(obj);
1878 void swfoutput_endclip(swfoutput*obj)
1880 swfoutput_internal*i = (swfoutput_internal*)obj->internal;
1887 msg("<error> Invalid end of clipping region");
1891 swf_ObjectPlaceClip(i->cliptags[i->clippos],i->clipshapes[i->clippos],i->clipdepths[i->clippos],&i->page_matrix,NULL,NULL,i->depth++);
1894 static void drawlink(struct swfoutput*obj, ActionTAG*,ActionTAG*, swfcoord*points, char mouseover);
1896 void swfoutput_linktourl(struct swfoutput*obj, char*url, swfcoord*points)
1898 swfoutput_internal*i = (swfoutput_internal*)obj->internal;
1900 if(!strncmp("http://pdf2swf:", url, 15)) {
1901 char*tmp = strdup(url);
1902 int l = strlen(tmp);
1905 swfoutput_namedlink(obj, tmp+15, points);
1915 if(config_opennewwindow)
1916 actions = action_GetUrl(0, url, "_parent");
1918 actions = action_GetUrl(0, url, "_this");
1919 actions = action_End(actions);
1921 drawlink(obj, actions, 0, points,0);
1923 void swfoutput_linktopage(struct swfoutput*obj, int page, swfcoord*points)
1925 swfoutput_internal*i = (swfoutput_internal*)obj->internal;
1933 actions = action_GotoFrame(0, page);
1934 actions = action_End(actions);
1936 drawlink(obj, actions, 0, points,0);
1939 /* Named Links (a.k.a. Acrobatmenu) are used to implement various gadgets
1940 of the viewer objects, like subtitles, index elements etc.
1942 void swfoutput_namedlink(struct swfoutput*obj, char*name, swfcoord*points)
1944 swfoutput_internal*i = (swfoutput_internal*)obj->internal;
1945 ActionTAG *actions1,*actions2;
1946 char*tmp = strdup(name);
1954 if(!strncmp(tmp, "call:", 5))
1956 char*x = strchr(&tmp[5], ':');
1958 actions1 = action_PushInt(0, 0); //number of parameters (0)
1959 actions1 = action_PushString(actions1, &tmp[5]); //function name
1960 actions1 = action_CallFunction(actions1);
1963 actions1 = action_PushString(0, x+1); //parameter
1964 actions1 = action_PushInt(actions1, 1); //number of parameters (1)
1965 actions1 = action_PushString(actions1, &tmp[5]); //function name
1966 actions1 = action_CallFunction(actions1);
1968 actions2 = action_End(0);
1973 actions1 = action_PushString(0, "/:subtitle");
1974 actions1 = action_PushString(actions1, name);
1975 actions1 = action_SetVariable(actions1);
1976 actions1 = action_End(actions1);
1978 actions2 = action_PushString(0, "/:subtitle");
1979 actions2 = action_PushString(actions2, "");
1980 actions2 = action_SetVariable(actions2);
1981 actions2 = action_End(actions2);
1984 drawlink(obj, actions1, actions2, points,mouseover);
1986 swf_ActionFree(actions1);
1987 swf_ActionFree(actions2);
1991 static void drawlink(struct swfoutput*obj, ActionTAG*actions1, ActionTAG*actions2, swfcoord*points, char mouseover)
1993 swfoutput_internal*i = (swfoutput_internal*)obj->internal;
1998 struct plotxy p1,p2,p3,p4;
2002 double xmax=xmin=points[0].x,ymax=ymin=points[0].y;
2006 int buttonid = ++i->currentswfid;
2009 if(points[t].x>xmax) xmax=points[t].x;
2010 if(points[t].y>ymax) ymax=points[t].y;
2011 if(points[t].x<xmin) xmin=points[t].x;
2012 if(points[t].y<ymin) ymin=points[t].y;
2015 p1.x=points[0].x; p1.y=points[0].y; p2.x=points[1].x; p2.y=points[1].y;
2016 p3.x=points[2].x; p3.y=points[2].y; p4.x=points[3].x; p4.y=points[3].y;
2018 /* the following code subtracts the upper left edge from all coordinates,
2019 and set's posx,posy so that ST_PLACEOBJECT is used with a matrix.
2020 Necessary for preprocessing with swfcombine. */
2021 posx = xmin; posy = ymin;
2022 p1.x-=posx;p2.x-=posx;p3.x-=posx;p4.x-=posx;
2023 p1.y-=posy;p2.y-=posy;p3.y-=posy;p4.y-=posy;
2024 xmin -= posx; ymin -= posy;
2025 xmax -= posx; ymax -= posy;
2028 myshapeid = ++i->currentswfid;
2029 i->tag = swf_InsertTag(i->tag,ST_DEFINESHAPE3);
2030 swf_ShapeNew(&i->shape);
2031 rgb.r = rgb.b = rgb.a = rgb.g = 0;
2032 fsid = swf_ShapeAddSolidFillStyle(i->shape,&rgb);
2033 swf_SetU16(i->tag, myshapeid);
2034 r.xmin = (int)(xmin*20);
2035 r.ymin = (int)(ymin*20);
2036 r.xmax = (int)(xmax*20);
2037 r.ymax = (int)(ymax*20);
2038 swf_SetRect(i->tag,&r);
2039 swf_SetShapeStyles(i->tag,i->shape);
2040 swf_ShapeCountBits(i->shape,NULL,NULL);
2041 swf_SetShapeBits(i->tag,i->shape);
2042 swf_ShapeSetAll(i->tag,i->shape,/*x*/0,/*y*/0,0,fsid,0);
2043 i->swflastx = i->swflasty = 0;
2044 moveto(obj, i->tag, p1);
2045 lineto(obj, i->tag, p2);
2046 lineto(obj, i->tag, p3);
2047 lineto(obj, i->tag, p4);
2048 lineto(obj, i->tag, p1);
2049 swf_ShapeSetEnd(i->tag);
2052 myshapeid2 = ++i->currentswfid;
2053 i->tag = swf_InsertTag(i->tag,ST_DEFINESHAPE3);
2054 swf_ShapeNew(&i->shape);
2055 rgb.r = rgb.b = rgb.a = rgb.g = 255;
2057 fsid = swf_ShapeAddSolidFillStyle(i->shape,&rgb);
2058 swf_SetU16(i->tag, myshapeid2);
2059 r.xmin = (int)(xmin*20);
2060 r.ymin = (int)(ymin*20);
2061 r.xmax = (int)(xmax*20);
2062 r.ymax = (int)(ymax*20);
2063 swf_SetRect(i->tag,&r);
2064 swf_SetShapeStyles(i->tag,i->shape);
2065 swf_ShapeCountBits(i->shape,NULL,NULL);
2066 swf_SetShapeBits(i->tag,i->shape);
2067 swf_ShapeSetAll(i->tag,i->shape,/*x*/0,/*y*/0,0,fsid,0);
2068 i->swflastx = i->swflasty = 0;
2069 moveto(obj, i->tag, p1);
2070 lineto(obj, i->tag, p2);
2071 lineto(obj, i->tag, p3);
2072 lineto(obj, i->tag, p4);
2073 lineto(obj, i->tag, p1);
2074 swf_ShapeSetEnd(i->tag);
2078 i->tag = swf_InsertTag(i->tag,ST_DEFINEBUTTON);
2079 swf_SetU16(i->tag,buttonid); //id
2080 swf_ButtonSetFlags(i->tag, 0); //menu=no
2081 swf_ButtonSetRecord(i->tag,0x01,myshapeid,i->depth,0,0);
2082 swf_ButtonSetRecord(i->tag,0x02,myshapeid2,i->depth,0,0);
2083 swf_ButtonSetRecord(i->tag,0x04,myshapeid2,i->depth,0,0);
2084 swf_ButtonSetRecord(i->tag,0x08,myshapeid,i->depth,0,0);
2085 swf_SetU8(i->tag,0);
2086 swf_ActionSet(i->tag,actions1);
2087 swf_SetU8(i->tag,0);
2091 i->tag = swf_InsertTag(i->tag,ST_DEFINEBUTTON2);
2092 swf_SetU16(i->tag,buttonid); //id
2093 swf_ButtonSetFlags(i->tag, 0); //menu=no
2094 swf_ButtonSetRecord(i->tag,0x01,myshapeid,i->depth,0,0);
2095 swf_ButtonSetRecord(i->tag,0x02,myshapeid2,i->depth,0,0);
2096 swf_ButtonSetRecord(i->tag,0x04,myshapeid2,i->depth,0,0);
2097 swf_ButtonSetRecord(i->tag,0x08,myshapeid,i->depth,0,0);
2098 swf_SetU8(i->tag,0); // end of button records
2099 swf_ButtonSetCondition(i->tag, BC_IDLE_OVERUP);
2100 swf_ActionSet(i->tag,actions1);
2102 swf_ButtonSetCondition(i->tag, BC_OVERUP_IDLE);
2103 swf_ActionSet(i->tag,actions2);
2104 swf_SetU8(i->tag,0);
2105 swf_ButtonPostProcess(i->tag, 2);
2107 swf_SetU8(i->tag,0);
2108 swf_ButtonPostProcess(i->tag, 1);
2112 i->tag = swf_InsertTag(i->tag,ST_PLACEOBJECT2);
2114 if(posx!=0 || posy!=0) {
2116 p.x = (int)(posx*20);
2117 p.y = (int)(posy*20);
2118 p = swf_TurnPoint(p, &i->page_matrix);
2123 swf_ObjectPlace(i->tag, buttonid, i->depth++,&m,0,0);
2126 swf_ObjectPlace(i->tag, buttonid, i->depth++,&i->page_matrix,0,0);
2130 static void drawimage(struct swfoutput*obj, int bitid, int sizex,int sizey,
2131 double x1,double y1,
2132 double x2,double y2,
2133 double x3,double y3,
2134 double x4,double y4)
2136 swfoutput_internal*i = (swfoutput_internal*)obj->internal;
2141 struct plotxy p1,p2,p3,p4;
2143 double xmax=x1,ymax=y1,xmin=x1,ymin=y1;
2144 if(x2>xmax) xmax=x2;
2145 if(y2>ymax) ymax=y2;
2146 if(x2<xmin) xmin=x2;
2147 if(y2<ymin) ymin=y2;
2148 if(x3>xmax) xmax=x3;
2149 if(y3>ymax) ymax=y3;
2150 if(x3<xmin) xmin=x3;
2151 if(y3<ymin) ymin=y3;
2152 if(x4>xmax) xmax=x4;
2153 if(y4>ymax) ymax=y4;
2154 if(x4<xmin) xmin=x4;
2155 if(y4<ymin) ymin=y4;
2161 {p1.x = (int)(p1.x*20)/20.0;
2162 p1.y = (int)(p1.y*20)/20.0;
2163 p2.x = (int)(p2.x*20)/20.0;
2164 p2.y = (int)(p2.y*20)/20.0;
2165 p3.x = (int)(p3.x*20)/20.0;
2166 p3.y = (int)(p3.y*20)/20.0;
2167 p4.x = (int)(p4.x*20)/20.0;
2168 p4.y = (int)(p4.y*20)/20.0;}
2171 m.sx = (int)(65536*20*(p4.x-p1.x)/sizex);
2172 m.r1 = -(int)(65536*20*(p4.y-p1.y)/sizex);
2173 m.r0 = (int)(65536*20*(p1.x-p2.x)/sizey);
2174 m.sy = -(int)(65536*20*(p1.y-p2.y)/sizey);
2176 m.tx = (int)(p1.x*20);
2177 m.ty = (int)(p1.y*20);
2180 myshapeid = ++i->currentswfid;
2181 i->tag = swf_InsertTag(i->tag,ST_DEFINESHAPE);
2183 swf_ShapeNew(&shape);
2184 //lsid = ShapeAddLineStyle(shape,linewidth,&obj->strokergb);
2185 //fsid = ShapeAddSolidFillStyle(shape,&obj->fillrgb);
2186 fsid = swf_ShapeAddBitmapFillStyle(shape,&m,bitid,1);
2187 swf_SetU16(i->tag, myshapeid);
2188 r.xmin = (int)(xmin*20);
2189 r.ymin = (int)(ymin*20);
2190 r.xmax = (int)(xmax*20);
2191 r.ymax = (int)(ymax*20);
2192 swf_SetRect(i->tag,&r);
2193 swf_SetShapeStyles(i->tag,shape);
2194 swf_ShapeCountBits(shape,NULL,NULL);
2195 swf_SetShapeBits(i->tag,shape);
2196 swf_ShapeSetAll(i->tag,shape,/*x*/0,/*y*/0,lsid,fsid,0);
2197 i->swflastx = i->swflasty = 0;
2198 moveto(obj, i->tag, p1);
2199 lineto(obj, i->tag, p2);
2200 lineto(obj, i->tag, p3);
2201 lineto(obj, i->tag, p4);
2202 lineto(obj, i->tag, p1);
2204 ShapeMoveTo (tag, shape, (int)(x1*20),(int)(y1*20));
2205 ShapeSetLine (tag, shape, (int)(x1*20);
2206 ShapeSetLine (tag, shape, x*20,0);
2207 ShapeSetLine (tag, shape, 0,-y*20);
2208 ShapeSetLine (tag, shape, -x*20,0);*/
2209 swf_ShapeSetEnd(i->tag);
2210 swf_ShapeFree(shape);
2213 i->tag = swf_InsertTag(i->tag,ST_PLACEOBJECT2);
2215 swf_ObjectPlace(i->tag,myshapeid,/*depth*/i->depth++,&i->page_matrix,NULL,NULL);
2218 int swfoutput_drawimagejpeg_old(struct swfoutput*obj, char*filename, int sizex,int sizey,
2219 double x1,double y1,
2220 double x2,double y2,
2221 double x3,double y3,
2222 double x4,double y4)
2224 swfoutput_internal*i = (swfoutput_internal*)obj->internal;
2231 int bitid = ++i->currentswfid;
2233 i->tag = swf_InsertTag(i->tag,ST_DEFINEBITSJPEG2);
2234 swf_SetU16(i->tag, bitid);
2235 if(swf_SetJPEGBits(i->tag, filename, config_jpegquality)<0) {
2236 swf_DeleteTag(i->tag);
2241 drawimage(obj, bitid, sizex, sizey, x1,y1,x2,y2,x3,y3,x4,y4);
2245 int swfoutput_drawimagejpeg(struct swfoutput*obj, RGBA*mem, int sizex,int sizey,
2246 double x1,double y1,
2247 double x2,double y2,
2248 double x3,double y3,
2249 double x4,double y4)
2251 swfoutput_internal*i = (swfoutput_internal*)obj->internal;
2260 int bitid = ++i->currentswfid;
2262 i->tag = swf_InsertTag(i->tag,ST_DEFINEBITSJPEG2);
2263 swf_SetU16(i->tag, bitid);
2264 swf_SetJPEGBits2(i->tag,sizex,sizey,mem,config_jpegquality);
2265 drawimage(obj, bitid, sizex, sizey, x1,y1,x2,y2,x3,y3,x4,y4);
2269 int swfoutput_drawimagelossless(struct swfoutput*obj, RGBA*mem, int sizex,int sizey,
2270 double x1,double y1,
2271 double x2,double y2,
2272 double x3,double y3,
2273 double x4,double y4)
2275 swfoutput_internal*i = (swfoutput_internal*)obj->internal;
2282 int bitid = ++i->currentswfid;
2284 i->tag = swf_InsertTag(i->tag,ST_DEFINEBITSLOSSLESS);
2285 swf_SetU16(i->tag, bitid);
2286 if(swf_SetLosslessBits(i->tag,sizex,sizey,mem, BMF_32BIT)<0) {
2287 swf_DeleteTag(i->tag);
2292 drawimage(obj, bitid, sizex, sizey, x1,y1,x2,y2,x3,y3,x4,y4);
2296 int swfoutput_drawimagelosslessN(struct swfoutput*obj, U8*mem, RGBA*pal, int sizex,int sizey,
2297 double x1,double y1,
2298 double x2,double y2,
2299 double x3,double y3,
2300 double x4,double y4, int n)
2302 swfoutput_internal*i = (swfoutput_internal*)obj->internal;
2312 /* SWF expects scanlines to be 4 byte aligned */
2315 mem2 = (U8*)malloc(BYTES_PER_SCANLINE(sizex)*sizey);
2317 for(y=0;y<sizey;y++)
2319 for(x=0;x<sizex;x++)
2320 *ptr++ = mem[y*sizex+x];
2321 ptr+= BYTES_PER_SCANLINE(sizex)-sizex;
2326 int bitid = ++i->currentswfid;
2328 i->tag = swf_InsertTag(i->tag,ST_DEFINEBITSLOSSLESS2);
2329 swf_SetU16(i->tag, bitid);
2330 if(swf_SetLosslessBitsIndexed(i->tag,sizex,sizey,mem, pal, n)<0) {
2331 swf_DeleteTag(i->tag);
2338 drawimage(obj, bitid, sizex, sizey, x1,y1,x2,y2,x3,y3,x4,y4);
2342 void swfoutput_drawimageagain(struct swfoutput*obj, int id, int sizex,int sizey,
2343 double x1,double y1,
2344 double x2,double y2,
2345 double x3,double y3,
2346 double x4,double y4)
2348 swfoutput_internal*i = (swfoutput_internal*)obj->internal;
2355 drawimage(obj, id, sizex, sizey, x1,y1,x2,y2,x3,y3,x4,y4);
2358 void swfoutput_setparameter(char*name, char*value)
2360 if(!strcmp(name, "drawonlyshapes")) {
2361 config_drawonlyshapes = atoi(value);
2362 } else if(!strcmp(name, "ignoredraworder")) {
2363 config_ignoredraworder = atoi(value);
2364 } else if(!strcmp(name, "filloverlap")) {
2365 config_filloverlap = atoi(value);
2366 } else if(!strcmp(name, "linksopennewwindow")) {
2367 config_opennewwindow = atoi(value);
2368 } else if(!strcmp(name, "opennewwindow")) {
2369 config_opennewwindow = atoi(value);
2370 } else if(!strcmp(name, "storeallcharacters")) {
2371 config_storeallcharacters = atoi(value);
2372 } else if(!strcmp(name, "enablezlib")) {
2373 config_enablezlib = atoi(value);
2374 } else if(!strcmp(name, "insertstop")) {
2375 config_insertstoptag = atoi(value);
2376 } else if(!strcmp(name, "protected")) {
2377 config_protect = atoi(value);
2378 } else if(!strcmp(name, "flashversion")) {
2379 config_flashversion = atoi(value);
2380 } else if(!strcmp(name, "minlinewidth")) {
2381 config_minlinewidth = atof(value);
2382 } else if(!strcmp(name, "jpegquality")) {
2383 int val = atoi(value);
2385 if(val>100) val=100;
2386 config_jpegquality = val;
2387 } else if(!strcmp(name, "splinequality")) {
2388 int v = atoi(value);
2389 v = 500-(v*5); // 100% = 0.25 pixel, 0% = 25 pixel
2391 config_splinemaxerror = v;
2392 } else if(!strcmp(name, "fontquality")) {
2393 int v = atoi(value);
2394 v = 500-(v*5); // 100% = 0.25 pixel, 0% = 25 pixel
2396 config_fontsplinemaxerror = v;
2398 fprintf(stderr, "unknown parameter: %s (=%s)\n", name, value);