added logging of glyph bounding boxes.
[swftools.git] / pdf2swf / swfoutput.cc
1 /* swfoutput.cc
2    Implements generation of swf files using the rfxswf lib. The routines
3    in this file are called from pdf2swf.
4
5    This file is part of swftools.
6
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.
11
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.
16
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 */
20
21 #include <stdlib.h>
22 #include <stdio.h>
23 #include <string.h>
24 #include "../config.h"
25 #include <fcntl.h>
26 #include <unistd.h>
27 #ifdef HAVE_ASSERT_H
28 #include <assert.h>
29 #else
30 #define assert(a)
31 #endif
32 #include <math.h>
33 #include "swfoutput.h"
34 #include "spline.h"
35 extern "C" {
36 #include "../lib/log.h"
37 #include "../lib/rfxswf.h"
38 }
39
40 int opennewwindow=0;
41 int ignoredraworder=0;
42 int drawonlyshapes=0;
43 int jpegquality=85;
44 int storeallcharacters=0;
45 int enablezlib=0;
46 int insertstoptag=0;
47 int flashversion=5;
48 int splinemaxerror=1;
49 int fontsplinemaxerror=1;
50
51 static char storefont = 0;
52 static int flag_protected = 0;
53
54 typedef unsigned char u8;
55 typedef unsigned short int u16;
56 typedef unsigned long int u32;
57
58 static int fi;
59 static char* filename = 0;
60 static SWF swf;
61 static TAG *tag;
62 static int currentswfid = 0;
63 static int depth = 1;
64 static int startdepth = 1;
65
66 static SHAPE* shape;
67 static int shapeid = -1;
68 static int textid = -1;
69
70 static int fillstyleid;
71 static int linestyleid;
72 static int swflastx=0;
73 static int swflasty=0;
74 static int lastwasfill = 0;
75 static int shapeisempty = 1;
76 static char fill = 0;
77 static int sizex;
78 static int sizey;
79 TAG* cliptags[128];
80 int clipshapes[128];
81 u32 clipdepths[128];
82 int clippos = 0;
83
84 int CHARMIDX = 0;
85 int CHARMIDY = 0;
86
87 char fillstylechanged = 0;
88
89 static void startshape(struct swfoutput* obj);
90 static void starttext(struct swfoutput* obj);
91 static void endshape();
92 static void endtext();
93
94 // matrix multiplication. changes p0
95 static void transform (plotxy*p0,struct swfmatrix*m)
96 {
97     double x,y;
98     x = m->m11*p0->x+m->m12*p0->y;
99     y = m->m21*p0->x+m->m22*p0->y;
100     p0->x = x + m->m13;
101     p0->y = y + m->m23;
102 }
103
104 // write a move-to command into the swf
105 static int moveto(TAG*tag, plotxy p0)
106 {
107     int rx = (int)(p0.x*20);
108     int ry = (int)(p0.y*20);
109     if(rx!=swflastx || ry!=swflasty || fillstylechanged) {
110       swf_ShapeSetMove (tag, shape, rx,ry);
111       fillstylechanged = 0;
112       swflastx=rx;
113       swflasty=ry;
114       return 1;
115     }
116     return 0;
117 }
118
119 // write a line-to command into the swf
120 static void lineto(TAG*tag, plotxy p0)
121 {
122     int px = (int)(p0.x*20);
123     int py = (int)(p0.y*20);
124     int rx = (px-swflastx);
125     int ry = (py-swflasty);
126     /* we can't skip this for rx=0,ry=0, those
127        are plots */
128     swf_ShapeSetLine (tag, shape, rx,ry);
129
130     //swf_ExpandRect3(boundingBox, px, py, linewidth);
131     //swf_ExpandRect3(boundingBox, swflastx, swflasty, linewidth);
132
133     shapeisempty = 0;
134     swflastx+=rx;
135     swflasty+=ry;
136 }
137
138 // write a spline-to command into the swf
139 static void splineto(TAG*tag, plotxy control,plotxy end)
140 {
141     int cx = ((int)(control.x*20)-swflastx);
142     int cy = ((int)(control.y*20)-swflasty);
143     swflastx += cx;
144     swflasty += cy;
145     int ex = ((int)(end.x*20)-swflastx);
146     int ey = ((int)(end.y*20)-swflasty);
147     swflastx += ex;
148     swflasty += ey;
149     if(cx || cy || ex || ey)
150         swf_ShapeSetCurve(tag, shape, cx,cy,ex,ey);
151     shapeisempty = 0;
152 }
153
154 /* write a line, given two points and the transformation
155    matrix. */
156 static void line(TAG*tag, plotxy p0, plotxy p1, struct swfmatrix*m)
157 {
158     transform(&p0,m);
159     transform(&p1,m);
160     moveto(tag, p0);
161     lineto(tag, p1);
162 }
163
164 /* write a cubic (!) spline. This involves calling the approximate()
165    function out of spline.cc to convert it to a quadratic spline.  */
166 static void spline(TAG*tag,plotxy p0,plotxy p1,plotxy p2,plotxy p3,struct swfmatrix*m)
167 {
168     double d;
169     struct qspline q[128];
170     int num;
171     int t;
172     transform(&p0,m);
173     transform(&p1,m);
174     transform(&p2,m);
175     transform(&p3,m);
176     cspline c;
177     c.start = p3;
178     c.control1 = p2;
179     c.control2 = p1;
180     c.end = p0;
181
182     if(storefont) {
183         /* fonts use a different approximation than shapes */
184         num = cspline_approximate(&c, q, fontsplinemaxerror/20.0, APPROXIMATE_RECURSIVE_BINARY);
185         //num = cspline_approximate(&c, q, 10.0, APPROXIMATE_INFLECTION);
186     } else {
187         num = cspline_approximate(&c, q,     splinemaxerror/20.0, APPROXIMATE_RECURSIVE_BINARY);
188     }
189     for(t=0;t<num;t++) {
190         if(!t) 
191             moveto(tag,q[t].start);
192         splineto(tag,q[t].control, q[t].end);
193     }
194 }
195
196 void resetdrawer()
197 {
198     swflastx = 0;
199     swflasty = 0;
200 }
201
202 static void stopFill()
203 {
204     if(lastwasfill)
205     {
206         swf_ShapeSetStyle(tag,shape,linestyleid,0x8000,0);
207         fillstylechanged = 1;
208         lastwasfill = 0;
209     }
210 }
211 static void startFill()
212 {
213     if(!lastwasfill)
214     {
215         swf_ShapeSetStyle(tag,shape,0x8000,fillstyleid,0);
216         fillstylechanged = 1;
217         lastwasfill = 1;
218     }
219 }
220
221 /* draw an outline. These are generated by pdf2swf and by t1lib
222    (representing characters). */
223 void drawpath(struct swfoutput*output, SWF_OUTLINE*outline, struct swfmatrix*m, int log)
224 {
225     if( tag->id != ST_DEFINESHAPE &&
226         tag->id != ST_DEFINESHAPE2 &&
227         tag->id != ST_DEFINESHAPE3)
228     {
229         msg("<error> internal error: drawpath needs a shape tag, not %d\n",tag->id);
230         exit(1);
231     }
232     double x=0,y=0;
233     double lastx=0,lasty=0;
234     double firstx=0,firsty=0;
235     int init=1;
236
237     while (outline)
238     {
239         x += (outline->dest.x/(float)0xffff);
240         y += (outline->dest.y/(float)0xffff);
241         if(outline->type == SWF_PATHTYPE_MOVE)
242         {
243             if(!init && fill && output->drawmode != DRAWMODE_EOFILL && !ignoredraworder) {
244                 /* drawmode=FILL (not EOFILL) means that
245                    seperate shapes do not cancel each other out.
246                    On SWF side, we need to start a new shape for each
247                    closed polygon, because SWF only knows EOFILL.
248                 */
249                 endshape();
250                 startshape(output);
251                 startFill();
252             }
253
254             if(((int)(lastx*20) != (int)(firstx*20) ||
255                 (int)(lasty*20) != (int)(firsty*20)) &&
256                      fill && !init)
257             {
258                 plotxy p0;
259                 plotxy p1;
260                 p0.x=lastx;
261                 p0.y=lasty;
262                 p1.x=firstx;
263                 p1.y=firsty;
264                 if(log) printf("fix: %f,%f -> %f,%f\n",p0.x,p0.y,p1.x,p1.y);
265                 line(tag, p0, p1, m);
266             }
267             firstx=x;
268             firsty=y;
269             init = 0;
270         }
271         else if(outline->type == SWF_PATHTYPE_LINE) 
272         {
273             plotxy p0;
274             plotxy p1;
275             p0.x=lastx;
276             p0.y=lasty;
277             p1.x=x;
278             p1.y=y;
279             if(log) printf("line: %f,%f -> %f,%f\n",p0.x,p0.y,p1.x,p1.y);
280             line(tag, p0,p1,m);
281         }
282         else if(outline->type == SWF_PATHTYPE_BEZIER)
283         {
284             plotxy p0;
285             plotxy p1;
286             plotxy p2;
287             plotxy p3;
288             SWF_BEZIERSEGMENT*o2 = (SWF_BEZIERSEGMENT*)outline;
289             p0.x=x; 
290             p0.y=y;
291             p1.x=o2->C.x/(float)0xffff+lastx;
292             p1.y=o2->C.y/(float)0xffff+lasty;
293             p2.x=o2->B.x/(float)0xffff+lastx;
294             p2.y=o2->B.y/(float)0xffff+lasty;
295             p3.x=lastx;
296             p3.y=lasty;
297             if(log) printf("spline: %f,%f -> %f,%f\n",p3.x,p3.y,p0.x,p0.y);
298             spline(tag,p0,p1,p2,p3,m);
299         } 
300         else {
301             msg("<error> drawpath: unknown outline type:%d\n", outline->type);
302         }
303         lastx=x;
304         lasty=y;
305         outline = outline->link;
306     }
307     if(((int)(lastx*20) != (int)(firstx*20) ||
308         (int)(lasty*20) != (int)(firsty*20)) &&
309              fill)
310     {
311         plotxy p0;
312         plotxy p1;
313         p0.x=lastx;
314         p0.y=lasty;
315         p1.x=firstx;
316         p1.y=firsty;
317         if(log) printf("fix: %f,%f -> %f,%f\n",p0.x,p0.y,p1.x,p1.y);
318         line(tag, p0, p1, m);
319     }
320 }
321
322 plotxy getPivot(SWF_OUTLINE*outline, int dir, double line_width, int end, int trytwo)
323 {
324     SWF_PATHPOINT next, next2;
325     double xv=0,yv=0, xv2=0, yv2=0;
326     plotxy p;
327     int two = 0;
328
329     if(!end) {
330         if(outline->type == SWF_PATHTYPE_LINE) {
331             next = outline->dest;
332         } else {
333             next = ((SWF_BEZIERSEGMENT*)outline)->B;
334             if(next.x==0 && next.y==0) {
335                 next = ((SWF_BEZIERSEGMENT*)outline)->C;
336             }
337             if(next.x==0 && next.y==0) {
338                 next = ((SWF_BEZIERSEGMENT*)outline)->dest;
339             }
340         }
341         next2 = next;
342         if(trytwo && outline->last && outline->last->type != SWF_PATHTYPE_MOVE) {
343             if(outline->type == SWF_PATHTYPE_LINE) {
344                 next2 = outline->last->dest;
345             } else {
346                 SWF_PATHPOINT c = ((SWF_BEZIERSEGMENT*)(outline->last))->C;
347                 SWF_PATHPOINT b = ((SWF_BEZIERSEGMENT*)(outline->last))->B;
348                 next2.x = outline->last->dest.x - c.x;
349                 next2.y = outline->last->dest.y - c.y;
350                 if(next2.x==0 && next2.y==0) {
351                     next2.x = outline->last->dest.x - b.x;
352                     next2.y = outline->last->dest.y - b.y;
353                 }
354                 if(next2.x==0 && next2.y==0) {
355                     next2.x = outline->last->dest.x;
356                     next2.y = outline->last->dest.y;
357                 }
358             }
359             two = 1;
360         }
361     } else {
362         if(outline->type == SWF_PATHTYPE_LINE) {
363             next = outline->dest;
364         } else {
365             SWF_PATHPOINT c = ((SWF_BEZIERSEGMENT*)outline)->C;
366             SWF_PATHPOINT b = ((SWF_BEZIERSEGMENT*)outline)->B;
367             next.x = outline->dest.x - c.x;
368             next.y = outline->dest.y - c.y;
369             if(next.x==0 && next.y==0) {
370                 next.x = outline->dest.x - b.x;
371                 next.y = outline->dest.y - b.y;
372             }
373             if(next.x==0 && next.y==0) {
374                 next.x = outline->dest.x;
375                 next.y = outline->dest.y;
376             }
377         }
378         next2 = next;
379         if(trytwo && outline->link && outline->link->type != SWF_PATHTYPE_MOVE) {
380             if(outline->type == SWF_PATHTYPE_LINE) {
381                 next2 = outline->link->dest;
382             } else {
383                 next2 = ((SWF_BEZIERSEGMENT*)(outline->link))->B;
384                 if(next2.x==0 && next2.y==0) {
385                     next2 = ((SWF_BEZIERSEGMENT*)outline->link)->C;
386                 }
387                 if(next2.x==0 && next2.y==0) {
388                     next2 = ((SWF_BEZIERSEGMENT*)outline->link)->dest;
389                 }
390             }
391             two = 1;
392         }
393     }
394
395     if(dir) {
396         xv =  next.y/(float)0xffff;
397         yv = -next.x/(float)0xffff;
398     } else {
399         xv = -next.y/(float)0xffff;
400         yv =  next.x/(float)0xffff;
401     }
402
403     double r = (line_width/2)/sqrt(xv*xv+yv*yv);
404     xv*=r;
405     yv*=r;
406
407     if(two) {
408         if(dir) {
409             xv2 =  next2.y/(float)0xffff;
410             yv2 = -next2.x/(float)0xffff;
411         } else {
412             xv2 = -next2.y/(float)0xffff;
413             yv2 =  next2.x/(float)0xffff;
414         }
415
416         double r2 = (line_width/2)/sqrt(xv2*xv2+yv2*yv2);
417         xv2*=r2;
418         yv2*=r2;
419         xv = (xv+xv2)/2;
420         yv = (yv+yv2)/2;
421         double r3 = (line_width/2)/sqrt(xv*xv+yv*yv);
422         xv *= r3;
423         yv *= r3;
424     }
425
426     p.x = xv;
427     p.y = yv;
428     return p;
429 }
430
431 void drawShortPath(struct swfoutput*output, double x, double y, struct swfmatrix* m, SWF_OUTLINE*outline)
432 {
433     double lastx=x, lasty=y;
434     while (outline && outline->type != SWF_PATHTYPE_MOVE)
435     {
436         x += (outline->dest.x/(float)0xffff);
437         y += (outline->dest.y/(float)0xffff);
438
439         if(outline->type == SWF_PATHTYPE_LINE)
440         {
441             plotxy p0, p1;
442             p0.x=lastx;
443             p0.y=lasty;
444             p1.x= x; 
445             p1.y= y;
446             line(tag, p0, p1, m);
447         }
448         else if(outline->type == SWF_PATHTYPE_BEZIER)
449         {
450             plotxy p0,p1,p2,p3;
451             SWF_BEZIERSEGMENT*o2 = (SWF_BEZIERSEGMENT*)outline;
452             p3.x=lastx;
453             p3.y=lasty;
454             p1.x=o2->C.x/(float)0xffff+lastx;
455             p1.y=o2->C.y/(float)0xffff+lasty;
456             p2.x=o2->B.x/(float)0xffff+lastx;
457             p2.y=o2->B.y/(float)0xffff+lasty;
458             p0.x=x; 
459             p0.y=y;
460             spline(tag,p0,p1,p2,p3,m);
461         } 
462         lastx=x;
463         lasty=y;
464         outline = outline->link;
465     }
466 }
467
468 void drawShortPathWithEnds(struct swfoutput*output, double x, double y, struct swfmatrix* m, SWF_OUTLINE*outline, int num, int line_cap, int line_join, double line_width)
469 {
470     plotxy d,d2;
471     int back = 0;
472
473     if(line_cap == LINE_CAP_BUTT || line_cap == LINE_CAP_SQUARE) {
474         endshape();
475         startshape(output);
476         SWF_OUTLINE *last, *tmp=outline;
477         plotxy s,e,p0,p1,p2,p3,m0,m1,m2,m3;
478         double x2 = x;
479         double y2 = y;
480         double lx=x,ly=y;
481         double ee = 1.0;
482         int nr;
483         while(tmp && tmp->type != SWF_PATHTYPE_MOVE) {
484             last = tmp;
485             lx += (tmp->dest.x/(float)0xffff);
486             ly += (tmp->dest.y/(float)0xffff);
487             tmp = tmp->link;
488         }
489         s = getPivot(outline, 0, line_width, 0, 0);
490         e = getPivot(last, 0, line_width, 1, 0);
491
492         if(line_cap == LINE_CAP_BUTT) {
493             /* make the clipping rectangle slighly bigger
494                than the line ending, so that it get's clipped
495                propertly */
496             //ee = 1.01;
497             ee=1.0;
498         }
499
500         p0.x = x2 + s.x*ee; 
501         p0.y = y2 + s.y*ee;
502         p1.x = x2 - s.x*ee; 
503         p1.y = y2 - s.y*ee;
504         p2.x = x2 - s.y - s.x*ee; 
505         p2.y = y2 + s.x - s.y*ee;
506         p3.x = x2 - s.y + s.x*ee; 
507         p3.y = y2 + s.x + s.y*ee;
508         m0.x = lx + e.x*ee; 
509         m0.y = ly + e.y*ee;
510         m1.x = lx - e.x*ee; 
511         m1.y = ly - e.y*ee;
512         m2.x = lx + e.y - e.x*ee; 
513         m2.y = ly - e.x - e.y*ee;
514         m3.x = lx + e.y + e.x*ee; 
515         m3.y = ly - e.x + e.y*ee;
516
517         for(nr=0;nr<2;nr++) {
518             int dir=0;
519             struct plotxy q0,q1,q2,q3,q4,q5;
520             startFill();
521             if(line_cap == LINE_CAP_BUTT) {
522                 if(dir) {
523                     q0.x = 0; q0.y = 0;
524                     q1.x = sizex; q1.y = 0;
525                     q2.x = sizex; q2.y = sizey;
526                     q3.x = 0; q3.y = sizey;
527                 } else {
528                     q0.x = sizex; q0.y = sizey;
529                     q1.x = 0; q1.y = sizey;
530                     q2.x = 0; q2.y = 0;
531                     q3.x = sizex; q3.y = 0;
532                 }
533                 q4.x = p0.x; 
534                 q4.y = p0.y;
535                 moveto(tag, q0);
536                 lineto(tag, q1);
537                 lineto(tag, q2);
538                 lineto(tag, q3);
539                 lineto(tag, q0);
540
541                 transform(&q4,m);
542                 lineto(tag, q4);
543             }
544
545             line(tag, p0, p1, m);
546             line(tag, p1, p2, m);
547             line(tag, p2, p3, m);
548             line(tag, p3, p0, m);
549
550             if(line_cap == LINE_CAP_BUTT) {
551                 lineto(tag, q0);
552                 swf_ShapeSetEnd(tag);
553                 tag = swf_InsertTag(tag, ST_PLACEOBJECT2);
554                 swf_ObjectPlaceClip(tag,shapeid,depth,NULL,NULL,NULL,depth+2-nr);
555                 depth++;
556                 shapeid = -1;
557                 startshape(output);
558             }
559             p0 = m0;
560             p1 = m1;
561             p2 = m2;
562             p3 = m3;
563         }
564
565         stopFill();
566     }
567
568     drawShortPath(output,x,y,m,outline);
569
570     if(line_cap == LINE_CAP_BUTT) {
571         endshape();
572         startshape(output);
573     }
574 }
575
576 void drawT1toRect(struct swfoutput*output, double x, double y, struct swfmatrix* m, SWF_OUTLINE*outline, int num, int line_cap, int line_join, double line_width)
577 {
578     plotxy d1,d2,p1,p2,p3,p4;
579
580     d1.x = (outline->dest.x/(float)0xffff);
581     d1.y = (outline->dest.y/(float)0xffff);
582     d2 = getPivot(outline, 0, line_width, 0, 0);
583
584     assert(line_cap != LINE_CAP_ROUND);
585     if(line_cap == LINE_CAP_SQUARE) {
586         x -= +d2.y;
587         y -= -d2.x;
588         d1.x += +2*d2.y;
589         d1.y += -2*d2.x;
590     }
591
592     p1.x = x + d2.x;
593     p1.y = y + d2.y;
594     p2.x = x + d2.x + d1.x;
595     p2.y = y + d2.y + d1.y;
596     p3.x = x - d2.x + d1.x;
597     p3.y = y - d2.y + d1.y;
598     p4.x = x - d2.x;
599     p4.y = y - d2.y;
600
601     line(tag, p1,p2, m);
602     line(tag, p2,p3, m);
603     line(tag, p3,p4, m);
604     line(tag, p4,p1, m);
605 }
606
607 void drawShortPathWithStraightEnds(struct swfoutput*output, double x, double y, struct swfmatrix* m, SWF_OUTLINE*outline, int num, int line_cap, int line_join, double line_width)
608 {
609     SWF_OUTLINE*tmp=outline;
610     double xx=x,yy=y;
611     int stop=0;
612     assert(shapeid>=0);
613
614     startFill();
615     drawT1toRect(output, x, y, m,outline, num, line_cap, line_join, line_width);
616
617     while(tmp->link && tmp->link->type!=SWF_PATHTYPE_MOVE) {
618         xx += (tmp->dest.x/(float)0xffff);
619         yy += (tmp->dest.y/(float)0xffff);
620         tmp = tmp->link;
621     }
622     
623     assert(tmp->type == SWF_PATHTYPE_LINE);
624     assert(outline->type == SWF_PATHTYPE_LINE);
625     
626     if(tmp!=outline) {
627    
628         if(outline->link == tmp) {
629             /* the two straight line segments (which are everything we
630                need to draw) are very likely to overlap. To avoid that
631                they cancel each other out at the end points, start a new
632                shape for the second one */
633             endshape();startshape(output);
634             startFill();
635         }
636
637         drawT1toRect(output, xx, yy, m, tmp, num, line_cap, line_join, line_width);
638
639         if(outline->link != tmp)
640         {
641             stopFill();stop=1;
642             int save= tmp->type;
643             tmp->type = SWF_PATHTYPE_MOVE;
644             x += (outline->dest.x/(float)0xffff);
645             y += (outline->dest.y/(float)0xffff);
646             outline = outline->link;
647             drawShortPath(output, x, y, m, outline);
648             tmp->type = save;
649         }
650     }
651     if(!stop)
652         stopFill();
653 }
654
655 static int t1len(SWF_OUTLINE*line)
656 {
657     int num=0;
658     while(line && line->type != SWF_PATHTYPE_MOVE) {
659         num++;
660         line = line->link;
661     }
662     return num;
663 }
664
665 static float t1linelen(SWF_OUTLINE*line)
666 {
667     float x,y;
668     x = (line->dest.x/(float)0xffff);
669     y = (line->dest.y/(float)0xffff);
670     return sqrt(x*x+y*y);
671 }
672
673 void drawpath2poly(struct swfoutput *output, SWF_OUTLINE*outline, struct swfmatrix*m, int log, int line_join, int line_cap, double line_width, double miter_limit)
674 {
675     if( tag->id != ST_DEFINESHAPE &&
676         tag->id != ST_DEFINESHAPE2 &&
677         tag->id != ST_DEFINESHAPE3) {
678         msg("<error> internal error: drawpath needs a shape tag, not %d\n",tag->id);
679         exit(1);
680     }
681     assert(shapeid>=0);
682     double x=0,y=0;
683     double lastx=0,lasty=0;
684     int valid = 0;
685     int lastwasline = 0;
686     SWF_OUTLINE*tmp = outline, *last = 0;
687     tmp->last = 0;
688
689     while(1) {
690         if(tmp) {
691             x += (tmp->dest.x/(float)0xffff);
692             y += (tmp->dest.y/(float)0xffff);
693         }
694         if(!tmp || tmp->type == SWF_PATHTYPE_MOVE) {
695             if(valid && last) {
696                 if(last->type == SWF_PATHTYPE_LINE && t1linelen(last)>line_width*2 &&
697                    lastwasline && line_cap != LINE_CAP_ROUND)
698                     drawShortPathWithStraightEnds(output, lastx, lasty, m, last, valid, line_cap, line_join, line_width);
699                 else
700                     drawShortPathWithEnds(output, lastx, lasty, m, last, valid, line_cap, line_join, line_width);
701             }
702             if(!tmp)
703                 break;
704             valid = 0;
705             last = 0;
706             lastx = x;
707             lasty = y;
708         } else {
709             if(!last)
710                 last = tmp;
711             valid++;
712         }
713
714         if(tmp && tmp->type == SWF_PATHTYPE_LINE && t1linelen(tmp)>line_width*2)
715             lastwasline = 1;
716         else
717             lastwasline = 0;
718
719         if(tmp->link)
720             tmp->link->last = tmp; // make sure list is properly linked in both directions
721         tmp = tmp->link;
722     }
723 }
724
725 static inline int colorcompare(RGBA*a,RGBA*b)
726 {
727
728     if(a->r!=b->r ||
729        a->g!=b->g ||
730        a->b!=b->b ||
731        a->a!=b->a) {
732         return 0;
733     }
734     return 1;
735 }
736
737 static const int CHARDATAMAX = 8192;
738 struct chardata {
739     int charid;
740     int fontid;
741     int x;
742     int y;
743     int size;
744     RGBA color;
745 } chardata[CHARDATAMAX];
746 int chardatapos = 0;
747
748 static void putcharacters(TAG*tag)
749 {
750     int t;
751     SWFFONT font;
752     RGBA color;
753     color.r = chardata[0].color.r^255;
754     color.g = 0;
755     color.b = 0;
756     color.a = 0;
757     int lastfontid;
758     int lastx;
759     int lasty;
760     int lastsize;
761     int charids[128];
762     int charadvance[128];
763     int charstorepos;
764     int pass;
765     int glyphbits=1; //TODO: can this be zero?
766     int advancebits=1;
767
768     if(tag->id != ST_DEFINETEXT &&
769         tag->id != ST_DEFINETEXT2) {
770         msg("<error> internal error: putcharacters needs an text tag, not %d\n",tag->id);
771         exit(1);
772     }
773     if(!chardatapos) {
774         msg("<warning> putcharacters called with zero characters");
775     }
776
777     for(pass = 0; pass < 2; pass++)
778     {
779         charstorepos = 0;
780         lastfontid = -1;
781         lastx = CHARMIDX;
782         lasty = CHARMIDY;
783         lastsize = -1;
784
785         if(pass==1)
786         {
787             advancebits++; // add sign bit
788             swf_SetU8(tag, glyphbits);
789             swf_SetU8(tag, advancebits);
790         }
791
792         for(t=0;t<=chardatapos;t++)
793         {
794             if(lastfontid != chardata[t].fontid || 
795                     lastx!=chardata[t].x ||
796                     lasty!=chardata[t].y ||
797                     !colorcompare(&color, &chardata[t].color) ||
798                     charstorepos==127 ||
799                     lastsize != chardata[t].size ||
800                     t == chardatapos)
801             {
802                 if(charstorepos && pass==0)
803                 {
804                     int s;
805                     for(s=0;s<charstorepos;s++)
806                     {
807                         while(charids[s]>=(1<<glyphbits))
808                             glyphbits++;
809                         while(charadvance[s]>=(1<<advancebits))
810                             advancebits++;
811                     }
812                 }
813                 if(charstorepos && pass==1)
814                 {
815                     tag->writeBit = 0; // Q&D
816                     swf_SetBits(tag, 0, 1); // GLYPH Record
817                     swf_SetBits(tag, charstorepos, 7); // number of glyphs
818                     int s;
819                     for(s=0;s<charstorepos;s++)
820                     {
821                         swf_SetBits(tag, charids[s], glyphbits);
822                         swf_SetBits(tag, charadvance[s], advancebits);
823                     }
824                 }
825                 charstorepos = 0;
826
827                 if(pass == 1 && t<chardatapos)
828                 {
829                     RGBA*newcolor=0;
830                     SWFFONT*newfont=0;
831                     int newx = 0;
832                     int newy = 0;
833                     if(lastx != chardata[t].x ||
834                        lasty != chardata[t].y)
835                     {
836                         newx = chardata[t].x;
837                         newy = chardata[t].y;
838                         if(newx == 0)
839                             newx = SET_TO_ZERO;
840                         if(newy == 0)
841                             newy = SET_TO_ZERO;
842                     }
843                     if(!colorcompare(&color, &chardata[t].color)) 
844                     {
845                         color = chardata[t].color;
846                         newcolor = &color;
847                     }
848                     font.id = chardata[t].fontid;
849                     if(lastfontid != chardata[t].fontid || lastsize != chardata[t].size)
850                         newfont = &font;
851
852                     tag->writeBit = 0; // Q&D
853                     swf_TextSetInfoRecord(tag, newfont, chardata[t].size, newcolor, newx,newy);
854                 }
855
856                 lastfontid = chardata[t].fontid;
857                 lastx = chardata[t].x;
858                 lasty = chardata[t].y;
859                 lastsize = chardata[t].size;
860             }
861
862             if(t==chardatapos)
863                     break;
864
865             int advance;
866             int nextt = t==chardatapos-1?t:t+1;
867             int rel = chardata[nextt].x-chardata[t].x;
868             if(rel>=0 && (rel<(1<<(advancebits-1)) || pass==0)) {
869                advance = rel;
870                lastx=chardata[nextt].x;
871             }
872             else {
873                advance = 0;
874                lastx=chardata[t].x;
875             }
876             charids[charstorepos] = chardata[t].charid;
877             charadvance[charstorepos] = advance;
878             charstorepos ++;
879         }
880     }
881     chardatapos = 0;
882 }
883
884 static void putcharacter(struct swfoutput*obj, int fontid, int charid, 
885                     int x,int y, int size)
886 {
887     if(chardatapos == CHARDATAMAX)
888     {
889         msg("<warning> Character buffer too small. SWF will be slightly bigger");
890         endtext();
891         starttext(obj);
892     }
893     chardata[chardatapos].fontid = fontid;
894     chardata[chardatapos].charid = charid;
895     chardata[chardatapos].x = x;
896     chardata[chardatapos].y = y;
897     chardata[chardatapos].color = obj->fillrgb;
898     chardata[chardatapos].size = size;
899     chardatapos++;
900 }
901
902 struct fontlist_t 
903 {
904     SWFFONT *swffont;
905     fontlist_t*next;
906 } *fontlist = 0;
907
908 /* process a character. */
909 static int drawchar(struct swfoutput*obj, SWFFONT *swffont, char*character, int charnr, int u, swfmatrix*m)
910 {
911     int usefonts=1;
912     if(m->m12!=0 || m->m21!=0)
913         usefonts=0;
914     if(m->m11 != m->m22)
915         usefonts=0;
916
917     if(!swffont) {
918         msg("<warning> Font is NULL");
919         return 0;
920     }
921
922     if(!usefonts) {
923         msg("<verbose> Non diagonal font matrix: %f %f", m->m11, m->m21);
924         msg("<verbose> |                         %f %f", m->m12, m->m22);
925     }
926
927     //if(usefonts && ! drawonlyshapes)
928     if (1)
929     {
930         int charid = getCharID(swffont, charnr, character, u); 
931         
932         if(charid<0) {
933             msg("<warning> Didn't find character '%s' (c=%d,u=%d) in current charset (%s, %d characters)", 
934                     FIXNULL(character),charnr, u, FIXNULL((char*)swffont->name), swffont->numchars);
935             /*fontlist_t*it = fontlist;
936             while(it) {
937                 msg("<warning> Font history: %s [%d]", it->swffont->name, getCharID(it->swffont, charnr, character, u));
938                 it = it->next;
939             }*/
940             return 0;
941         }
942
943         if(shapeid>=0)
944             endshape();
945         if(textid<0)
946             starttext(obj);
947
948         putcharacter(obj, swffont->id, charid,(int)(m->m13*20),(int)(m->m23*20),
949                 (int)(m->m11+0.5));
950         swf_FontUseGlyph(swffont, charid);
951         return 1;
952     }
953     /*else
954     {
955         SWF_OUTLINE*outline = font->getOutline(character, charnr);
956         char* charname = character;
957
958         if(!outline) {
959          msg("<warning> Didn't find character '%s' (%d) in current charset (%s)", 
960                  FIXNULL(character),charnr,FIXNULL(font->getName()));
961          return;
962         }
963         
964         swfmatrix m2=*m;    
965         m2.m11/=100;
966         m2.m21/=100;
967         m2.m12/=100;
968         m2.m22/=100;
969
970         if(textid>=0)
971             endtext();
972         if(shapeid<0)
973             startshape(obj);
974
975         startFill();
976
977         int lf = fill;
978         fill = 1;
979         drawpath(tag, outline, &m2, 0);
980         fill = lf;
981     }*/
982 }
983
984 /* draw a curved polygon. */
985 void swfoutput_drawpath(swfoutput*output, SWF_OUTLINE*outline, 
986                             struct swfmatrix*m)
987 {
988     if(textid>=0)
989         endtext();
990
991     /* Multiple polygons in one shape don't overlap correctly, 
992        so we better start a new shape here if the polygon is filled
993      */
994     if(shapeid>=0 && fill && !ignoredraworder) {
995         endshape();
996     }
997
998     if(shapeid<0)
999         startshape(output);
1000
1001     if(!fill)
1002         stopFill();
1003     else
1004         startFill();
1005
1006     drawpath(output, outline,m, 0); 
1007 }
1008
1009 void swfoutput_drawpath2poly(struct swfoutput*output, SWF_OUTLINE*outline, struct swfmatrix*m, int line_join, int line_cap, double line_width, double miter_limit)
1010 {
1011     if(textid>=0)
1012         endtext();
1013     if(shapeid>=0)
1014         endshape();
1015     assert(shapeid<0);
1016     startshape(output);
1017     stopFill();
1018
1019     drawpath2poly(output, outline, m, 0, line_join, line_cap, line_width, miter_limit); 
1020 }
1021
1022 int getCharID(SWFFONT *font, int charnr, char *charname, int u)
1023 {
1024     int t;
1025     if(charname) {
1026         for(t=0;t<font->numchars;t++) {
1027             if(font->glyphnames[t] && !strcmp(font->glyphnames[t],charname)) {
1028                 return t;
1029             }
1030         }
1031         /* if we didn't find the character, maybe
1032            we can find the capitalized version */
1033         for(t=0;t<font->numchars;t++) {
1034             if(font->glyphnames[t] && !strcasecmp(font->glyphnames[t],charname)) {
1035                 return t;
1036             }
1037         }
1038     }
1039
1040     if(u>0) {
1041         /* try to use the unicode id */
1042         if(u>=0 && u<font->maxascii && font->ascii2glyph[u]>=0) {
1043             return font->ascii2glyph[u];
1044         }
1045     }
1046
1047     if(charnr>=0 && charnr<font->numchars) {
1048         return charnr;
1049     }
1050
1051     /* the following is technically wrong, and only works if the font encoding
1052        is US-ASCII based. It's needed for fonts which return broken unicode
1053        indices */
1054 /*    if(charnr>=0 && charnr<font->maxascii && font->ascii2glyph[charnr]>=0) {
1055         return font->ascii2glyph[charnr];
1056     }*/
1057
1058     return -1;
1059 }
1060
1061
1062 /* set's the t1 font index of the font to use for swfoutput_drawchar(). */
1063 void swfoutput_setfont(struct swfoutput*obj, char*fontid, char*filename)
1064 {
1065     fontlist_t*last=0,*iterator;
1066     if(!fontid) {
1067         msg("<error> No fontid");
1068         return;
1069     }
1070
1071     if(obj->swffont && obj->swffont->name && !strcmp((char*)obj->swffont->name,fontid))
1072         return;
1073
1074     iterator = fontlist;
1075     while(iterator) {
1076         if(!strcmp((char*)iterator->swffont->name,fontid)) {
1077             obj->swffont = iterator->swffont; 
1078             return;
1079         }
1080         last = iterator;
1081         iterator = iterator->next;
1082     }
1083
1084     if(!filename) {
1085         msg("<error> No filename given for font- internal error?");
1086         return;
1087     }
1088
1089     swf_SetLoadFontParameters(0,/*skip unused*/0,/*full unicode*/1);
1090     SWFFONT*swffont = swf_LoadFont(filename);
1091
1092     if(swffont == 0) {
1093         msg("<warning> Couldn't load font %s (%s)", fontid, filename);
1094         swffont = swf_LoadFont(0);
1095     }
1096
1097     swf_FontSetID(swffont, ++currentswfid);
1098     
1099     if(screenloglevel >= LOGLEVEL_DEBUG)  {
1100         // print font information
1101         msg("<debug> Font %s (%s)",swffont->name, filename);
1102         msg("<debug> |   ID: %d", swffont->id);
1103         msg("<debug> |   Version: %d", swffont->version);
1104         msg("<debug> |   Name: %s", fontid);
1105         msg("<debug> |   Numchars: %d", swffont->numchars);
1106         msg("<debug> |   Maxascii: %d", swffont->maxascii);
1107         msg("<debug> |   Style: %d", swffont->style);
1108         msg("<debug> |   Encoding: %d", swffont->encoding);
1109         for(int iii=0; iii<swffont->numchars;iii++) {
1110             msg("<debug> |   Glyph %d) name=%s, unicode=%d size=%d bbox=(%.2f,%.2f,%.2f,%.2f)\n", iii, swffont->glyphnames[iii], swffont->glyph2ascii[iii], swffont->glyph[iii].shape->bitlen, 
1111                     swffont->layout->bounds[iii].xmin/20.0,
1112                     swffont->layout->bounds[iii].ymin/20.0,
1113                     swffont->layout->bounds[iii].xmax/20.0,
1114                     swffont->layout->bounds[iii].ymax/20.0
1115                     );
1116         }
1117     }
1118
1119     /* set the font name to the ID we use here */
1120     if(swffont->name) free(swffont->name);
1121     swffont->name = (U8*)strdup(fontid);
1122
1123     iterator = new fontlist_t;
1124     iterator->swffont = swffont;
1125     iterator->next = 0;
1126
1127     if(last) 
1128         last->next = iterator;
1129     else 
1130         fontlist = iterator;
1131
1132     obj->swffont = swffont; 
1133 }
1134
1135 int swfoutput_queryfont(struct swfoutput*obj, char*fontid)
1136 {
1137     fontlist_t *iterator = fontlist;
1138     while(iterator) {
1139         if(!strcmp((char*)iterator->swffont->name,fontid))
1140             return 1;
1141         iterator = iterator->next;
1142     }
1143     return 0;
1144 }
1145
1146 /* set's the matrix which is to be applied to characters drawn by
1147    swfoutput_drawchar() */
1148 void swfoutput_setfontmatrix(struct swfoutput*obj,double m11,double m12,
1149                                                   double m21,double m22)
1150 {
1151     if(obj->fontm11 == m11 &&
1152        obj->fontm12 == m12 &&
1153        obj->fontm21 == m21 &&
1154        obj->fontm22 == m22)
1155         return;
1156 //    if(textid>=0)
1157 //      endtext();
1158     obj->fontm11 = m11;
1159     obj->fontm12 = m12;
1160     obj->fontm21 = m21;
1161     obj->fontm22 = m22;
1162 }
1163
1164 /* draws a character at x,y. */
1165 int swfoutput_drawchar(struct swfoutput* obj,double x,double y,char*character, int charnr, int u) 
1166 {
1167     swfmatrix m;
1168     m.m11 = obj->fontm11;
1169     m.m12 = obj->fontm12;
1170     m.m21 = obj->fontm21;
1171     m.m22 = obj->fontm22;
1172     m.m13 = x;
1173     m.m23 = y;
1174     return drawchar(obj, obj->swffont, character, charnr, u, &m);
1175 }
1176
1177 /* initialize the swf writer */
1178 void swfoutput_init(struct swfoutput* obj, char*_filename, int x1, int y1, int x2, int y2)
1179 {
1180   RGBA rgb;
1181   SRECT r;
1182   memset(obj, 0, sizeof(struct swfoutput));
1183   filename = _filename;
1184   sizex = x2;
1185   sizey = y2;
1186
1187   msg("<verbose> initializing swf output for size %d*%d\n", sizex,sizey);
1188
1189   obj->swffont = 0;
1190   obj->drawmode = -1;
1191   
1192   memset(&swf,0x00,sizeof(SWF));
1193
1194   swf.fileVersion    = flashversion;
1195   swf.frameRate      = 0x0040; // 1 frame per 4 seconds
1196   swf.movieSize.xmin = 20*x1;
1197   swf.movieSize.ymin = 20*y1;
1198   swf.movieSize.xmax = 20*x2;
1199   swf.movieSize.ymax = 20*y2;
1200   
1201   depth = 1;
1202   
1203   swf.firstTag = swf_InsertTag(NULL,ST_SETBACKGROUNDCOLOR);
1204   tag = swf.firstTag;
1205   rgb.a = rgb.r = rgb.g = rgb.b = 0xff;
1206   swf_SetRGB(tag,&rgb);
1207
1208   if(1)/* add white rectangle */
1209   {
1210       SRECT r;
1211       SHAPE* s;
1212       int ls1=0,fs1=0;
1213       int shapeid = ++currentswfid;
1214       r.xmin = x1*20;
1215       r.ymin = y1*20;
1216       r.xmax = x2*20;
1217       r.ymax = y2*20;
1218       tag = swf_InsertTag(tag, ST_DEFINESHAPE);
1219       swf_ShapeNew(&s);
1220       fs1 = swf_ShapeAddSolidFillStyle(s, &rgb);
1221       swf_SetU16(tag,shapeid);
1222       swf_SetRect(tag,&r);
1223       swf_SetShapeHeader(tag,s);
1224       swf_ShapeSetAll(tag,s,x1*20,y1*20,ls1,fs1,0);
1225       swf_ShapeSetLine(tag,s,20*(x2-x1),0);
1226       swf_ShapeSetLine(tag,s,0,20*(y2-y1));
1227       swf_ShapeSetLine(tag,s,20*(x1-x2),0);
1228       swf_ShapeSetLine(tag,s,0,20*(y1-y2));
1229       swf_ShapeSetEnd(tag);
1230       swf_ShapeFree(s);
1231       tag = swf_InsertTag(tag, ST_PLACEOBJECT2);
1232       swf_ObjectPlace(tag,shapeid,depth++,0,0,0);
1233       tag = swf_InsertTag(tag, ST_PLACEOBJECT2);
1234       swf_ObjectPlaceClip(tag,shapeid,depth++,0,0,0,65535);
1235   }
1236
1237   if(flag_protected)
1238     tag = swf_InsertTag(tag, ST_PROTECT);
1239   
1240   startdepth = depth;
1241 }
1242
1243 void swfoutput_setprotected() //write PROTECT tag
1244 {
1245   flag_protected = 1;
1246 }
1247
1248 static void startshape(struct swfoutput*obj)
1249 {
1250   RGBA rgb;
1251   SRECT r;
1252
1253   if(textid>=0)
1254       endtext();
1255
1256   tag = swf_InsertTag(tag,ST_DEFINESHAPE);
1257
1258   swf_ShapeNew(&shape);
1259   linestyleid = swf_ShapeAddLineStyle(shape,obj->linewidth,&obj->strokergb);
1260   rgb.r = obj->fillrgb.r;
1261   rgb.g = obj->fillrgb.g;
1262   rgb.b = obj->fillrgb.b;
1263   fillstyleid = swf_ShapeAddSolidFillStyle(shape,&obj->fillrgb);
1264
1265   shapeid = ++currentswfid;
1266   swf_SetU16(tag,shapeid);  // ID
1267
1268   r.xmin = 0;
1269   r.ymin = 0;
1270   r.xmax = 20*sizex;
1271   r.ymax = 20*sizey;
1272   
1273   swf_SetRect(tag,&r);
1274
1275   swf_SetShapeStyles(tag,shape);
1276   swf_ShapeCountBits(shape,NULL,NULL);
1277   swf_SetShapeBits(tag,shape);
1278
1279   swf_ShapeSetAll(tag,shape,/*x*/0,/*y*/0,linestyleid,0,0);
1280   swflastx=swflasty=0;
1281   lastwasfill = 0;
1282   shapeisempty = 1;
1283 }
1284
1285 static void starttext(struct swfoutput*obj)
1286 {
1287   SRECT r;
1288   MATRIX m;
1289   if(shapeid>=0)
1290       endshape();
1291   tag = swf_InsertTag(tag,ST_DEFINETEXT);
1292   textid = ++currentswfid;
1293   swf_SetU16(tag, textid);
1294
1295   r.xmin = 0;
1296   r.ymin = 0;
1297   r.xmax = 20*sizex;
1298   r.ymax = 20*sizey;
1299   
1300   swf_SetRect(tag,&r);
1301
1302   m.sx = 65536;
1303   m.sy = 65536;
1304   m.r0 = 0;
1305   m.r1 = 0;
1306   m.tx = 0;
1307   m.ty = 0;
1308  
1309   swf_SetMatrix(tag,&m);
1310   swflastx=swflasty=0;
1311 }
1312
1313 static void endshape()
1314 {
1315     if(shapeid<0) 
1316         return;
1317     swf_ShapeSetEnd(tag);
1318
1319     if(shapeisempty) {
1320         // delete the tag again, we didn't do anything
1321         TAG*todel = tag;
1322         tag = tag->prev;
1323         swf_DeleteTag(todel);
1324     } else {
1325         /* TODO: fix bounding box */
1326         tag = swf_InsertTag(tag,ST_PLACEOBJECT2);
1327         swf_ObjectPlace(tag,shapeid,/*depth*/depth++,NULL,NULL,NULL);
1328     }
1329     shapeid = -1;
1330 }
1331
1332 static void endtext()
1333 {
1334     if(textid<0)
1335         return;
1336     putcharacters(tag);
1337     swf_SetU8(tag,0);
1338     tag = swf_InsertTag(tag,ST_PLACEOBJECT2);
1339     swf_ObjectPlace(tag,textid,/*depth*/depth++,NULL,NULL,NULL);
1340     textid = -1;
1341 }
1342
1343 static void endpage(struct swfoutput*obj)
1344 {
1345     if(shapeid>=0)
1346       endshape();
1347     if(textid>=0)
1348       endtext();
1349     while(clippos)
1350         swfoutput_endclip(obj);
1351
1352     if(insertstoptag) {
1353         ActionTAG*atag=0;
1354         atag = action_Stop(atag);
1355         atag = action_End(atag);
1356         tag = swf_InsertTag(tag,ST_DOACTION);
1357         swf_ActionSet(tag,atag);
1358     }
1359     tag = swf_InsertTag(tag,ST_SHOWFRAME);
1360 }
1361
1362 void swfoutput_newpage(struct swfoutput*obj)
1363 {
1364     endpage(obj);
1365
1366     for(depth--;depth>=startdepth;depth--) {
1367         tag = swf_InsertTag(tag,ST_REMOVEOBJECT2);
1368         swf_SetU16(tag,depth);
1369     }
1370
1371     depth = startdepth;
1372 }
1373
1374 /* Perform cleaning up, complete the swf, and write it out. */
1375 void swfoutput_destroy(struct swfoutput* obj) 
1376 {
1377     endpage(obj);
1378     fontlist_t *tmp,*iterator = fontlist;
1379     while(iterator) {
1380         TAG*mtag = swf.firstTag;
1381         if(iterator->swffont) {
1382             mtag = swf_InsertTag(mtag, ST_DEFINEFONT2);
1383             /*if(!storeallcharacters)
1384                 swf_FontReduce(iterator->swffont);*/
1385             swf_FontSetDefine2(mtag, iterator->swffont);
1386             swf_FontFree(iterator->swffont);
1387         }
1388
1389         tmp = iterator;
1390         iterator = iterator->next;
1391         delete tmp;
1392     }
1393
1394     if(!filename) 
1395         return;
1396     if(filename)
1397      fi = open(filename, O_BINARY|O_CREAT|O_TRUNC|O_WRONLY, 0777);
1398     else
1399      fi = 1; // stdout
1400     
1401     if(fi<=0) {
1402      msg("<fatal> Could not create \"%s\". ", FIXNULL(filename));
1403      exit(1);
1404     }
1405  
1406     tag = swf_InsertTag(tag,ST_END);
1407
1408     if(enablezlib || flashversion>=6) {
1409       if FAILED(swf_WriteSWC(fi,&swf)) 
1410        msg("<error> WriteSWC() failed.\n");
1411     } else {
1412       if FAILED(swf_WriteSWF(fi,&swf)) 
1413        msg("<error> WriteSWF() failed.\n");
1414     }
1415
1416     if(filename)
1417      close(fi);
1418     msg("<notice> SWF written\n");
1419 }
1420
1421 void swfoutput_setdrawmode(swfoutput* obj, int mode)
1422 {
1423     obj->drawmode = mode;
1424     if(mode == DRAWMODE_FILL)
1425      fill = 1;
1426     else if(mode == DRAWMODE_EOFILL)
1427      fill = 1;
1428     else if(mode == DRAWMODE_STROKE)
1429      fill = 0;
1430     else if(mode == DRAWMODE_CLIP)
1431      fill = 1;
1432     else if(mode == DRAWMODE_EOCLIP)
1433      fill = 1;
1434 }
1435
1436 void swfoutput_setfillcolor(swfoutput* obj, u8 r, u8 g, u8 b, u8 a)
1437 {
1438     if(obj->fillrgb.r == r &&
1439        obj->fillrgb.g == g &&
1440        obj->fillrgb.b == b &&
1441        obj->fillrgb.a == a) return;
1442     if(shapeid>=0)
1443      endshape();
1444
1445     obj->fillrgb.r = r;
1446     obj->fillrgb.g = g;
1447     obj->fillrgb.b = b;
1448     obj->fillrgb.a = a;
1449 }
1450
1451 void swfoutput_setstrokecolor(swfoutput* obj, u8 r, u8 g, u8 b, u8 a)
1452 {
1453     if(obj->strokergb.r == r &&
1454        obj->strokergb.g == g &&
1455        obj->strokergb.b == b &&
1456        obj->strokergb.a == a) return;
1457
1458     if(shapeid>=0)
1459      endshape();
1460     obj->strokergb.r = r;
1461     obj->strokergb.g = g;
1462     obj->strokergb.b = b;
1463     obj->strokergb.a = a;
1464 }
1465
1466 void swfoutput_setlinewidth(struct swfoutput*obj, double linewidth)
1467 {
1468     if(obj->linewidth == (u16)(linewidth*20))
1469         return;
1470
1471     if(shapeid>=0)
1472      endshape();
1473     obj->linewidth = (u16)(linewidth*20);
1474 }
1475
1476
1477 void swfoutput_startclip(swfoutput*obj, SWF_OUTLINE*outline, struct swfmatrix*m)
1478 {
1479     if(textid>=0)
1480      endtext();
1481     if(shapeid>=0)
1482      endshape();
1483
1484     if(clippos >= 127)
1485     {
1486         msg("<warning> Too many clip levels.");
1487         clippos --;
1488     } 
1489     
1490     startshape(obj);
1491     int olddrawmode = obj->drawmode;
1492     swfoutput_setdrawmode(obj, DRAWMODE_CLIP);
1493     swfoutput_drawpath(obj, outline, m);
1494     swf_ShapeSetEnd(tag);
1495     swfoutput_setdrawmode(obj, olddrawmode);
1496
1497     tag = swf_InsertTag(tag,ST_PLACEOBJECT2);
1498     cliptags[clippos] = tag;
1499     clipshapes[clippos] = shapeid;
1500     clipdepths[clippos] = depth++;
1501     clippos++;
1502     shapeid = -1;
1503 }
1504
1505 void swfoutput_endclip(swfoutput*obj)
1506 {
1507     if(textid>=0)
1508      endtext();
1509     if(shapeid>=0)
1510      endshape();
1511
1512     if(!clippos) {
1513         msg("<error> Invalid end of clipping region");
1514         return;
1515     }
1516     clippos--;
1517     swf_ObjectPlaceClip(cliptags[clippos],clipshapes[clippos],clipdepths[clippos],NULL,NULL,NULL,depth++);
1518 }
1519
1520 static void drawlink(struct swfoutput*obj, ActionTAG*,ActionTAG*, swfcoord*points, char mouseover);
1521
1522 void swfoutput_linktourl(struct swfoutput*obj, char*url, swfcoord*points)
1523 {
1524     ActionTAG* actions;
1525     if(!strncmp("http://pdf2swf:", url, 15)) {
1526      char*tmp = strdup(url);
1527      int l = strlen(tmp);
1528      if(tmp[l-1] == '/')
1529         tmp[l-1] = 0;
1530      swfoutput_namedlink(obj, tmp+15, points);
1531      free(tmp);
1532      return;
1533     }
1534     
1535     if(shapeid>=0)
1536      endshape();
1537     if(textid>=0)
1538      endtext();
1539     
1540     if(opennewwindow)
1541       actions = action_GetUrl(0, url, "_parent");
1542     else
1543       actions = action_GetUrl(0, url, "_this");
1544     actions = action_End(actions);
1545     
1546     drawlink(obj, actions, 0, points,0);
1547 }
1548 void swfoutput_linktopage(struct swfoutput*obj, int page, swfcoord*points)
1549 {
1550     ActionTAG* actions;
1551
1552     if(shapeid>=0)
1553      endshape();
1554     if(textid>=0)
1555      endtext();
1556    
1557       actions = action_GotoFrame(0, page);
1558       actions = action_End(actions);
1559
1560     drawlink(obj, actions, 0, points,0);
1561 }
1562
1563 /* Named Links (a.k.a. Acrobatmenu) are used to implement various gadgets
1564    of the viewer objects, like subtitles, index elements etc.
1565 */
1566 void swfoutput_namedlink(struct swfoutput*obj, char*name, swfcoord*points)
1567 {
1568     ActionTAG *actions1,*actions2;
1569     char*tmp = strdup(name);
1570     char mouseover = 1;
1571
1572     if(shapeid>=0)
1573      endshape();
1574     if(textid>=0)
1575      endtext();
1576
1577     if(!strncmp(tmp, "call:", 5))
1578     {
1579         char*x = strchr(&tmp[5], ':');
1580         if(!x) {
1581             actions1 = action_PushInt(0, 0); //number of parameters (0)
1582             actions1 = action_PushString(actions1, &tmp[5]); //function name
1583             actions1 = action_CallFunction(actions1);
1584         } else {
1585             *x = 0;
1586             actions1 = action_PushString(0, x+1); //parameter
1587             actions1 = action_PushInt(actions1, 1); //number of parameters (1)
1588             actions1 = action_PushString(actions1, &tmp[5]); //function name
1589             actions1 = action_CallFunction(actions1);
1590         }
1591         actions2 = action_End(0);
1592         mouseover = 0;
1593     }
1594     else
1595     {
1596         actions1 = action_PushString(0, "/:subtitle");
1597         actions1 = action_PushString(actions1, name);
1598         actions1 = action_SetVariable(actions1);
1599         actions1 = action_End(actions1);
1600
1601         actions2 = action_PushString(0, "/:subtitle");
1602         actions2 = action_PushString(actions2, "");
1603         actions2 = action_SetVariable(actions2);
1604         actions2 = action_End(actions2);
1605     }
1606
1607     drawlink(obj, actions1, actions2, points,mouseover);
1608
1609     swf_ActionFree(actions1);
1610     swf_ActionFree(actions2);
1611     free(tmp);
1612 }
1613
1614 static void drawlink(struct swfoutput*obj, ActionTAG*actions1, ActionTAG*actions2, swfcoord*points, char mouseover)
1615 {
1616     RGBA rgb;
1617     SRECT r;
1618     int lsid=0;
1619     int fsid;
1620     struct plotxy p1,p2,p3,p4;
1621     int myshapeid;
1622     int myshapeid2;
1623     double xmin,ymin;
1624     double xmax=xmin=points[0].x,ymax=ymin=points[0].y;
1625     double posx = 0;
1626     double posy = 0;
1627     int t;
1628     int buttonid = ++currentswfid;
1629     for(t=1;t<4;t++)
1630     {
1631         if(points[t].x>xmax) xmax=points[t].x;
1632         if(points[t].y>ymax) ymax=points[t].y;
1633         if(points[t].x<xmin) xmin=points[t].x;
1634         if(points[t].y<ymin) ymin=points[t].y;
1635     }
1636    
1637     p1.x=points[0].x; p1.y=points[0].y; p2.x=points[1].x; p2.y=points[1].y; 
1638     p3.x=points[2].x; p3.y=points[2].y; p4.x=points[3].x; p4.y=points[3].y;
1639    
1640     /* the following code subtracts the upper left edge from all coordinates,
1641        and set's posx,posy so that ST_PLACEOBJECT is used with a matrix.
1642        Necessary for preprocessing with swfcombine. */
1643     posx = xmin; posy = ymin;
1644     p1.x-=posx;p2.x-=posx;p3.x-=posx;p4.x-=posx;
1645     p1.y-=posy;p2.y-=posy;p3.y-=posy;p4.y-=posy;
1646     xmin -= posx; ymin -= posy;
1647     xmax -= posx; ymax -= posy;
1648     
1649     /* shape */
1650     myshapeid = ++currentswfid;
1651     tag = swf_InsertTag(tag,ST_DEFINESHAPE3);
1652     swf_ShapeNew(&shape);
1653     rgb.r = rgb.b = rgb.a = rgb.g = 0; 
1654     fsid = swf_ShapeAddSolidFillStyle(shape,&rgb);
1655     swf_SetU16(tag, myshapeid);
1656     r.xmin = (int)(xmin*20);
1657     r.ymin = (int)(ymin*20);
1658     r.xmax = (int)(xmax*20);
1659     r.ymax = (int)(ymax*20);
1660     swf_SetRect(tag,&r);
1661     swf_SetShapeStyles(tag,shape);
1662     swf_ShapeCountBits(shape,NULL,NULL);
1663     swf_SetShapeBits(tag,shape);
1664     swf_ShapeSetAll(tag,shape,/*x*/0,/*y*/0,0,fsid,0);
1665     swflastx = swflasty = 0;
1666     moveto(tag, p1);
1667     lineto(tag, p2);
1668     lineto(tag, p3);
1669     lineto(tag, p4);
1670     lineto(tag, p1);
1671     swf_ShapeSetEnd(tag);
1672
1673     /* shape2 */
1674     myshapeid2 = ++currentswfid;
1675     tag = swf_InsertTag(tag,ST_DEFINESHAPE3);
1676     swf_ShapeNew(&shape);
1677     rgb.r = rgb.b = rgb.a = rgb.g = 255;
1678     rgb.a = 40;
1679     fsid = swf_ShapeAddSolidFillStyle(shape,&rgb);
1680     swf_SetU16(tag, myshapeid2);
1681     r.xmin = (int)(xmin*20);
1682     r.ymin = (int)(ymin*20);
1683     r.xmax = (int)(xmax*20);
1684     r.ymax = (int)(ymax*20);
1685     swf_SetRect(tag,&r);
1686     swf_SetShapeStyles(tag,shape);
1687     swf_ShapeCountBits(shape,NULL,NULL);
1688     swf_SetShapeBits(tag,shape);
1689     swf_ShapeSetAll(tag,shape,/*x*/0,/*y*/0,0,fsid,0);
1690     swflastx = swflasty = 0;
1691     moveto(tag, p1);
1692     lineto(tag, p2);
1693     lineto(tag, p3);
1694     lineto(tag, p4);
1695     lineto(tag, p1);
1696     swf_ShapeSetEnd(tag);
1697
1698     if(!mouseover)
1699     {
1700         tag = swf_InsertTag(tag,ST_DEFINEBUTTON);
1701         swf_SetU16(tag,buttonid); //id
1702         swf_ButtonSetFlags(tag, 0); //menu=no
1703         swf_ButtonSetRecord(tag,0x01,myshapeid,depth,0,0);
1704         swf_ButtonSetRecord(tag,0x02,myshapeid2,depth,0,0);
1705         swf_ButtonSetRecord(tag,0x04,myshapeid2,depth,0,0);
1706         swf_ButtonSetRecord(tag,0x08,myshapeid,depth,0,0);
1707         swf_SetU8(tag,0);
1708         swf_ActionSet(tag,actions1);
1709         swf_SetU8(tag,0);
1710     }
1711     else
1712     {
1713         tag = swf_InsertTag(tag,ST_DEFINEBUTTON2);
1714         swf_SetU16(tag,buttonid); //id
1715         swf_ButtonSetFlags(tag, 0); //menu=no
1716         swf_ButtonSetRecord(tag,0x01,myshapeid,depth,0,0);
1717         swf_ButtonSetRecord(tag,0x02,myshapeid2,depth,0,0);
1718         swf_ButtonSetRecord(tag,0x04,myshapeid2,depth,0,0);
1719         swf_ButtonSetRecord(tag,0x08,myshapeid,depth,0,0);
1720         swf_SetU8(tag,0); // end of button records
1721         swf_ButtonSetCondition(tag, BC_IDLE_OVERUP);
1722         swf_ActionSet(tag,actions1);
1723         if(actions2) {
1724             swf_ButtonSetCondition(tag, BC_OVERUP_IDLE);
1725             swf_ActionSet(tag,actions2);
1726             swf_SetU8(tag,0);
1727             swf_ButtonPostProcess(tag, 2);
1728         } else {
1729             swf_SetU8(tag,0);
1730             swf_ButtonPostProcess(tag, 1);
1731         }
1732     }
1733     
1734     tag = swf_InsertTag(tag,ST_PLACEOBJECT2);
1735
1736     if(posx!=0 || posy!=0) {
1737         MATRIX m;
1738         swf_GetMatrix(0,&m);
1739         m.tx = (int)(posx*20);
1740         m.ty = (int)(posy*20);
1741         swf_ObjectPlace(tag, buttonid, depth++,&m,0,0);
1742     }
1743     else {
1744         swf_ObjectPlace(tag, buttonid, depth++,0,0,0);
1745     }
1746 }
1747
1748 static void drawimage(struct swfoutput*obj, int bitid, int sizex,int sizey, 
1749         double x1,double y1,
1750         double x2,double y2,
1751         double x3,double y3,
1752         double x4,double y4)
1753 {
1754     RGBA rgb;
1755     SRECT r;
1756     int lsid=0;
1757     int fsid;
1758     struct plotxy p1,p2,p3,p4;
1759     int myshapeid;
1760     double xmax=x1,ymax=y1,xmin=x1,ymin=y1;
1761     if(x2>xmax) xmax=x2;
1762     if(y2>ymax) ymax=y2;
1763     if(x2<xmin) xmin=x2;
1764     if(y2<ymin) ymin=y2;
1765     if(x3>xmax) xmax=x3;
1766     if(y3>ymax) ymax=y3;
1767     if(x3<xmin) xmin=x3;
1768     if(y3<ymin) ymin=y3;
1769     if(x4>xmax) xmax=x4;
1770     if(y4>ymax) ymax=y4;
1771     if(x4<xmin) xmin=x4;
1772     if(y4<ymin) ymin=y4;
1773     p1.x=x1; p1.y=y1;
1774     p2.x=x2; p2.y=y2;
1775     p3.x=x3; p3.y=y3;
1776     p4.x=x4; p4.y=y4;
1777
1778     {p1.x = (int)(p1.x*20)/20.0;
1779      p1.y = (int)(p1.y*20)/20.0;
1780      p2.x = (int)(p2.x*20)/20.0;
1781      p2.y = (int)(p2.y*20)/20.0;
1782      p3.x = (int)(p3.x*20)/20.0;
1783      p3.y = (int)(p3.y*20)/20.0;
1784      p4.x = (int)(p4.x*20)/20.0;
1785      p4.y = (int)(p4.y*20)/20.0;}
1786     
1787     MATRIX m;
1788     m.sx = (int)(65536*20*(p4.x-p1.x)/sizex);
1789     m.r1 = -(int)(65536*20*(p4.y-p1.y)/sizex);
1790     m.r0 = (int)(65536*20*(p1.x-p2.x)/sizey);
1791     m.sy = -(int)(65536*20*(p1.y-p2.y)/sizey);
1792
1793     m.tx = (int)(p1.x*20);
1794     m.ty = (int)(p1.y*20);
1795   
1796     /* shape */
1797     myshapeid = ++currentswfid;
1798     tag = swf_InsertTag(tag,ST_DEFINESHAPE);
1799     swf_ShapeNew(&shape);
1800     //lsid = ShapeAddLineStyle(shape,obj->linewidth,&obj->strokergb);
1801     //fsid = ShapeAddSolidFillStyle(shape,&obj->fillrgb);
1802     fsid = swf_ShapeAddBitmapFillStyle(shape,&m,bitid,1);
1803     swf_SetU16(tag, myshapeid);
1804     r.xmin = (int)(xmin*20);
1805     r.ymin = (int)(ymin*20);
1806     r.xmax = (int)(xmax*20);
1807     r.ymax = (int)(ymax*20);
1808     swf_SetRect(tag,&r);
1809     swf_SetShapeStyles(tag,shape);
1810     swf_ShapeCountBits(shape,NULL,NULL);
1811     swf_SetShapeBits(tag,shape);
1812     swf_ShapeSetAll(tag,shape,/*x*/0,/*y*/0,lsid,fsid,0);
1813     swflastx = swflasty = 0;
1814     moveto(tag, p1);
1815     lineto(tag, p2);
1816     lineto(tag, p3);
1817     lineto(tag, p4);
1818     lineto(tag, p1);
1819     /*
1820     ShapeMoveTo  (tag, shape, (int)(x1*20),(int)(y1*20));
1821     ShapeSetLine (tag, shape, (int)(x1*20);
1822     ShapeSetLine (tag, shape, x*20,0);
1823     ShapeSetLine (tag, shape, 0,-y*20);
1824     ShapeSetLine (tag, shape, -x*20,0);*/
1825     swf_ShapeSetEnd(tag);
1826
1827     /* instance */
1828     tag = swf_InsertTag(tag,ST_PLACEOBJECT2);
1829     swf_ObjectPlace(tag,myshapeid,/*depth*/depth++,NULL,NULL,NULL);
1830 }
1831
1832 int swfoutput_drawimagejpeg_old(struct swfoutput*obj, char*filename, int sizex,int sizey, 
1833         double x1,double y1,
1834         double x2,double y2,
1835         double x3,double y3,
1836         double x4,double y4)
1837 {
1838     TAG*oldtag;
1839     if(shapeid>=0)
1840      endshape();
1841     if(textid>=0)
1842      endtext();
1843
1844     int bitid = ++currentswfid;
1845     oldtag = tag;
1846     tag = swf_InsertTag(tag,ST_DEFINEBITSJPEG2);
1847     swf_SetU16(tag, bitid);
1848     if(swf_SetJPEGBits(tag, filename, jpegquality)<0) {
1849         swf_DeleteTag(tag);
1850         tag = oldtag;
1851         return -1;
1852     }
1853
1854     drawimage(obj, bitid, sizex, sizey, x1,y1,x2,y2,x3,y3,x4,y4);
1855     return bitid;
1856 }
1857
1858 int swfoutput_drawimagejpeg(struct swfoutput*obj, RGBA*mem, int sizex,int sizey, 
1859         double x1,double y1,
1860         double x2,double y2,
1861         double x3,double y3,
1862         double x4,double y4)
1863 {
1864     TAG*oldtag;
1865     JPEGBITS*jpeg;
1866
1867     if(shapeid>=0)
1868      endshape();
1869     if(textid>=0)
1870      endtext();
1871
1872     int bitid = ++currentswfid;
1873     oldtag = tag;
1874     tag = swf_InsertTag(tag,ST_DEFINEBITSJPEG2);
1875     swf_SetU16(tag, bitid);
1876     swf_SetJPEGBits2(tag,sizex,sizey,mem,jpegquality);
1877     drawimage(obj, bitid, sizex, sizey, x1,y1,x2,y2,x3,y3,x4,y4);
1878     return bitid;
1879 }
1880
1881 int swfoutput_drawimagelossless(struct swfoutput*obj, RGBA*mem, int sizex,int sizey, 
1882         double x1,double y1,
1883         double x2,double y2,
1884         double x3,double y3,
1885         double x4,double y4)
1886 {
1887     TAG*oldtag;
1888     if(shapeid>=0)
1889      endshape();
1890     if(textid>=0)
1891      endtext();
1892
1893     int bitid = ++currentswfid;
1894     oldtag = tag;
1895     tag = swf_InsertTag(tag,ST_DEFINEBITSLOSSLESS);
1896     swf_SetU16(tag, bitid);
1897     if(swf_SetLosslessBits(tag,sizex,sizey,mem, BMF_32BIT)<0) {
1898         swf_DeleteTag(tag);
1899         tag = oldtag;
1900         return -1;
1901     }
1902     
1903     drawimage(obj, bitid, sizex, sizey, x1,y1,x2,y2,x3,y3,x4,y4);
1904     return bitid;
1905 }
1906
1907 int swfoutput_drawimagelosslessN(struct swfoutput*obj, U8*mem, RGBA*pal, int sizex,int sizey, 
1908         double x1,double y1,
1909         double x2,double y2,
1910         double x3,double y3,
1911         double x4,double y4, int n)
1912 {
1913     TAG*oldtag;
1914     U8*mem2 = 0;
1915     if(shapeid>=0)
1916      endshape();
1917     if(textid>=0)
1918      endtext();
1919
1920     if(sizex&3)
1921     { 
1922         /* SWF expects scanlines to be 4 byte aligned */
1923         int x,y;
1924         U8*ptr;
1925         mem2 = (U8*)malloc(BYTES_PER_SCANLINE(sizex)*sizey);
1926         ptr = mem2;
1927         for(y=0;y<sizey;y++)
1928         {
1929             for(x=0;x<sizex;x++)
1930                 *ptr++ = mem[y*sizex+x];
1931             ptr+= BYTES_PER_SCANLINE(sizex)-sizex;
1932         }
1933         mem = mem2;
1934     }
1935
1936     int bitid = ++currentswfid;
1937     oldtag = tag;
1938     tag = swf_InsertTag(tag,ST_DEFINEBITSLOSSLESS2);
1939     swf_SetU16(tag, bitid);
1940     if(swf_SetLosslessBitsIndexed(tag,sizex,sizey,mem, pal, n)<0) {
1941         swf_DeleteTag(tag);
1942         tag = oldtag;
1943         return -1;
1944     }
1945     if(mem2)
1946         free(mem2);
1947   
1948     drawimage(obj, bitid, sizex, sizey, x1,y1,x2,y2,x3,y3,x4,y4);
1949     return bitid;
1950 }
1951
1952 void swfoutput_drawimageagain(struct swfoutput*obj, int id, int sizex,int sizey, 
1953         double x1,double y1,
1954         double x2,double y2,
1955         double x3,double y3,
1956         double x4,double y4)
1957 {
1958     if(id<0) return;
1959     if(shapeid>=0)
1960      endshape();
1961     if(textid>=0)
1962      endtext();
1963
1964     drawimage(obj, id, sizex, sizey, x1,y1,x2,y2,x3,y3,x4,y4);
1965 }
1966