7071724e95b957a1eb8e0d0985de25f45ffd1e04
[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 #define CHARDATAMAX 8192
41 #define CHARMIDX 0
42 #define CHARMIDY 0
43
44 typedef struct _chardata {
45     int charid;
46     int fontid; /* TODO: use a SWFFONT instead */
47     int x;
48     int y;
49     int size;
50     RGBA color;
51 } chardata_t;
52
53 struct fontlist_t 
54 {
55     SWFFONT *swffont;
56     fontlist_t*next;
57 };
58
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;
70 int config_protect=0;
71 float config_minlinewidth=0.05;
72
73 typedef struct _swfoutput_internal
74 {
75     SWF swf;
76
77     fontlist_t* fontlist;
78
79     char storefont;
80
81     MATRIX page_matrix;
82
83     TAG *tag;
84     int currentswfid;
85     int depth;
86     int startdepth;
87     int linewidth;
88     
89     SHAPE* shape;
90     int shapeid;
91     int textid;
92     
93     int fillstyleid;
94     int linestyleid;
95     int swflastx;
96     int swflasty;
97     int lastwasfill;
98     int shapeisempty;
99     char fill;
100     int min_x,max_x;
101     int min_y,max_y;
102     TAG* cliptags[128];
103     int clipshapes[128];
104     U32 clipdepths[128];
105     int clippos;
106
107     int frameno;
108     int lastframeno;
109     
110     char fillstylechanged;
111     
112     int bboxrectpos;
113     SRECT bboxrect;
114
115     TAG*cliptag;
116  
117     chardata_t chardata[CHARDATAMAX];
118     int chardatapos;
119     int firstpage;
120     char pagefinished;
121 } swfoutput_internal;
122
123 static swfoutput_internal* init_internal_struct()
124 {
125     swfoutput_internal*i = (swfoutput_internal*)malloc(sizeof(swfoutput_internal));
126     memset(i, 0, sizeof(swfoutput_internal));
127    
128
129     i->storefont = 0;
130     i->currentswfid = 0;
131     i->depth = 1;
132     i->startdepth = 1;
133     i->linewidth = 0;
134     i->shapeid = -1;
135     i->textid = -1;
136     i->frameno = 0;
137     i->lastframeno = 0;
138
139     i->fillstyleid;
140     i->linestyleid;
141     i->swflastx=0;
142     i->swflasty=0;
143     i->lastwasfill = 0;
144     i->shapeisempty = 1;
145     i->fill = 0;
146     i->clippos = 0;
147
148     i->fillstylechanged = 0;
149
150     i->bboxrectpos = -1;
151     i->chardatapos = 0;
152     i->firstpage = 1;
153     i->pagefinished = 1;
154
155     return i;
156 };
157
158 static void startshape(struct swfoutput* obj);
159 static void starttext(struct swfoutput* obj);
160 static void endshape(struct swfoutput* obj,int clip);
161 static void endtext(struct swfoutput* obj);
162
163 // matrix multiplication. changes p0
164 static void transform (plotxy*p0,struct swfmatrix*m)
165 {
166     double x,y;
167     x = m->m11*p0->x+m->m12*p0->y;
168     y = m->m21*p0->x+m->m22*p0->y;
169     p0->x = x + m->m13;
170     p0->y = y + m->m23;
171 }
172
173 // write a move-to command into the swf
174 static int moveto(struct swfoutput*obj, TAG*tag, plotxy p0)
175 {
176     swfoutput_internal*i = (swfoutput_internal*)obj->internal;
177     int rx = (int)(p0.x*20);
178     int ry = (int)(p0.y*20);
179     if(rx!=i->swflastx || ry!=i->swflasty || i->fillstylechanged) {
180       swf_ShapeSetMove (tag, i->shape, rx,ry);
181       i->fillstylechanged = 0;
182       i->swflastx=rx;
183       i->swflasty=ry;
184       return 1;
185     }
186     return 0;
187 }
188 static int moveto(struct swfoutput*obj, TAG*tag, float x, float y)
189 {
190     swfoutput_internal*i = (swfoutput_internal*)obj->internal;
191     plotxy p;
192     p.x = x;
193     p.y = y;
194     return moveto(obj, tag, p);
195 }
196 static void addPointToBBox(struct swfoutput*obj, int px, int py) 
197 {
198     swfoutput_internal*i = (swfoutput_internal*)obj->internal;
199
200     SPOINT p;
201     p.x = px;
202     p.y = py;
203     if(i->fill) {
204         swf_ExpandRect(&i->bboxrect, p);
205     } else {
206         swf_ExpandRect3(&i->bboxrect, p, i->linewidth*3/2);
207     }
208 }
209
210 // write a line-to command into the swf
211 static void lineto(struct swfoutput*obj, TAG*tag, plotxy p0)
212 {
213     swfoutput_internal*i = (swfoutput_internal*)obj->internal;
214     int px = (int)(p0.x*20);
215     int py = (int)(p0.y*20);
216     int rx = (px-i->swflastx);
217     int ry = (py-i->swflasty);
218     /* we can't skip this for rx=0,ry=0, those
219        are plots */
220     swf_ShapeSetLine (tag, i->shape, rx,ry);
221
222     addPointToBBox(obj, i->swflastx,i->swflasty);
223     addPointToBBox(obj, px,py);
224
225     i->shapeisempty = 0;
226     i->swflastx+=rx;
227     i->swflasty+=ry;
228 }
229 static void lineto(struct swfoutput*obj, TAG*tag, double x, double y)
230 {
231     plotxy p;
232     p.x = x;
233     p.y = y;
234     lineto(obj,tag, p);
235 }
236
237 // write a spline-to command into the swf
238 static void splineto(struct swfoutput*obj, TAG*tag, plotxy control,plotxy end)
239 {
240     swfoutput_internal*i = (swfoutput_internal*)obj->internal;
241     int lastlastx = i->swflastx;
242     int lastlasty = i->swflasty;
243
244     int cx = ((int)(control.x*20)-i->swflastx);
245     int cy = ((int)(control.y*20)-i->swflasty);
246     i->swflastx += cx;
247     i->swflasty += cy;
248     int ex = ((int)(end.x*20)-i->swflastx);
249     int ey = ((int)(end.y*20)-i->swflasty);
250     i->swflastx += ex;
251     i->swflasty += ey;
252     
253     if(cx || cy || ex || ey) {
254         swf_ShapeSetCurve(tag, i->shape, cx,cy,ex,ey);
255         addPointToBBox(obj, lastlastx   ,lastlasty   );
256         addPointToBBox(obj, lastlastx+cx,lastlasty+cy);
257         addPointToBBox(obj, lastlastx+cx+ex,lastlasty+cy+ey);
258     }
259     i->shapeisempty = 0;
260 }
261
262 /* write a line, given two points and the transformation
263    matrix. */
264 static void line(struct swfoutput*obj, TAG*tag, plotxy p0, plotxy p1, struct swfmatrix*m)
265 {
266     transform(&p0,m);
267     transform(&p1,m);
268     moveto(obj, tag, p0);
269     lineto(obj, tag, p1);
270 }
271
272 /* write a cubic (!) spline. This involves calling the approximate()
273    function out of spline.cc to convert it to a quadratic spline.  */
274 static void spline(struct swfoutput*obj, TAG*tag,plotxy p0,plotxy p1,plotxy p2,plotxy p3,struct swfmatrix*m)
275 {
276     swfoutput_internal*i = (swfoutput_internal*)obj->internal;
277     double d;
278     struct qspline q[128];
279     int num;
280     int t;
281     transform(&p0,m);
282     transform(&p1,m);
283     transform(&p2,m);
284     transform(&p3,m);
285     cspline c;
286     c.start = p3;
287     c.control1 = p2;
288     c.control2 = p1;
289     c.end = p0;
290
291     if(i->storefont) {
292         /* fonts use a different approximation than shapes */
293         num = cspline_approximate(&c, q, config_fontsplinemaxerror/20.0, APPROXIMATE_RECURSIVE_BINARY);
294         //num = cspline_approximate(&c, q, 10.0, APPROXIMATE_INFLECTION);
295     } else {
296         num = cspline_approximate(&c, q, config_splinemaxerror/20.0, APPROXIMATE_RECURSIVE_BINARY);
297     }
298     for(t=0;t<num;t++) {
299         if(!t) 
300             moveto(obj, tag,q[t].start);
301         splineto(obj, tag,q[t].control, q[t].end);
302     }
303 }
304
305 void resetdrawer(struct swfoutput*obj)
306 {
307     swfoutput_internal*i = (swfoutput_internal*)obj->internal;
308     i->swflastx = 0;
309     i->swflasty = 0;
310 }
311
312 static void stopFill(struct swfoutput*obj)
313 {
314     swfoutput_internal*i = (swfoutput_internal*)obj->internal;
315     if(i->lastwasfill)
316     {
317         swf_ShapeSetStyle(i->tag,i->shape,i->linestyleid,0x8000,0);
318         i->fillstylechanged = 1;
319         i->lastwasfill = 0;
320     }
321 }
322 static void startFill(struct swfoutput*obj)
323 {
324     swfoutput_internal*i = (swfoutput_internal*)obj->internal;
325     if(!i->lastwasfill)
326     {
327         swf_ShapeSetStyle(i->tag,i->shape,0x8000,i->fillstyleid,0);
328         i->fillstylechanged = 1;
329         i->lastwasfill = 1;
330     }
331 }
332
333 /* draw an outline. These are generated by pdf2swf and by t1lib
334    (representing characters). */
335 void drawpath(struct swfoutput*obj, SWF_OUTLINE*outline, struct swfmatrix*m, int log)
336 {
337     swfoutput_internal*i = (swfoutput_internal*)obj->internal;
338     if( i->tag->id != ST_DEFINESHAPE &&
339         i->tag->id != ST_DEFINESHAPE2 &&
340         i->tag->id != ST_DEFINESHAPE3)
341     {
342         msg("<error> internal error: drawpath needs a shape tag, not %d\n",i->tag->id);
343         exit(1);
344     }
345     double x=0,y=0;
346     double lastx=0,lasty=0;
347     double firstx=0,firsty=0;
348     int init=1;
349
350     while (outline)
351     {
352         x += (outline->dest.x/(float)0xffff);
353         y += (outline->dest.y/(float)0xffff);
354         if(outline->type == SWF_PATHTYPE_MOVE)
355         {
356             //if(!init && fill && obj->drawmode != DRAWMODE_EOFILL && !ignoredraworder) {
357             if(config_filloverlap && !init && i->fill && obj->drawmode != DRAWMODE_EOFILL) {
358                 /* drawmode=FILL (not EOFILL) means that
359                    seperate shapes do not cancel each other out.
360                    On SWF side, we need to start a new shape for each
361                    closed polygon, because SWF only knows EOFILL.
362                 */
363                 endshape(obj,0);
364                 startshape(obj);
365                 startFill(obj);
366             }
367
368             if(((int)(lastx*20) != (int)(firstx*20) ||
369                 (int)(lasty*20) != (int)(firsty*20)) &&
370                      i->fill && !init)
371             {
372                 plotxy p0;
373                 plotxy p1;
374                 p0.x=lastx;
375                 p0.y=lasty;
376                 p1.x=firstx;
377                 p1.y=firsty;
378                 if(log) printf("fix: %f,%f -> %f,%f\n",p0.x,p0.y,p1.x,p1.y);
379                 line(obj,i->tag, p0, p1, m);
380             }
381             firstx=x;
382             firsty=y;
383             init = 0;
384         }
385         else if(outline->type == SWF_PATHTYPE_LINE) 
386         {
387             plotxy p0;
388             plotxy p1;
389             p0.x=lastx;
390             p0.y=lasty;
391             p1.x=x;
392             p1.y=y;
393             if(log) printf("line: %f,%f -> %f,%f\n",p0.x,p0.y,p1.x,p1.y);
394             line(obj,i->tag, p0,p1,m);
395         }
396         else if(outline->type == SWF_PATHTYPE_BEZIER)
397         {
398             plotxy p0;
399             plotxy p1;
400             plotxy p2;
401             plotxy p3;
402             SWF_BEZIERSEGMENT*o2 = (SWF_BEZIERSEGMENT*)outline;
403             p0.x=x; 
404             p0.y=y;
405             p1.x=o2->C.x/(float)0xffff+lastx;
406             p1.y=o2->C.y/(float)0xffff+lasty;
407             p2.x=o2->B.x/(float)0xffff+lastx;
408             p2.y=o2->B.y/(float)0xffff+lasty;
409             p3.x=lastx;
410             p3.y=lasty;
411             if(log) printf("spline: %f,%f -> %f,%f\n",p3.x,p3.y,p0.x,p0.y);
412             spline(obj,i->tag,p0,p1,p2,p3,m);
413         } 
414         else {
415             msg("<error> drawpath: unknown outline type:%d\n", outline->type);
416         }
417         lastx=x;
418         lasty=y;
419         outline = outline->link;
420     }
421     if(((int)(lastx*20) != (int)(firstx*20) ||
422         (int)(lasty*20) != (int)(firsty*20)) &&
423              i->fill)
424     {
425         plotxy p0;
426         plotxy p1;
427         p0.x=lastx;
428         p0.y=lasty;
429         p1.x=firstx;
430         p1.y=firsty;
431         if(log) printf("fix: %f,%f -> %f,%f\n",p0.x,p0.y,p1.x,p1.y);
432         line(obj, i->tag, p0, p1, m);
433     }
434 }
435
436 plotxy getPivot(struct swfoutput*obj, SWF_OUTLINE*outline, int dir, double line_width, int end, int trytwo)
437 {
438     swfoutput_internal*i = (swfoutput_internal*)obj->internal;
439     SWF_PATHPOINT next, next2;
440     double xv=0,yv=0, xv2=0, yv2=0;
441     plotxy p;
442     int two = 0;
443
444     if(!end) {
445         if(outline->type == SWF_PATHTYPE_LINE) {
446             next = outline->dest;
447         } else {
448             next = ((SWF_BEZIERSEGMENT*)outline)->B;
449             if(next.x==0 && next.y==0) {
450                 next = ((SWF_BEZIERSEGMENT*)outline)->C;
451             }
452             if(next.x==0 && next.y==0) {
453                 next = ((SWF_BEZIERSEGMENT*)outline)->dest;
454             }
455         }
456         next2 = next;
457         if(trytwo && outline->last && outline->last->type != SWF_PATHTYPE_MOVE) {
458             if(outline->type == SWF_PATHTYPE_LINE) {
459                 next2 = outline->last->dest;
460             } else {
461                 SWF_PATHPOINT c = ((SWF_BEZIERSEGMENT*)(outline->last))->C;
462                 SWF_PATHPOINT b = ((SWF_BEZIERSEGMENT*)(outline->last))->B;
463                 next2.x = outline->last->dest.x - c.x;
464                 next2.y = outline->last->dest.y - c.y;
465                 if(next2.x==0 && next2.y==0) {
466                     next2.x = outline->last->dest.x - b.x;
467                     next2.y = outline->last->dest.y - b.y;
468                 }
469                 if(next2.x==0 && next2.y==0) {
470                     next2.x = outline->last->dest.x;
471                     next2.y = outline->last->dest.y;
472                 }
473             }
474             two = 1;
475         }
476     } else {
477         if(outline->type == SWF_PATHTYPE_LINE) {
478             next = outline->dest;
479         } else {
480             SWF_PATHPOINT c = ((SWF_BEZIERSEGMENT*)outline)->C;
481             SWF_PATHPOINT b = ((SWF_BEZIERSEGMENT*)outline)->B;
482             next.x = outline->dest.x - c.x;
483             next.y = outline->dest.y - c.y;
484             if(next.x==0 && next.y==0) {
485                 next.x = outline->dest.x - b.x;
486                 next.y = outline->dest.y - b.y;
487             }
488             if(next.x==0 && next.y==0) {
489                 next.x = outline->dest.x;
490                 next.y = outline->dest.y;
491             }
492         }
493         next2 = next;
494         if(trytwo && outline->link && outline->link->type != SWF_PATHTYPE_MOVE) {
495             if(outline->type == SWF_PATHTYPE_LINE) {
496                 next2 = outline->link->dest;
497             } else {
498                 next2 = ((SWF_BEZIERSEGMENT*)(outline->link))->B;
499                 if(next2.x==0 && next2.y==0) {
500                     next2 = ((SWF_BEZIERSEGMENT*)outline->link)->C;
501                 }
502                 if(next2.x==0 && next2.y==0) {
503                     next2 = ((SWF_BEZIERSEGMENT*)outline->link)->dest;
504                 }
505             }
506             two = 1;
507         }
508     }
509
510     if(dir) {
511         xv =  next.y/(float)0xffff;
512         yv = -next.x/(float)0xffff;
513     } else {
514         xv = -next.y/(float)0xffff;
515         yv =  next.x/(float)0xffff;
516     }
517
518     double r = (line_width/2)/sqrt(xv*xv+yv*yv);
519     xv*=r;
520     yv*=r;
521
522     if(two) {
523         if(dir) {
524             xv2 =  next2.y/(float)0xffff;
525             yv2 = -next2.x/(float)0xffff;
526         } else {
527             xv2 = -next2.y/(float)0xffff;
528             yv2 =  next2.x/(float)0xffff;
529         }
530
531         double r2 = (line_width/2)/sqrt(xv2*xv2+yv2*yv2);
532         xv2*=r2;
533         yv2*=r2;
534         xv = (xv+xv2)/2;
535         yv = (yv+yv2)/2;
536         double r3 = (line_width/2)/sqrt(xv*xv+yv*yv);
537         xv *= r3;
538         yv *= r3;
539     }
540
541     p.x = xv;
542     p.y = yv;
543     return p;
544 }
545
546 void drawShortPath(struct swfoutput*obj, double x, double y, struct swfmatrix* m, SWF_OUTLINE*outline)
547 {
548     swfoutput_internal*i = (swfoutput_internal*)obj->internal;
549     double lastx=x, lasty=y;
550     while (outline && outline->type != SWF_PATHTYPE_MOVE)
551     {
552         x += (outline->dest.x/(float)0xffff);
553         y += (outline->dest.y/(float)0xffff);
554
555         if(outline->type == SWF_PATHTYPE_LINE)
556         {
557             plotxy p0, p1;
558             p0.x=lastx;
559             p0.y=lasty;
560             p1.x= x; 
561             p1.y= y;
562             line(obj, i->tag, p0, p1, m);
563         }
564         else if(outline->type == SWF_PATHTYPE_BEZIER)
565         {
566             plotxy p0,p1,p2,p3;
567             SWF_BEZIERSEGMENT*o2 = (SWF_BEZIERSEGMENT*)outline;
568             p3.x=lastx;
569             p3.y=lasty;
570             p1.x=o2->C.x/(float)0xffff+lastx;
571             p1.y=o2->C.y/(float)0xffff+lasty;
572             p2.x=o2->B.x/(float)0xffff+lastx;
573             p2.y=o2->B.y/(float)0xffff+lasty;
574             p0.x=x; 
575             p0.y=y;
576             spline(obj, i->tag,p0,p1,p2,p3,m);
577         } 
578         lastx=x;
579         lasty=y;
580         outline = outline->link;
581     }
582 }
583
584 void drawShortPathWithEnds(struct swfoutput*obj, double x, double y, struct swfmatrix* m, SWF_OUTLINE*outline, int num, int line_cap, int line_join, double line_width)
585 {
586     swfoutput_internal*i = (swfoutput_internal*)obj->internal;
587     plotxy d,d2;
588     int back = 0;
589
590     if(line_cap == LINE_CAP_BUTT || line_cap == LINE_CAP_SQUARE) {
591         endshape(obj,0);
592         startshape(obj);
593         SWF_OUTLINE *last, *tmp=outline;
594         plotxy s,e,p0,p1,p2,p3,m0,m1,m2,m3;
595         double x2 = x;
596         double y2 = y;
597         double lx=x,ly=y;
598         double ee = 1.0;
599         int nr;
600         while(tmp && tmp->type != SWF_PATHTYPE_MOVE) {
601             last = tmp;
602             lx += (tmp->dest.x/(float)0xffff);
603             ly += (tmp->dest.y/(float)0xffff);
604             tmp = tmp->link;
605         }
606         s = getPivot(obj, outline, 0, line_width, 0, 0);
607         e = getPivot(obj, last, 0, line_width, 1, 0);
608
609         if(line_cap == LINE_CAP_BUTT) {
610             /* make the clipping rectangle slighly bigger
611                than the line ending, so that it get's clipped
612                propertly */
613             //ee = 1.01;
614             ee=1.0;
615         }
616
617         p0.x = x2 + s.x*ee; 
618         p0.y = y2 + s.y*ee;
619         p1.x = x2 - s.x*ee; 
620         p1.y = y2 - s.y*ee;
621         p2.x = x2 - s.y - s.x*ee; 
622         p2.y = y2 + s.x - s.y*ee;
623         p3.x = x2 - s.y + s.x*ee; 
624         p3.y = y2 + s.x + s.y*ee;
625         m0.x = lx + e.x*ee; 
626         m0.y = ly + e.y*ee;
627         m1.x = lx - e.x*ee; 
628         m1.y = ly - e.y*ee;
629         m2.x = lx + e.y - e.x*ee; 
630         m2.y = ly - e.x - e.y*ee;
631         m3.x = lx + e.y + e.x*ee; 
632         m3.y = ly - e.x + e.y*ee;
633
634         for(nr=0;nr<2;nr++) {
635             int dir=0;
636             struct plotxy q0,q1,q2,q3,q4,q5;
637
638             startFill(obj);
639             if(line_cap == LINE_CAP_BUTT) {
640                 if(dir) {
641                     /* FIXME: box should be smaller */
642                     q0.x = 0; q0.y = 0;
643                     q1.x = i->max_x; q1.y = 0;
644                     q2.x = i->max_x; q2.y = i->max_y;
645                     q3.x = 0; q3.y = i->max_y;
646                 } else {
647                     /* FIXME: box should be smaller */
648                     q0.x = i->max_x; q0.y = i->max_y;
649                     q1.x = 0; q1.y = i->max_y;
650                     q2.x = 0; q2.y = 0;
651                     q3.x = i->max_x; q3.y = 0;
652                 }
653                 q4.x = p0.x; 
654                 q4.y = p0.y;
655                 moveto(obj, i->tag, q0);
656                 lineto(obj, i->tag, q1);
657                 lineto(obj, i->tag, q2);
658                 lineto(obj, i->tag, q3);
659                 lineto(obj, i->tag, q0);
660
661                 transform(&q4,m);
662                 lineto(obj, i->tag, q4);
663             }
664
665             line(obj, i->tag, p0, p1, m);
666             line(obj, i->tag, p1, p2, m);
667             line(obj, i->tag, p2, p3, m);
668             line(obj, i->tag, p3, p0, m);
669
670             if(line_cap == LINE_CAP_BUTT) {
671                 lineto(obj, i->tag, q0);
672                 endshape(obj, i->depth+2-nr);
673                 startshape(obj);
674             }
675             p0 = m0;
676             p1 = m1;
677             p2 = m2;
678             p3 = m3;
679         }
680
681         stopFill(obj);
682     }
683
684     drawShortPath(obj,x,y,m,outline);
685
686     if(line_cap == LINE_CAP_BUTT) {
687         endshape(obj,0);
688         startshape(obj);
689     }
690 }
691
692 void drawT1toRect(struct swfoutput*obj, double x, double y, struct swfmatrix* m, SWF_OUTLINE*outline, int num, int line_cap, int line_join, double line_width)
693 {
694     swfoutput_internal*i = (swfoutput_internal*)obj->internal;
695     plotxy d1,d2,p1,p2,p3,p4;
696
697     d1.x = (outline->dest.x/(float)0xffff);
698     d1.y = (outline->dest.y/(float)0xffff);
699     d2 = getPivot(obj, outline, 0, line_width, 0, 0);
700
701     assert(line_cap != LINE_CAP_ROUND);
702     if(line_cap == LINE_CAP_SQUARE) {
703         x -= +d2.y;
704         y -= -d2.x;
705         d1.x += +2*d2.y;
706         d1.y += -2*d2.x;
707     }
708
709     p1.x = x + d2.x;
710     p1.y = y + d2.y;
711     p2.x = x + d2.x + d1.x;
712     p2.y = y + d2.y + d1.y;
713     p3.x = x - d2.x + d1.x;
714     p3.y = y - d2.y + d1.y;
715     p4.x = x - d2.x;
716     p4.y = y - d2.y;
717
718     line(obj, i->tag, p1,p2, m);
719     line(obj, i->tag, p2,p3, m);
720     line(obj, i->tag, p3,p4, m);
721     line(obj, i->tag, p4,p1, m);
722 }
723
724 void drawShortPathWithStraightEnds(struct swfoutput*obj, double x, double y, struct swfmatrix* m, SWF_OUTLINE*outline, int num, int line_cap, int line_join, double line_width)
725 {
726     swfoutput_internal*i = (swfoutput_internal*)obj->internal;
727     SWF_OUTLINE*tmp=outline;
728     double xx=x,yy=y;
729     int stop=0;
730     assert(i->shapeid>=0);
731
732     startFill(obj);
733     drawT1toRect(obj, x, y, m,outline, num, line_cap, line_join, line_width);
734
735     while(tmp->link && tmp->link->type!=SWF_PATHTYPE_MOVE) {
736         xx += (tmp->dest.x/(float)0xffff);
737         yy += (tmp->dest.y/(float)0xffff);
738         tmp = tmp->link;
739     }
740     
741     assert(tmp->type == SWF_PATHTYPE_LINE);
742     assert(outline->type == SWF_PATHTYPE_LINE);
743     
744     if(tmp!=outline) {
745    
746         if(outline->link == tmp) {
747             /* the two straight line segments (which are everything we
748                need to draw) are very likely to overlap. To avoid that
749                they cancel each other out at the end points, start a new
750                shape for the second one */
751             endshape(obj,0);startshape(obj);
752             startFill(obj);
753         }
754
755         drawT1toRect(obj, xx, yy, m, tmp, num, line_cap, line_join, line_width);
756
757         if(outline->link != tmp)
758         {
759             stopFill(obj);stop=1;
760             int save= tmp->type;
761             tmp->type = SWF_PATHTYPE_MOVE;
762             x += (outline->dest.x/(float)0xffff);
763             y += (outline->dest.y/(float)0xffff);
764             outline = outline->link;
765             drawShortPath(obj, x, y, m, outline);
766             tmp->type = save;
767         }
768     }
769     if(!stop)
770         stopFill(obj);
771 }
772
773 static int t1len(struct swfoutput*obj, SWF_OUTLINE*line)
774 {
775     swfoutput_internal*i = (swfoutput_internal*)obj->internal;
776     int num=0;
777     while(line && line->type != SWF_PATHTYPE_MOVE) {
778         num++;
779         line = line->link;
780     }
781     return num;
782 }
783
784 static float t1linelen(struct swfoutput*obj, SWF_OUTLINE*line)
785 {
786     swfoutput_internal*i = (swfoutput_internal*)obj->internal;
787     float x,y;
788     x = (line->dest.x/(float)0xffff);
789     y = (line->dest.y/(float)0xffff);
790     return sqrt(x*x+y*y);
791 }
792
793 void drawpath2poly(struct swfoutput *obj, SWF_OUTLINE*outline, struct swfmatrix*m, int log, int line_join, int line_cap, double line_width, double miter_limit)
794 {
795     swfoutput_internal*i = (swfoutput_internal*)obj->internal;
796     if( i->tag->id != ST_DEFINESHAPE &&
797         i->tag->id != ST_DEFINESHAPE2 &&
798         i->tag->id != ST_DEFINESHAPE3) {
799         msg("<error> internal error: drawpath needs a shape tag, not %d\n",i->tag->id);
800         exit(1);
801     }
802     assert(i->shapeid>=0);
803     double x=0,y=0;
804     double lastx=0,lasty=0;
805     int valid = 0;
806     int lastwasline = 0;
807     SWF_OUTLINE*tmp = outline, *last = 0;
808     tmp->last = 0;
809
810     while(1) {
811         double previousx = x, previousy = y;
812         if(tmp) {
813             x += (tmp->dest.x/(float)0xffff);
814             y += (tmp->dest.y/(float)0xffff);
815         }
816         if(!tmp || tmp->type == SWF_PATHTYPE_MOVE) {
817             if(valid && last) {
818                 if(fabs(lastx-previousx)<0.001 && fabs(lasty-previousy)<0.001) {
819                     /* endpoints match- the path is closed.
820                        Don't bother to draw endings */
821                     drawShortPath(obj, lastx, lasty, m, last);
822                 }
823                 if(last->type == SWF_PATHTYPE_LINE && t1linelen(obj,last)>line_width*2 &&
824                    lastwasline && line_cap != LINE_CAP_ROUND)
825                     drawShortPathWithStraightEnds(obj, lastx, lasty, m, last, valid, line_cap, line_join, line_width);
826                 else
827                     drawShortPathWithEnds(obj, lastx, lasty, m, last, valid, line_cap, line_join, line_width);
828             }
829             if(!tmp)
830                 break;
831             valid = 0;
832             last = 0;
833             lastx = x;
834             lasty = y;
835         } else {
836             if(!last)
837                 last = tmp; //remember last stroke start (first segment after moveto)
838             valid++;
839         }
840
841         if(tmp && tmp->type == SWF_PATHTYPE_LINE && t1linelen(obj,tmp)>line_width*2)
842             lastwasline = 1;
843         else
844             lastwasline = 0;
845
846         if(tmp->link)
847             tmp->link->last = tmp; // make sure list is properly linked in both directions
848         tmp = tmp->link;
849     }
850 }
851
852 static inline int colorcompare(struct swfoutput*obj, RGBA*a,RGBA*b)
853 {
854
855     if(a->r!=b->r ||
856        a->g!=b->g ||
857        a->b!=b->b ||
858        a->a!=b->a) {
859         return 0;
860     }
861     return 1;
862 }
863
864 static SRECT getcharacterbbox(struct swfoutput*obj, SWFFONT*font)
865 {
866     swfoutput_internal*i = (swfoutput_internal*)obj->internal;
867     SRECT r;
868     char debug = 0;
869     memset(&r, 0, sizeof(r));
870
871     int t;
872     if(debug) printf("\n");
873     for(t=0;t<i->chardatapos;t++)
874     {
875         if(i->chardata[t].fontid != font->id) {
876             msg("<error> Internal error: fontid %d != fontid %d", i->chardata[t].fontid, font->id);
877             exit(1);
878         }
879         SRECT b = font->layout->bounds[i->chardata[t].charid];
880         b.xmin *= i->chardata[t].size;
881         b.ymin *= i->chardata[t].size;
882         b.xmax *= i->chardata[t].size;
883         b.ymax *= i->chardata[t].size;
884         b.xmin /= 1024;
885         b.ymin /= 1024;
886         b.xmax /= 1024;
887         b.ymax /= 1024;
888         b.xmin += i->chardata[t].x;
889         b.ymin += i->chardata[t].y;
890         b.xmax += i->chardata[t].x;
891         b.ymax += i->chardata[t].y;
892
893         /* until we solve the INTERNAL_SCALING problem (see below)
894            make sure the bounding box is big enough */
895         b.xmin -= 20;
896         b.ymin -= 20;
897         b.xmax += 20;
898         b.ymax += 20;
899
900         if(debug) printf("(%f,%f,%f,%f) -> (%f,%f,%f,%f) [font %d/%d, char %d]\n",
901                 font->layout->bounds[i->chardata[t].charid].xmin/20.0,
902                 font->layout->bounds[i->chardata[t].charid].ymin/20.0,
903                 font->layout->bounds[i->chardata[t].charid].xmax/20.0,
904                 font->layout->bounds[i->chardata[t].charid].ymax/20.0,
905                 b.xmin/20.0,
906                 b.ymin/20.0,
907                 b.xmax/20.0,
908                 b.ymax/20.0,
909                 i->chardata[t].fontid,
910                 font->id,
911                 i->chardata[t].charid
912                 );
913         swf_ExpandRect2(&r, &b);
914     }
915     if(debug) printf("-----> (%f,%f,%f,%f)\n",
916             r.xmin/20.0,
917             r.ymin/20.0,
918             r.xmax/20.0,
919             r.ymax/20.0);
920     return r;
921 }
922
923 static void putcharacters(struct swfoutput*obj, TAG*tag)
924 {
925     swfoutput_internal*i = (swfoutput_internal*)obj->internal;
926     int t;
927     SWFFONT font;
928     RGBA color;
929     color.r = i->chardata[0].color.r^255;
930     color.g = 0;
931     color.b = 0;
932     color.a = 0;
933     int lastfontid;
934     int lastx;
935     int lasty;
936     int lastsize;
937     int charids[128];
938     int charadvance[128];
939     int charstorepos;
940     int pass;
941     int glyphbits=1; //TODO: can this be zero?
942     int advancebits=1;
943
944     if(tag->id != ST_DEFINETEXT &&
945         tag->id != ST_DEFINETEXT2) {
946         msg("<error> internal error: putcharacters needs an text tag, not %d\n",tag->id);
947         exit(1);
948     }
949     if(!i->chardatapos) {
950         msg("<warning> putcharacters called with zero characters");
951     }
952
953     for(pass = 0; pass < 2; pass++)
954     {
955         charstorepos = 0;
956         lastfontid = -1;
957         lastx = CHARMIDX;
958         lasty = CHARMIDY;
959         lastsize = -1;
960
961         if(pass==1)
962         {
963             advancebits++; // add sign bit
964             swf_SetU8(tag, glyphbits);
965             swf_SetU8(tag, advancebits);
966         }
967
968         for(t=0;t<=i->chardatapos;t++)
969         {
970             if(lastfontid != i->chardata[t].fontid || 
971                     lastx!=i->chardata[t].x ||
972                     lasty!=i->chardata[t].y ||
973                     !colorcompare(obj,&color, &i->chardata[t].color) ||
974                     charstorepos==127 ||
975                     lastsize != i->chardata[t].size ||
976                     t == i->chardatapos)
977             {
978                 if(charstorepos && pass==0)
979                 {
980                     int s;
981                     for(s=0;s<charstorepos;s++)
982                     {
983                         while(charids[s]>=(1<<glyphbits))
984                             glyphbits++;
985                         while(charadvance[s]>=(1<<advancebits))
986                             advancebits++;
987                     }
988                 }
989                 if(charstorepos && pass==1)
990                 {
991                     tag->writeBit = 0; // Q&D
992                     swf_SetBits(tag, 0, 1); // GLYPH Record
993                     swf_SetBits(tag, charstorepos, 7); // number of glyphs
994                     int s;
995                     for(s=0;s<charstorepos;s++)
996                     {
997                         swf_SetBits(tag, charids[s], glyphbits);
998                         swf_SetBits(tag, charadvance[s], advancebits);
999                     }
1000                 }
1001                 charstorepos = 0;
1002
1003                 if(pass == 1 && t<i->chardatapos)
1004                 {
1005                     RGBA*newcolor=0;
1006                     SWFFONT*newfont=0;
1007                     int newx = 0;
1008                     int newy = 0;
1009                     if(lastx != i->chardata[t].x ||
1010                        lasty != i->chardata[t].y)
1011                     {
1012                         newx = i->chardata[t].x;
1013                         newy = i->chardata[t].y;
1014                         if(newx == 0)
1015                             newx = SET_TO_ZERO;
1016                         if(newy == 0)
1017                             newy = SET_TO_ZERO;
1018                     }
1019                     if(!colorcompare(obj,&color, &i->chardata[t].color)) 
1020                     {
1021                         color = i->chardata[t].color;
1022                         newcolor = &color;
1023                     }
1024                     font.id = i->chardata[t].fontid;
1025                     if(lastfontid != i->chardata[t].fontid || lastsize != i->chardata[t].size)
1026                         newfont = &font;
1027
1028                     tag->writeBit = 0; // Q&D
1029                     swf_TextSetInfoRecord(tag, newfont, i->chardata[t].size, newcolor, newx,newy);
1030                 }
1031
1032                 lastfontid = i->chardata[t].fontid;
1033                 lastx = i->chardata[t].x;
1034                 lasty = i->chardata[t].y;
1035                 lastsize = i->chardata[t].size;
1036             }
1037
1038             if(t==i->chardatapos)
1039                     break;
1040
1041             int advance;
1042             int nextt = t==i->chardatapos-1?t:t+1;
1043             int rel = i->chardata[nextt].x-i->chardata[t].x;
1044             if(rel>=0 && (rel<(1<<(advancebits-1)) || pass==0)) {
1045                advance = rel;
1046                lastx=i->chardata[nextt].x;
1047             }
1048             else {
1049                advance = 0;
1050                lastx=i->chardata[t].x;
1051             }
1052             charids[charstorepos] = i->chardata[t].charid;
1053             charadvance[charstorepos] = advance;
1054             charstorepos ++;
1055         }
1056     }
1057     i->chardatapos = 0;
1058 }
1059
1060 static void putcharacter(struct swfoutput*obj, int fontid, int charid, 
1061                     int x,int y, int size)
1062 {
1063     swfoutput_internal*i = (swfoutput_internal*)obj->internal;
1064     if(i->chardatapos == CHARDATAMAX)
1065     {
1066         msg("<warning> Character buffer too small. SWF will be slightly bigger");
1067         endtext(obj);
1068         starttext(obj);
1069     }
1070     i->chardata[i->chardatapos].fontid = fontid;
1071     i->chardata[i->chardatapos].charid = charid;
1072     i->chardata[i->chardatapos].x = x;
1073     i->chardata[i->chardatapos].y = y;
1074     i->chardata[i->chardatapos].color = obj->fillrgb;
1075     i->chardata[i->chardatapos].size = size;
1076     i->chardatapos++;
1077 }
1078
1079 /* Notice: we can only put chars in the range -1639,1638 (-32768/20,32768/20).
1080    So if we set this value to high, the char coordinates will overflow.
1081    If we set it to low, however, the char positions will be inaccurate */
1082 #define FONT_INTERNAL_SIZE 4
1083
1084 /* process a character. */
1085 static int drawchar(struct swfoutput*obj, SWFFONT *swffont, char*character, int charnr, int u, swfmatrix*m)
1086 {
1087     swfoutput_internal*i = (swfoutput_internal*)obj->internal;
1088     if(!swffont) {
1089         msg("<warning> Font is NULL");
1090         return 0;
1091     }
1092
1093     int charid = getCharID(swffont, charnr, character, u); 
1094     
1095     if(charid<0) {
1096         msg("<warning> Didn't find character '%s' (c=%d,u=%d) in current charset (%s, %d characters)", 
1097                 FIXNULL(character),charnr, u, FIXNULL((char*)swffont->name), swffont->numchars);
1098         return 0;
1099     }
1100
1101     if(i->shapeid>=0)
1102         endshape(obj,0);
1103     if(i->textid<0)
1104         starttext(obj);
1105
1106     float x = m->m13;
1107     float y = m->m23;
1108     float det = ((m->m11*m->m22)-(m->m21*m->m12));
1109     if(fabs(det) < 0.0005) { 
1110         /* x direction equals y direction- the text is invisible */
1111         return 1;
1112     }
1113     det = 20*FONT_INTERNAL_SIZE / det;
1114
1115     SPOINT p;
1116     p.x = (SCOORD)((  x * m->m22 - y * m->m12)*det);
1117     p.y = (SCOORD)((- x * m->m21 + y * m->m11)*det);
1118
1119     putcharacter(obj, swffont->id, charid,p.x,p.y,FONT_INTERNAL_SIZE);
1120     swf_FontUseGlyph(swffont, charid);
1121     return 1;
1122
1123     /*else
1124     {
1125         SWF_OUTLINE*outline = font->getOutline(character, charnr);
1126         char* charname = character;
1127
1128         if(!outline) {
1129          msg("<warning> Didn't find character '%s' (%d) in current charset (%s)", 
1130                  FIXNULL(character),charnr,FIXNULL(font->getName()));
1131          return;
1132         }
1133         
1134         swfmatrix m2=*m;    
1135         m2.m11/=100;
1136         m2.m21/=100;
1137         m2.m12/=100;
1138         m2.m22/=100;
1139
1140         if(textid>=0)
1141             endtext(obj);
1142         if(shapeid<0)
1143             startshape(obj);
1144
1145         startFill(obj);
1146
1147         int lf = fill;
1148         fill = 1;
1149         drawpath(tag, outline, &m2, 0);
1150         fill = lf;
1151     }*/
1152 }
1153
1154 static void endtext(swfoutput*obj)
1155 {
1156     swfoutput_internal*i = (swfoutput_internal*)obj->internal;
1157     if(i->textid<0)
1158         return;
1159
1160     i->tag = swf_InsertTag(i->tag,ST_DEFINETEXT);
1161     swf_SetU16(i->tag, i->textid);
1162
1163     SRECT r;
1164     r = getcharacterbbox(obj, obj->swffont);
1165     
1166     swf_SetRect(i->tag,&r);
1167
1168     MATRIX m;
1169     swf_GetMatrix(0, &m); /* set unit matrix- the real matrix is in the placeobject */
1170     swf_SetMatrix(i->tag,&m);
1171
1172     putcharacters(obj, i->tag);
1173     swf_SetU8(i->tag,0);
1174     i->tag = swf_InsertTag(i->tag,ST_PLACEOBJECT2);
1175     //swf_ObjectPlace(i->tag,i->textid,/*depth*/i->depth++,&i->page_matrix,NULL,NULL);
1176     MATRIX m2;
1177     swf_MatrixJoin(&m2,&obj->fontmatrix, &i->page_matrix);
1178
1179     swf_ObjectPlace(i->tag,i->textid,/*depth*/i->depth++,&m2,NULL,NULL);
1180     i->textid = -1;
1181 }
1182
1183
1184 /* draw a curved polygon. */
1185 void swfoutput_drawpath(swfoutput*obj, SWF_OUTLINE*outline, 
1186                             struct swfmatrix*m)
1187 {
1188     swfoutput_internal*i = (swfoutput_internal*)obj->internal;
1189     if(i->textid>=0)
1190         endtext(obj);
1191
1192     /* Multiple polygons in one shape don't overlap correctly, 
1193        so we better start a new shape here if the polygon is filled
1194      */
1195     if(i->shapeid>=0 && i->fill && !config_ignoredraworder) {
1196         endshape(obj,0);
1197     }
1198
1199     if(i->shapeid<0) {
1200         startshape(obj);
1201     }
1202
1203     if(!i->fill)
1204         stopFill(obj);
1205     else
1206         startFill(obj);
1207
1208     drawpath(obj, outline,m, 0); 
1209 }
1210
1211 void swfoutput_drawpath2poly(struct swfoutput*obj, SWF_OUTLINE*outline, struct swfmatrix*m, int line_join, int line_cap, double line_width, double miter_limit)
1212 {
1213     swfoutput_internal*i = (swfoutput_internal*)obj->internal;
1214     if(i->textid>=0)
1215         endtext(obj);
1216     if(i->shapeid>=0)
1217         endshape(obj,0);
1218     assert(i->shapeid<0);
1219     startshape(obj);
1220     stopFill(obj);
1221
1222     drawpath2poly(obj, outline, m, 0, line_join, line_cap, line_width, miter_limit); 
1223 }
1224
1225 int getCharID(SWFFONT *font, int charnr, char *charname, int u)
1226 {
1227     int t;
1228     if(charname && font->glyphnames) {
1229         for(t=0;t<font->numchars;t++) {
1230             if(font->glyphnames[t] && !strcmp(font->glyphnames[t],charname)) {
1231                 msg("<debug> Char [%d,>%s<,%d] maps to %d\n", charnr, charname, u, t);
1232                 return t;
1233             }
1234         }
1235         /* if we didn't find the character, maybe
1236            we can find the capitalized version */
1237         for(t=0;t<font->numchars;t++) {
1238             if(font->glyphnames[t] && !strcasecmp(font->glyphnames[t],charname)) {
1239                 msg("<debug> Char [%d,>>%s<<,%d] maps to %d\n", charnr, charname, u, t);
1240                 return t;
1241             }
1242         }
1243     }
1244
1245     if(u>0 && font->encoding != 255) {
1246         /* try to use the unicode id */
1247         if(u>=0 && u<font->maxascii && font->ascii2glyph[u]>=0) {
1248             msg("<debug> Char [%d,%s,>%d<] maps to %d\n", charnr, charname, u, font->ascii2glyph[u]);
1249             return font->ascii2glyph[u];
1250         }
1251     }
1252
1253     if(font->encoding != FONT_ENCODING_UNICODE) {
1254         /* the following only works if the font encoding
1255            is US-ASCII based. It's needed for fonts which return broken unicode
1256            indices */
1257         if(charnr>=0 && charnr<font->maxascii && font->ascii2glyph[charnr]>=0) {
1258             msg("<debug> Char [>%d<,%s,%d] maps to %d\n", charnr, charname, u, font->ascii2glyph[charnr]);
1259             return font->ascii2glyph[charnr];
1260         }
1261     } 
1262     
1263     if(charnr>=0 && charnr<font->numchars) {
1264         msg("<debug> Char [>%d<,%s,%d] maps to %d\n", charnr, charname, u, charnr);
1265         return charnr;
1266     }
1267     
1268     return -1;
1269 }
1270
1271
1272 /* set's the t1 font index of the font to use for swfoutput_drawchar(). */
1273 void swfoutput_setfont(struct swfoutput*obj, char*fontid, char*filename)
1274 {
1275     swfoutput_internal*i = (swfoutput_internal*)obj->internal;
1276     fontlist_t*last=0,*iterator;
1277     if(!fontid) {
1278         msg("<error> No fontid");
1279         return;
1280     }
1281
1282     if(obj->swffont && obj->swffont->name && !strcmp((char*)obj->swffont->name,fontid))
1283         return;
1284
1285     /* TODO: remove the need for this (enhance getcharacterbbox so that it can cope
1286              with multiple fonts */
1287     endtext(obj);
1288
1289     iterator = i->fontlist;
1290     while(iterator) {
1291         if(!strcmp((char*)iterator->swffont->name,fontid)) {
1292             obj->swffont = iterator->swffont; 
1293             return;
1294         }
1295         last = iterator;
1296         iterator = iterator->next;
1297     }
1298
1299     if(!filename) {
1300         msg("<error> No filename given for font- internal error?");
1301         return;
1302     }
1303
1304     swf_SetLoadFontParameters(64,/*skip unused*/0,/*full unicode*/1);
1305     SWFFONT*swffont = swf_LoadFont(filename);
1306
1307     if(swffont == 0) {
1308         msg("<warning> Couldn't load font %s (%s)", fontid, filename);
1309         swffont = swf_LoadFont(0);
1310     }
1311     
1312     if(swffont->glyph2ascii) {
1313         int t;
1314         int bad = 0;
1315         /* check whether the Unicode indices look o.k.
1316            If they don't, disable the unicode lookup by setting
1317            the encoding to 255 */
1318         for(t=0;t<swffont->numchars;t++) {
1319             int c = swffont->glyph2ascii[t];
1320             if(c && c < 32 && swffont->glyph[t].shape->bitlen > 16) {
1321                 // the character maps into the unicode control character range
1322                 // between 0001-001f. Yet it is not empty. Treat the one
1323                 // mapping as broken, and look how many of those we find.
1324                 bad ++;
1325             }
1326         }
1327         if(bad>5) {
1328             msg("<warning> Font %s has bad unicode mapping", swffont->name);
1329             swffont->encoding = 255;
1330         }
1331     }
1332
1333     swf_FontSetID(swffont, ++i->currentswfid);
1334     
1335     if(getScreenLogLevel() >= LOGLEVEL_DEBUG)  {
1336         // print font information
1337         msg("<debug> Font %s (%s)",swffont->name, filename);
1338         msg("<debug> |   ID: %d", swffont->id);
1339         msg("<debug> |   Version: %d", swffont->version);
1340         msg("<debug> |   Name: %s", fontid);
1341         msg("<debug> |   Numchars: %d", swffont->numchars);
1342         msg("<debug> |   Maxascii: %d", swffont->maxascii);
1343         msg("<debug> |   Style: %d", swffont->style);
1344         msg("<debug> |   Encoding: %d", swffont->encoding);
1345         for(int iii=0; iii<swffont->numchars;iii++) {
1346             msg("<debug> |   Glyph %d) name=%s, unicode=%d size=%d bbox=(%.2f,%.2f,%.2f,%.2f)\n", iii, swffont->glyphnames?swffont->glyphnames[iii]:"<nonames>", swffont->glyph2ascii[iii], swffont->glyph[iii].shape->bitlen, 
1347                     swffont->layout->bounds[iii].xmin/20.0,
1348                     swffont->layout->bounds[iii].ymin/20.0,
1349                     swffont->layout->bounds[iii].xmax/20.0,
1350                     swffont->layout->bounds[iii].ymax/20.0
1351                     );
1352             int t;
1353             for(t=0;t<swffont->maxascii;t++) {
1354                 if(swffont->ascii2glyph[t] == iii)
1355                     msg("<debug> | - maps to %d",t);
1356             }
1357         }
1358     }
1359
1360     /* set the font name to the ID we use here */
1361     if(swffont->name) free(swffont->name);
1362     swffont->name = (U8*)strdup(fontid);
1363
1364     iterator = new fontlist_t;
1365     iterator->swffont = swffont;
1366     iterator->next = 0;
1367
1368     if(last) 
1369         last->next = iterator;
1370     else 
1371         i->fontlist = iterator;
1372
1373     obj->swffont = swffont; 
1374 }
1375
1376 int swfoutput_queryfont(struct swfoutput*obj, char*fontid)
1377 {
1378     swfoutput_internal*i = (swfoutput_internal*)obj->internal;
1379     fontlist_t *iterator = i->fontlist;
1380     while(iterator) {
1381         if(!strcmp((char*)iterator->swffont->name,fontid))
1382             return 1;
1383         iterator = iterator->next;
1384     }
1385     return 0;
1386 }
1387
1388 /* set's the matrix which is to be applied to characters drawn by
1389    swfoutput_drawchar() */
1390 void swfoutput_setfontmatrix(struct swfoutput*obj,double m11,double m12,
1391                                                   double m21,double m22)
1392 {
1393     swfoutput_internal*i = (swfoutput_internal*)obj->internal;
1394     if(obj->fontm11 == m11 &&
1395        obj->fontm12 == m12 &&
1396        obj->fontm21 == m21 &&
1397        obj->fontm22 == m22)
1398         return;
1399    if(i->textid>=0)
1400         endtext(obj);
1401     obj->fontm11 = m11;
1402     obj->fontm12 = m12;
1403     obj->fontm21 = m21;
1404     obj->fontm22 = m22;
1405     
1406     MATRIX m;
1407     m.sx = (U32)(((obj->fontm11)*65536)/FONT_INTERNAL_SIZE); m.r1 = (U32)(((obj->fontm12)*65536)/FONT_INTERNAL_SIZE);
1408     m.r0 = (U32)(((obj->fontm21)*65536)/FONT_INTERNAL_SIZE); m.sy = (U32)(((obj->fontm22)*65536)/FONT_INTERNAL_SIZE); 
1409     m.tx = 0;
1410     m.ty = 0;
1411     obj->fontmatrix = m;
1412 }
1413
1414 /* draws a character at x,y. */
1415 int swfoutput_drawchar(struct swfoutput* obj,double x,double y,char*character, int charnr, int u) 
1416 {
1417     swfoutput_internal*i = (swfoutput_internal*)obj->internal;
1418     swfmatrix m;
1419     m.m11 = obj->fontm11;
1420     m.m12 = obj->fontm12;
1421     m.m21 = obj->fontm21;
1422     m.m22 = obj->fontm22;
1423     m.m13 = x;
1424     m.m23 = y;
1425     return drawchar(obj, obj->swffont, character, charnr, u, &m);
1426 }
1427
1428 static void endpage(struct swfoutput*obj)
1429 {
1430     swfoutput_internal*i = (swfoutput_internal*)obj->internal;
1431     if(i->shapeid>=0)
1432       endshape(obj,0);
1433     if(i->textid>=0)
1434       endtext(obj);
1435     while(i->clippos)
1436         swfoutput_endclip(obj);
1437     i->pagefinished = 1;
1438 }
1439
1440 void swfoutput_pagefeed(struct swfoutput*obj)
1441 {
1442     swfoutput_internal*i = (swfoutput_internal*)obj->internal;
1443     
1444     if(!i->pagefinished)
1445         endpage(obj);
1446
1447     if(config_insertstoptag) {
1448         ActionTAG*atag=0;
1449         atag = action_Stop(atag);
1450         atag = action_End(atag);
1451         i->tag = swf_InsertTag(i->tag,ST_DOACTION);
1452         swf_ActionSet(i->tag,atag);
1453     }
1454     i->tag = swf_InsertTag(i->tag,ST_SHOWFRAME);
1455     i->frameno ++;
1456     
1457     for(i->depth--;i->depth>=i->startdepth;i->depth--) {
1458         i->tag = swf_InsertTag(i->tag,ST_REMOVEOBJECT2);
1459         swf_SetU16(i->tag,i->depth);
1460     }
1461     i->depth = i->startdepth;
1462 }
1463
1464 static void setBackground(struct swfoutput*obj, int x1, int y1, int x2, int y2)
1465 {
1466     swfoutput_internal*i = (swfoutput_internal*)obj->internal;
1467     RGBA rgb;
1468     rgb.a = rgb.r = rgb.g = rgb.b = 0xff;
1469     SRECT r;
1470     SHAPE* s;
1471     int ls1=0,fs1=0;
1472     int shapeid = ++i->currentswfid;
1473     r.xmin = x1;
1474     r.ymin = y1;
1475     r.xmax = x2;
1476     r.ymax = y2;
1477     i->tag = swf_InsertTag(i->tag, ST_DEFINESHAPE);
1478     swf_ShapeNew(&s);
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);
1489     swf_ShapeFree(s);
1490     i->tag = swf_InsertTag(i->tag, ST_PLACEOBJECT2);
1491     swf_ObjectPlace(i->tag,shapeid,i->depth++,0,0,0);
1492     i->tag = swf_InsertTag(i->tag, ST_PLACEOBJECT2);
1493     swf_ObjectPlaceClip(i->tag,shapeid,i->depth++,0,0,0,65535);
1494     i->cliptag = i->tag;
1495 }
1496
1497 void swfoutput_newpage(struct swfoutput*obj, int pageNum, int movex, int movey, int x1, int y1, int x2, int y2)
1498 {
1499     swfoutput_internal*i = (swfoutput_internal*)obj->internal;
1500     if(!i->firstpage && !i->pagefinished)
1501         endpage(obj);
1502
1503     swf_GetMatrix(0, &i->page_matrix);
1504     i->page_matrix.tx = movex*20;
1505     i->page_matrix.ty = movey*20;
1506
1507     if(i->cliptag && i->frameno == i->lastframeno) {
1508         SWFPLACEOBJECT obj;
1509         swf_GetPlaceObject(i->cliptag, &obj);
1510         obj.clipdepth = i->depth++;
1511         swf_ResetTag(i->cliptag, i->cliptag->id);
1512         swf_SetPlaceObject(i->cliptag, &obj);
1513         swf_PlaceObjectFree(&obj);
1514     }
1515
1516     i->min_x = x1;
1517     i->min_y = y1;
1518     i->max_x = x2;
1519     i->max_y = y2;
1520     
1521     msg("<notice> processing page %d (%dx%d:%d:%d)", pageNum,x2-x1,y2-y1, x1, y1);
1522
1523     x1*=20;y1*=20;x2*=20;y2*=20;
1524
1525     /* set clipping/background rectangle */
1526     /* TODO: this should all be done in SWFOutputDev */
1527     setBackground(obj, x1, y1, x2, y2);
1528
1529     /* increase SWF's bounding box */
1530     SRECT r;
1531     r.xmin = x1;
1532     r.ymin = y1;
1533     r.xmax = x2;
1534     r.ymax = y2;
1535     swf_ExpandRect2(&i->swf.movieSize, &r);
1536
1537     i->lastframeno = i->frameno;
1538     i->firstpage = 0;
1539     i->pagefinished = 0;
1540 }
1541
1542 /* initialize the swf writer */
1543 void swfoutput_init(struct swfoutput* obj)
1544 {
1545     memset(obj, 0, sizeof(struct swfoutput));
1546     obj->internal = init_internal_struct();
1547
1548     swfoutput_internal*i = (swfoutput_internal*)obj->internal;
1549
1550     SRECT r;
1551     RGBA rgb;
1552
1553     msg("<verbose> initializing swf output for size %d*%d\n", i->max_x,i->max_y);
1554
1555     obj->swffont = 0;
1556     obj->drawmode = -1;
1557     
1558     memset(&i->swf,0x00,sizeof(SWF));
1559
1560     i->swf.fileVersion    = config_flashversion;
1561     i->swf.frameRate      = 0x0040; // 1 frame per 4 seconds
1562     i->swf.movieSize.xmin = 0;
1563     i->swf.movieSize.ymin = 0;
1564     i->swf.movieSize.xmax = 0;
1565     i->swf.movieSize.ymax = 0;
1566     
1567     i->swf.firstTag = swf_InsertTag(NULL,ST_SETBACKGROUNDCOLOR);
1568     i->tag = i->swf.firstTag;
1569     rgb.a = rgb.r = rgb.g = rgb.b = 0xff;
1570     swf_SetRGB(i->tag,&rgb);
1571
1572     i->startdepth = i->depth = 3; /* leave room for clip and background rectangle */
1573     
1574     if(config_protect)
1575       i->tag = swf_InsertTag(i->tag, ST_PROTECT);
1576 }
1577
1578 static void startshape(struct swfoutput*obj)
1579 {
1580     swfoutput_internal*i = (swfoutput_internal*)obj->internal;
1581     RGBA rgb;
1582     SRECT r;
1583
1584     if(i->textid>=0)
1585         endtext(obj);
1586
1587     i->tag = swf_InsertTag(i->tag,ST_DEFINESHAPE);
1588
1589     swf_ShapeNew(&i->shape);
1590     i->linestyleid = swf_ShapeAddLineStyle(i->shape,i->linewidth,&obj->strokergb);
1591     rgb.r = obj->fillrgb.r;
1592     rgb.g = obj->fillrgb.g;
1593     rgb.b = obj->fillrgb.b;
1594     i->fillstyleid = swf_ShapeAddSolidFillStyle(i->shape,&obj->fillrgb);
1595
1596     i->shapeid = ++i->currentswfid;
1597     swf_SetU16(i->tag,i->shapeid);  // ID
1598
1599     i->bboxrectpos = i->tag->len;
1600     r.xmin = 0;
1601     r.ymin = 0;
1602     r.xmax = 20*i->max_x;
1603     r.ymax = 20*i->max_y;
1604     swf_SetRect(i->tag,&r);
1605    
1606     memset(&i->bboxrect, 0, sizeof(i->bboxrect));
1607
1608     swf_SetShapeStyles(i->tag,i->shape);
1609     swf_ShapeCountBits(i->shape,NULL,NULL);
1610     swf_SetShapeBits(i->tag,i->shape);
1611
1612     /* TODO: do we really need this? */
1613     swf_ShapeSetAll(i->tag,i->shape,/*x*/0,/*y*/0,i->linestyleid,0,0);
1614     i->swflastx=i->swflasty=0;
1615     i->lastwasfill = 0;
1616     i->shapeisempty = 1;
1617 }
1618
1619 static void starttext(struct swfoutput*obj)
1620 {
1621     swfoutput_internal*i = (swfoutput_internal*)obj->internal;
1622     if(i->shapeid>=0)
1623         endshape(obj,0);
1624       
1625     i->textid = ++i->currentswfid;
1626
1627     i->swflastx=i->swflasty=0;
1628 }
1629             
1630
1631 /* TODO: move to ../lib/rfxswf */
1632 void changeRect(struct swfoutput*obj, TAG*tag, int pos, SRECT*newrect)
1633 {
1634     swfoutput_internal*i = (swfoutput_internal*)obj->internal;
1635     /* determine length of old rect */
1636     tag->pos = pos;
1637     tag->readBit = 0;
1638     SRECT old;
1639     swf_GetRect(tag, &old);
1640     swf_ResetReadBits(tag);
1641     int pos_end = tag->pos;
1642
1643     int len = tag->len - pos_end;
1644     U8*data = (U8*)malloc(len);
1645     memcpy(data, &tag->data[pos_end], len);
1646     tag->writeBit = 0;
1647     tag->len = pos;
1648     swf_SetRect(tag, newrect);
1649     swf_SetBlock(tag, data, len);
1650     free(data);
1651     tag->pos = tag->readBit = 0;
1652 }
1653
1654 void cancelshape(swfoutput*obj)
1655 {
1656     swfoutput_internal*i = (swfoutput_internal*)obj->internal;
1657     /* delete old shape tag */
1658     TAG*todel = i->tag;
1659     i->tag = i->tag->prev;
1660     swf_DeleteTag(todel);
1661     if(i->shape) {swf_ShapeFree(i->shape);i->shape=0;}
1662     i->shapeid = -1;
1663     i->bboxrectpos = -1;
1664 }
1665
1666 void fixAreas(swfoutput*obj)
1667 {
1668     swfoutput_internal*i = (swfoutput_internal*)obj->internal;
1669     if(!i->shapeisempty && i->fill &&
1670        (i->bboxrect.xmin == i->bboxrect.xmax ||
1671         i->bboxrect.ymin == i->bboxrect.ymax) &&
1672         config_minlinewidth >= 0.001
1673        ) {
1674         msg("<debug> Shape has size 0: width=%.2f height=%.2f",
1675                 (i->bboxrect.xmax-i->bboxrect.xmin)/20.0,
1676                 (i->bboxrect.ymax-i->bboxrect.ymin)/20.0
1677                 );
1678     
1679         SRECT r = i->bboxrect;
1680         
1681         if(r.xmin == r.xmax && r.ymin == r.ymax) {
1682             /* this thing comes down to a single dot- nothing to fix here */
1683             return;
1684         }
1685
1686         cancelshape(obj);
1687
1688         RGBA save_col = obj->strokergb;
1689         int  save_width = i->linewidth;
1690
1691         obj->strokergb = obj->fillrgb;
1692         i->linewidth = (int)(config_minlinewidth*20);
1693         if(i->linewidth==0) i->linewidth = 1;
1694         
1695         startshape(obj);
1696
1697         moveto(obj, i->tag, r.xmin/20.0,r.ymin/20.0);
1698         lineto(obj, i->tag, r.xmax/20.0,r.ymax/20.0);
1699
1700         obj->strokergb = save_col;
1701         i->linewidth = save_width;
1702     }
1703     
1704 }
1705
1706 static void endshape_noput(swfoutput*obj)
1707 {
1708     swfoutput_internal*i = (swfoutput_internal*)obj->internal;
1709     if(i->shapeid<0) 
1710         return;
1711     //changeRect(obj, i->tag, i->bboxrectpos, &i->bboxrect);
1712     i->shapeid = -1;
1713     if(i->shape) {
1714         swf_ShapeFree(i->shape);
1715         i->shape=0;
1716     }
1717 }
1718
1719 static void endshape(swfoutput*obj, int clipdepth)
1720 {
1721     swfoutput_internal*i = (swfoutput_internal*)obj->internal;
1722     if(i->shapeid<0) 
1723         return;
1724
1725     if(!clipdepth)
1726         fixAreas(obj);
1727         
1728     if(i->shapeisempty ||
1729        /*bbox empty?*/
1730        (i->bboxrect.xmin == i->bboxrect.xmax && 
1731         i->bboxrect.ymin == i->bboxrect.ymax) ||
1732        /*bbox outside page?*/
1733        (i->bboxrect.xmax <= i->min_x*20 ||
1734         i->bboxrect.ymax <= i->min_y*20 ||
1735         i->bboxrect.xmin >= i->max_x*20 ||
1736         i->bboxrect.ymin >= i->max_y*20)
1737        )
1738     {
1739         // delete the shape again, we didn't do anything
1740         cancelshape(obj);
1741         return;
1742     }
1743     
1744     swf_ShapeSetEnd(i->tag);
1745
1746     changeRect(obj, i->tag, i->bboxrectpos, &i->bboxrect);
1747
1748     i->tag = swf_InsertTag(i->tag,ST_PLACEOBJECT2);
1749
1750     if(clipdepth)
1751         swf_ObjectPlaceClip(i->tag,i->shapeid,i->depth++,&i->page_matrix,NULL,NULL,clipdepth);
1752     else
1753         swf_ObjectPlace(i->tag,i->shapeid,/*depth*/i->depth++,&i->page_matrix,NULL,NULL);
1754
1755     swf_ShapeFree(i->shape);
1756     i->shape = 0;
1757     i->shapeid = -1;
1758     i->bboxrectpos = -1;
1759 }
1760
1761 void swfoutput_finalize(struct swfoutput*obj)
1762 {
1763     swfoutput_internal*i = (swfoutput_internal*)obj->internal;
1764
1765     if(i->tag && i->tag->id == ST_END)
1766         return; //already done
1767
1768     if(i->frameno == i->lastframeno) // fix: add missing pagefeed
1769         swfoutput_pagefeed(obj);
1770
1771     endpage(obj);
1772     fontlist_t *tmp,*iterator = i->fontlist;
1773     while(iterator) {
1774         TAG*mtag = i->swf.firstTag;
1775         if(iterator->swffont) {
1776             mtag = swf_InsertTag(mtag, ST_DEFINEFONT2);
1777             /*if(!storeallcharacters)
1778                 swf_FontReduce(iterator->swffont);*/
1779             swf_FontSetDefine2(mtag, iterator->swffont);
1780         }
1781
1782         iterator = iterator->next;
1783     }
1784     i->tag = swf_InsertTag(i->tag,ST_END);
1785     TAG* tag = i->tag->prev;
1786
1787     /* remove the removeobject2 tags between the last ST_SHOWFRAME
1788        and the ST_END- they confuse the flash player  */
1789     while(tag->id == ST_REMOVEOBJECT2) {
1790         TAG* prev = tag->prev;
1791         swf_DeleteTag(tag);
1792         tag = prev;
1793     }
1794 }
1795
1796 SWF* swfoutput_get(struct swfoutput*obj)
1797 {
1798     swfoutput_internal*i = (swfoutput_internal*)obj->internal;
1799
1800     swfoutput_finalize(obj);
1801
1802     return swf_CopySWF(&i->swf);
1803 }
1804
1805 void swfoutput_getdimensions(struct swfoutput*obj, int*x1, int*y1, int*x2, int*y2)
1806 {
1807     swfoutput_internal*i = (swfoutput_internal*)obj->internal;
1808     if(x1) *x1 = i->swf.movieSize.xmin/20;
1809     if(y1) *y1 = i->swf.movieSize.ymin/20;
1810     if(x2) *x2 = i->swf.movieSize.xmax/20;
1811     if(y2) *y2 = i->swf.movieSize.ymax/20;
1812 }
1813
1814 int swfoutput_save(struct swfoutput* obj, char*filename) 
1815 {
1816     swfoutput_internal*i = (swfoutput_internal*)obj->internal;
1817     swfoutput_finalize(obj);
1818
1819     int fi;
1820     if(filename)
1821      fi = open(filename, O_BINARY|O_CREAT|O_TRUNC|O_WRONLY, 0777);
1822     else
1823      fi = 1; // stdout
1824     
1825     if(fi<=0) {
1826      msg("<fatal> Could not create \"%s\". ", FIXNULL(filename));
1827      return 0;
1828     }
1829     
1830     if(config_enablezlib || config_flashversion>=6) {
1831       if FAILED(swf_WriteSWC(fi,&i->swf)) 
1832        msg("<error> WriteSWC() failed.\n");
1833     } else {
1834       if FAILED(swf_WriteSWF(fi,&i->swf)) 
1835        msg("<error> WriteSWF() failed.\n");
1836     }
1837
1838     if(filename)
1839      close(fi);
1840     msg("<notice> SWF written\n");
1841     return 1;
1842 }
1843
1844 /* Perform cleaning up, complete the swf, and write it out. */
1845 void swfoutput_destroy(struct swfoutput* obj) 
1846 {
1847     swfoutput_internal*i = (swfoutput_internal*)obj->internal;
1848     if(!i) {
1849         /* not initialized yet- nothing to destroy */
1850         return;
1851     }
1852
1853     fontlist_t *tmp,*iterator = i->fontlist;
1854     while(iterator) {
1855         if(iterator->swffont) {
1856             swf_FontFree(iterator->swffont);iterator->swffont=0;
1857         }
1858         tmp = iterator;
1859         iterator = iterator->next;
1860         delete tmp;
1861     }
1862     swf_FreeTags(&i->swf);
1863
1864     free(i);i=0;
1865     memset(obj, 0, sizeof(swfoutput));
1866 }
1867
1868 void swfoutput_setdrawmode(swfoutput* obj, int mode)
1869 {
1870     swfoutput_internal*i = (swfoutput_internal*)obj->internal;
1871     obj->drawmode = mode;
1872     if(mode == DRAWMODE_FILL)
1873      i->fill = 1;
1874     else if(mode == DRAWMODE_EOFILL)
1875      i->fill = 1;
1876     else if(mode == DRAWMODE_STROKE)
1877      i->fill = 0;
1878     else if(mode == DRAWMODE_CLIP)
1879      i->fill = 1;
1880     else if(mode == DRAWMODE_EOCLIP)
1881      i->fill = 1;
1882 }
1883
1884 void swfoutput_setfillcolor(swfoutput* obj, U8 r, U8 g, U8 b, U8 a)
1885 {
1886     swfoutput_internal*i = (swfoutput_internal*)obj->internal;
1887     if(obj->fillrgb.r == r &&
1888        obj->fillrgb.g == g &&
1889        obj->fillrgb.b == b &&
1890        obj->fillrgb.a == a) return;
1891     if(i->shapeid>=0)
1892      endshape(obj,0);
1893
1894     obj->fillrgb.r = r;
1895     obj->fillrgb.g = g;
1896     obj->fillrgb.b = b;
1897     obj->fillrgb.a = a;
1898 }
1899
1900 void swfoutput_setstrokecolor(swfoutput* obj, U8 r, U8 g, U8 b, U8 a)
1901 {
1902     swfoutput_internal*i = (swfoutput_internal*)obj->internal;
1903     if(obj->strokergb.r == r &&
1904        obj->strokergb.g == g &&
1905        obj->strokergb.b == b &&
1906        obj->strokergb.a == a) return;
1907
1908     if(i->shapeid>=0)
1909      endshape(obj,0);
1910     obj->strokergb.r = r;
1911     obj->strokergb.g = g;
1912     obj->strokergb.b = b;
1913     obj->strokergb.a = a;
1914 }
1915
1916 void swfoutput_setlinewidth(struct swfoutput*obj, double _linewidth)
1917 {
1918     swfoutput_internal*i = (swfoutput_internal*)obj->internal;
1919     if(i->linewidth == (U16)(_linewidth*20))
1920         return;
1921
1922     if(i->shapeid>=0)
1923         endshape(obj,0);
1924     i->linewidth = (U16)(_linewidth*20);
1925 }
1926
1927
1928 void swfoutput_startclip(swfoutput*obj, SWF_OUTLINE*outline, struct swfmatrix*m)
1929 {
1930     swfoutput_internal*i = (swfoutput_internal*)obj->internal;
1931     if(i->textid>=0)
1932      endtext(obj);
1933     if(i->shapeid>=0)
1934      endshape(obj,0);
1935
1936     if(i->clippos >= 127)
1937     {
1938         msg("<warning> Too many clip levels.");
1939         i->clippos --;
1940     } 
1941     
1942     startshape(obj);
1943     int olddrawmode = obj->drawmode;
1944     swfoutput_setdrawmode(obj, DRAWMODE_CLIP);
1945     swfoutput_drawpath(obj, outline, m);
1946     swf_ShapeSetEnd(i->tag);
1947     swfoutput_setdrawmode(obj, olddrawmode);
1948
1949     i->tag = swf_InsertTag(i->tag,ST_PLACEOBJECT2);
1950     i->cliptags[i->clippos] = i->tag;
1951     i->clipshapes[i->clippos] = i->shapeid;
1952     i->clipdepths[i->clippos] = i->depth++;
1953     i->clippos++;
1954     
1955     endshape_noput(obj);
1956 }
1957
1958 void swfoutput_endclip(swfoutput*obj)
1959 {
1960     swfoutput_internal*i = (swfoutput_internal*)obj->internal;
1961     if(i->textid>=0)
1962         endtext(obj);
1963     if(i->shapeid>=0)
1964         endshape(obj,0);
1965
1966     if(!i->clippos) {
1967         msg("<error> Invalid end of clipping region");
1968         return;
1969     }
1970     i->clippos--;
1971     swf_ObjectPlaceClip(i->cliptags[i->clippos],i->clipshapes[i->clippos],i->clipdepths[i->clippos],&i->page_matrix,NULL,NULL,i->depth++);
1972 }
1973
1974 static void drawlink(struct swfoutput*obj, ActionTAG*,ActionTAG*, swfcoord*points, char mouseover);
1975
1976 void swfoutput_linktourl(struct swfoutput*obj, char*url, swfcoord*points)
1977 {
1978     swfoutput_internal*i = (swfoutput_internal*)obj->internal;
1979     ActionTAG* actions;
1980     if(!strncmp("http://pdf2swf:", url, 15)) {
1981      char*tmp = strdup(url);
1982      int l = strlen(tmp);
1983      if(tmp[l-1] == '/')
1984         tmp[l-1] = 0;
1985      swfoutput_namedlink(obj, tmp+15, points);
1986      free(tmp);
1987      return;
1988     }
1989     
1990     if(i->shapeid>=0)
1991         endshape(obj,0);
1992     if(i->textid>=0)
1993         endtext(obj);
1994     
1995     if(config_opennewwindow)
1996       actions = action_GetUrl(0, url, "_parent");
1997     else
1998       actions = action_GetUrl(0, url, "_this");
1999     actions = action_End(actions);
2000     
2001     drawlink(obj, actions, 0, points,0);
2002 }
2003 void swfoutput_linktopage(struct swfoutput*obj, int page, swfcoord*points)
2004 {
2005     swfoutput_internal*i = (swfoutput_internal*)obj->internal;
2006     ActionTAG* actions;
2007
2008     if(i->shapeid>=0)
2009         endshape(obj,0);
2010     if(i->textid>=0)
2011         endtext(obj);
2012    
2013       actions = action_GotoFrame(0, page);
2014       actions = action_End(actions);
2015
2016     drawlink(obj, actions, 0, points,0);
2017 }
2018
2019 /* Named Links (a.k.a. Acrobatmenu) are used to implement various gadgets
2020    of the viewer objects, like subtitles, index elements etc.
2021 */
2022 void swfoutput_namedlink(struct swfoutput*obj, char*name, swfcoord*points)
2023 {
2024     swfoutput_internal*i = (swfoutput_internal*)obj->internal;
2025     ActionTAG *actions1,*actions2;
2026     char*tmp = strdup(name);
2027     char mouseover = 1;
2028
2029     if(i->shapeid>=0)
2030         endshape(obj,0);
2031     if(i->textid>=0)
2032         endtext(obj);
2033
2034     if(!strncmp(tmp, "call:", 5))
2035     {
2036         char*x = strchr(&tmp[5], ':');
2037         if(!x) {
2038             actions1 = action_PushInt(0, 0); //number of parameters (0)
2039             actions1 = action_PushString(actions1, &tmp[5]); //function name
2040             actions1 = action_CallFunction(actions1);
2041         } else {
2042             *x = 0;
2043             actions1 = action_PushString(0, x+1); //parameter
2044             actions1 = action_PushInt(actions1, 1); //number of parameters (1)
2045             actions1 = action_PushString(actions1, &tmp[5]); //function name
2046             actions1 = action_CallFunction(actions1);
2047         }
2048         actions2 = action_End(0);
2049         mouseover = 0;
2050     }
2051     else
2052     {
2053         actions1 = action_PushString(0, "/:subtitle");
2054         actions1 = action_PushString(actions1, name);
2055         actions1 = action_SetVariable(actions1);
2056         actions1 = action_End(actions1);
2057
2058         actions2 = action_PushString(0, "/:subtitle");
2059         actions2 = action_PushString(actions2, "");
2060         actions2 = action_SetVariable(actions2);
2061         actions2 = action_End(actions2);
2062     }
2063
2064     drawlink(obj, actions1, actions2, points,mouseover);
2065
2066     swf_ActionFree(actions1);
2067     swf_ActionFree(actions2);
2068     free(tmp);
2069 }
2070
2071 static void drawlink(struct swfoutput*obj, ActionTAG*actions1, ActionTAG*actions2, swfcoord*points, char mouseover)
2072 {
2073     swfoutput_internal*i = (swfoutput_internal*)obj->internal;
2074     RGBA rgb;
2075     SRECT r;
2076     int lsid=0;
2077     int fsid;
2078     struct plotxy p1,p2,p3,p4;
2079     int myshapeid;
2080     int myshapeid2;
2081     double xmin,ymin;
2082     double xmax=xmin=points[0].x,ymax=ymin=points[0].y;
2083     double posx = 0;
2084     double posy = 0;
2085     int t;
2086     int buttonid = ++i->currentswfid;
2087     for(t=1;t<4;t++)
2088     {
2089         if(points[t].x>xmax) xmax=points[t].x;
2090         if(points[t].y>ymax) ymax=points[t].y;
2091         if(points[t].x<xmin) xmin=points[t].x;
2092         if(points[t].y<ymin) ymin=points[t].y;
2093     }
2094    
2095     p1.x=points[0].x; p1.y=points[0].y; p2.x=points[1].x; p2.y=points[1].y; 
2096     p3.x=points[2].x; p3.y=points[2].y; p4.x=points[3].x; p4.y=points[3].y;
2097    
2098     /* the following code subtracts the upper left edge from all coordinates,
2099        and set's posx,posy so that ST_PLACEOBJECT is used with a matrix.
2100        Necessary for preprocessing with swfcombine. */
2101     posx = xmin; posy = ymin;
2102     p1.x-=posx;p2.x-=posx;p3.x-=posx;p4.x-=posx;
2103     p1.y-=posy;p2.y-=posy;p3.y-=posy;p4.y-=posy;
2104     xmin -= posx; ymin -= posy;
2105     xmax -= posx; ymax -= posy;
2106     
2107     /* shape */
2108     myshapeid = ++i->currentswfid;
2109     i->tag = swf_InsertTag(i->tag,ST_DEFINESHAPE3);
2110     swf_ShapeNew(&i->shape);
2111     rgb.r = rgb.b = rgb.a = rgb.g = 0; 
2112     fsid = swf_ShapeAddSolidFillStyle(i->shape,&rgb);
2113     swf_SetU16(i->tag, myshapeid);
2114     r.xmin = (int)(xmin*20);
2115     r.ymin = (int)(ymin*20);
2116     r.xmax = (int)(xmax*20);
2117     r.ymax = (int)(ymax*20);
2118     swf_SetRect(i->tag,&r);
2119     swf_SetShapeStyles(i->tag,i->shape);
2120     swf_ShapeCountBits(i->shape,NULL,NULL);
2121     swf_SetShapeBits(i->tag,i->shape);
2122     swf_ShapeSetAll(i->tag,i->shape,/*x*/0,/*y*/0,0,fsid,0);
2123     i->swflastx = i->swflasty = 0;
2124     moveto(obj, i->tag, p1);
2125     lineto(obj, i->tag, p2);
2126     lineto(obj, i->tag, p3);
2127     lineto(obj, i->tag, p4);
2128     lineto(obj, i->tag, p1);
2129     swf_ShapeSetEnd(i->tag);
2130
2131     /* shape2 */
2132     myshapeid2 = ++i->currentswfid;
2133     i->tag = swf_InsertTag(i->tag,ST_DEFINESHAPE3);
2134     swf_ShapeNew(&i->shape);
2135     rgb.r = rgb.b = rgb.a = rgb.g = 255;
2136     rgb.a = 40;
2137     fsid = swf_ShapeAddSolidFillStyle(i->shape,&rgb);
2138     swf_SetU16(i->tag, myshapeid2);
2139     r.xmin = (int)(xmin*20);
2140     r.ymin = (int)(ymin*20);
2141     r.xmax = (int)(xmax*20);
2142     r.ymax = (int)(ymax*20);
2143     swf_SetRect(i->tag,&r);
2144     swf_SetShapeStyles(i->tag,i->shape);
2145     swf_ShapeCountBits(i->shape,NULL,NULL);
2146     swf_SetShapeBits(i->tag,i->shape);
2147     swf_ShapeSetAll(i->tag,i->shape,/*x*/0,/*y*/0,0,fsid,0);
2148     i->swflastx = i->swflasty = 0;
2149     moveto(obj, i->tag, p1);
2150     lineto(obj, i->tag, p2);
2151     lineto(obj, i->tag, p3);
2152     lineto(obj, i->tag, p4);
2153     lineto(obj, i->tag, p1);
2154     swf_ShapeSetEnd(i->tag);
2155
2156     if(!mouseover)
2157     {
2158         i->tag = swf_InsertTag(i->tag,ST_DEFINEBUTTON);
2159         swf_SetU16(i->tag,buttonid); //id
2160         swf_ButtonSetFlags(i->tag, 0); //menu=no
2161         swf_ButtonSetRecord(i->tag,0x01,myshapeid,i->depth,0,0);
2162         swf_ButtonSetRecord(i->tag,0x02,myshapeid2,i->depth,0,0);
2163         swf_ButtonSetRecord(i->tag,0x04,myshapeid2,i->depth,0,0);
2164         swf_ButtonSetRecord(i->tag,0x08,myshapeid,i->depth,0,0);
2165         swf_SetU8(i->tag,0);
2166         swf_ActionSet(i->tag,actions1);
2167         swf_SetU8(i->tag,0);
2168     }
2169     else
2170     {
2171         i->tag = swf_InsertTag(i->tag,ST_DEFINEBUTTON2);
2172         swf_SetU16(i->tag,buttonid); //id
2173         swf_ButtonSetFlags(i->tag, 0); //menu=no
2174         swf_ButtonSetRecord(i->tag,0x01,myshapeid,i->depth,0,0);
2175         swf_ButtonSetRecord(i->tag,0x02,myshapeid2,i->depth,0,0);
2176         swf_ButtonSetRecord(i->tag,0x04,myshapeid2,i->depth,0,0);
2177         swf_ButtonSetRecord(i->tag,0x08,myshapeid,i->depth,0,0);
2178         swf_SetU8(i->tag,0); // end of button records
2179         swf_ButtonSetCondition(i->tag, BC_IDLE_OVERUP);
2180         swf_ActionSet(i->tag,actions1);
2181         if(actions2) {
2182             swf_ButtonSetCondition(i->tag, BC_OVERUP_IDLE);
2183             swf_ActionSet(i->tag,actions2);
2184             swf_SetU8(i->tag,0);
2185             swf_ButtonPostProcess(i->tag, 2);
2186         } else {
2187             swf_SetU8(i->tag,0);
2188             swf_ButtonPostProcess(i->tag, 1);
2189         }
2190     }
2191     
2192     i->tag = swf_InsertTag(i->tag,ST_PLACEOBJECT2);
2193
2194     if(posx!=0 || posy!=0) {
2195         SPOINT p;
2196         p.x = (int)(posx*20);
2197         p.y = (int)(posy*20);
2198         p = swf_TurnPoint(p, &i->page_matrix);
2199         MATRIX m;
2200         m = i->page_matrix;
2201         m.tx = p.x;
2202         m.ty = p.y;
2203         swf_ObjectPlace(i->tag, buttonid, i->depth++,&m,0,0);
2204     }
2205     else {
2206         swf_ObjectPlace(i->tag, buttonid, i->depth++,&i->page_matrix,0,0);
2207     }
2208 }
2209
2210 static void drawimage(struct swfoutput*obj, int bitid, int sizex,int sizey, 
2211         double x1,double y1,
2212         double x2,double y2,
2213         double x3,double y3,
2214         double x4,double y4)
2215 {
2216     swfoutput_internal*i = (swfoutput_internal*)obj->internal;
2217     RGBA rgb;
2218     SRECT r;
2219     int lsid=0;
2220     int fsid;
2221     struct plotxy p1,p2,p3,p4;
2222     int myshapeid;
2223     double xmax=x1,ymax=y1,xmin=x1,ymin=y1;
2224     if(x2>xmax) xmax=x2;
2225     if(y2>ymax) ymax=y2;
2226     if(x2<xmin) xmin=x2;
2227     if(y2<ymin) ymin=y2;
2228     if(x3>xmax) xmax=x3;
2229     if(y3>ymax) ymax=y3;
2230     if(x3<xmin) xmin=x3;
2231     if(y3<ymin) ymin=y3;
2232     if(x4>xmax) xmax=x4;
2233     if(y4>ymax) ymax=y4;
2234     if(x4<xmin) xmin=x4;
2235     if(y4<ymin) ymin=y4;
2236     p1.x=x1; p1.y=y1;
2237     p2.x=x2; p2.y=y2;
2238     p3.x=x3; p3.y=y3;
2239     p4.x=x4; p4.y=y4;
2240
2241     {p1.x = (int)(p1.x*20)/20.0;
2242      p1.y = (int)(p1.y*20)/20.0;
2243      p2.x = (int)(p2.x*20)/20.0;
2244      p2.y = (int)(p2.y*20)/20.0;
2245      p3.x = (int)(p3.x*20)/20.0;
2246      p3.y = (int)(p3.y*20)/20.0;
2247      p4.x = (int)(p4.x*20)/20.0;
2248      p4.y = (int)(p4.y*20)/20.0;}
2249     
2250     MATRIX m;
2251     m.sx = (int)(65536*20*(p4.x-p1.x)/sizex);
2252     m.r1 = -(int)(65536*20*(p4.y-p1.y)/sizex);
2253     m.r0 = (int)(65536*20*(p1.x-p2.x)/sizey);
2254     m.sy = -(int)(65536*20*(p1.y-p2.y)/sizey);
2255
2256     m.tx = (int)(p1.x*20);
2257     m.ty = (int)(p1.y*20);
2258   
2259     /* shape */
2260     myshapeid = ++i->currentswfid;
2261     i->tag = swf_InsertTag(i->tag,ST_DEFINESHAPE);
2262     SHAPE*shape;
2263     swf_ShapeNew(&shape);
2264     //lsid = ShapeAddLineStyle(shape,linewidth,&obj->strokergb);
2265     //fsid = ShapeAddSolidFillStyle(shape,&obj->fillrgb);
2266     fsid = swf_ShapeAddBitmapFillStyle(shape,&m,bitid,1);
2267     swf_SetU16(i->tag, myshapeid);
2268     r.xmin = (int)(xmin*20);
2269     r.ymin = (int)(ymin*20);
2270     r.xmax = (int)(xmax*20);
2271     r.ymax = (int)(ymax*20);
2272     swf_SetRect(i->tag,&r);
2273     swf_SetShapeStyles(i->tag,shape);
2274     swf_ShapeCountBits(shape,NULL,NULL);
2275     swf_SetShapeBits(i->tag,shape);
2276     swf_ShapeSetAll(i->tag,shape,/*x*/0,/*y*/0,lsid,fsid,0);
2277     i->swflastx = i->swflasty = 0;
2278     moveto(obj, i->tag, p1);
2279     lineto(obj, i->tag, p2);
2280     lineto(obj, i->tag, p3);
2281     lineto(obj, i->tag, p4);
2282     lineto(obj, i->tag, p1);
2283     /*
2284     ShapeMoveTo  (tag, shape, (int)(x1*20),(int)(y1*20));
2285     ShapeSetLine (tag, shape, (int)(x1*20);
2286     ShapeSetLine (tag, shape, x*20,0);
2287     ShapeSetLine (tag, shape, 0,-y*20);
2288     ShapeSetLine (tag, shape, -x*20,0);*/
2289     swf_ShapeSetEnd(i->tag);
2290     swf_ShapeFree(shape);
2291
2292     /* instance */
2293     i->tag = swf_InsertTag(i->tag,ST_PLACEOBJECT2);
2294
2295     swf_ObjectPlace(i->tag,myshapeid,/*depth*/i->depth++,&i->page_matrix,NULL,NULL);
2296 }
2297
2298 int swfoutput_drawimagejpeg_old(struct swfoutput*obj, char*filename, int sizex,int sizey, 
2299         double x1,double y1,
2300         double x2,double y2,
2301         double x3,double y3,
2302         double x4,double y4)
2303 {
2304     swfoutput_internal*i = (swfoutput_internal*)obj->internal;
2305     TAG*oldtag;
2306     if(i->shapeid>=0)
2307         endshape(obj,0);
2308     if(i->textid>=0)
2309         endtext(obj);
2310
2311     int bitid = ++i->currentswfid;
2312     oldtag = i->tag;
2313     i->tag = swf_InsertTag(i->tag,ST_DEFINEBITSJPEG2);
2314     swf_SetU16(i->tag, bitid);
2315     if(swf_SetJPEGBits(i->tag, filename, config_jpegquality)<0) {
2316         swf_DeleteTag(i->tag);
2317         i->tag = oldtag;
2318         return -1;
2319     }
2320
2321     drawimage(obj, bitid, sizex, sizey, x1,y1,x2,y2,x3,y3,x4,y4);
2322     return bitid;
2323 }
2324
2325 int swfoutput_drawimagejpeg(struct swfoutput*obj, RGBA*mem, int sizex,int sizey, 
2326         double x1,double y1,
2327         double x2,double y2,
2328         double x3,double y3,
2329         double x4,double y4)
2330 {
2331     swfoutput_internal*i = (swfoutput_internal*)obj->internal;
2332     TAG*oldtag;
2333     JPEGBITS*jpeg;
2334
2335     if(i->shapeid>=0)
2336         endshape(obj,0);
2337     if(i->textid>=0)
2338         endtext(obj);
2339
2340     int bitid = ++i->currentswfid;
2341     oldtag = i->tag;
2342     i->tag = swf_InsertTag(i->tag,ST_DEFINEBITSJPEG2);
2343     swf_SetU16(i->tag, bitid);
2344     swf_SetJPEGBits2(i->tag,sizex,sizey,mem,config_jpegquality);
2345     drawimage(obj, bitid, sizex, sizey, x1,y1,x2,y2,x3,y3,x4,y4);
2346     return bitid;
2347 }
2348
2349 int swfoutput_drawimagelossless(struct swfoutput*obj, RGBA*mem, int sizex,int sizey, 
2350         double x1,double y1,
2351         double x2,double y2,
2352         double x3,double y3,
2353         double x4,double y4)
2354 {
2355     swfoutput_internal*i = (swfoutput_internal*)obj->internal;
2356     TAG*oldtag;
2357     if(i->shapeid>=0)
2358         endshape(obj,0);
2359     if(i->textid>=0)
2360         endtext(obj);
2361
2362     int bitid = ++i->currentswfid;
2363     oldtag = i->tag;
2364     i->tag = swf_InsertTag(i->tag,ST_DEFINEBITSLOSSLESS);
2365     swf_SetU16(i->tag, bitid);
2366     if(swf_SetLosslessBits(i->tag,sizex,sizey,mem, BMF_32BIT)<0) {
2367         swf_DeleteTag(i->tag);
2368         i->tag = oldtag;
2369         return -1;
2370     }
2371     
2372     drawimage(obj, bitid, sizex, sizey, x1,y1,x2,y2,x3,y3,x4,y4);
2373     return bitid;
2374 }
2375
2376 int swfoutput_drawimagelosslessN(struct swfoutput*obj, U8*mem, RGBA*pal, int sizex,int sizey, 
2377         double x1,double y1,
2378         double x2,double y2,
2379         double x3,double y3,
2380         double x4,double y4, int n)
2381 {
2382     swfoutput_internal*i = (swfoutput_internal*)obj->internal;
2383     TAG*oldtag;
2384     U8*mem2 = 0;
2385     if(i->shapeid>=0)
2386         endshape(obj,0);
2387     if(i->textid>=0)
2388         endtext(obj);
2389
2390     if(sizex&3)
2391     { 
2392         /* SWF expects scanlines to be 4 byte aligned */
2393         int x,y;
2394         U8*ptr;
2395         mem2 = (U8*)malloc(BYTES_PER_SCANLINE(sizex)*sizey);
2396         ptr = mem2;
2397         for(y=0;y<sizey;y++)
2398         {
2399             for(x=0;x<sizex;x++)
2400                 *ptr++ = mem[y*sizex+x];
2401             ptr+= BYTES_PER_SCANLINE(sizex)-sizex;
2402         }
2403         mem = mem2;
2404     }
2405
2406     int bitid = ++i->currentswfid;
2407     oldtag = i->tag;
2408     i->tag = swf_InsertTag(i->tag,ST_DEFINEBITSLOSSLESS2);
2409     swf_SetU16(i->tag, bitid);
2410     if(swf_SetLosslessBitsIndexed(i->tag,sizex,sizey,mem, pal, n)<0) {
2411         swf_DeleteTag(i->tag);
2412         i->tag = oldtag;
2413         return -1;
2414     }
2415     if(mem2)
2416         free(mem2);
2417   
2418     drawimage(obj, bitid, sizex, sizey, x1,y1,x2,y2,x3,y3,x4,y4);
2419     return bitid;
2420 }
2421
2422 void swfoutput_drawimageagain(struct swfoutput*obj, int id, int sizex,int sizey, 
2423         double x1,double y1,
2424         double x2,double y2,
2425         double x3,double y3,
2426         double x4,double y4)
2427 {
2428     swfoutput_internal*i = (swfoutput_internal*)obj->internal;
2429     if(id<0) return;
2430     if(i->shapeid>=0)
2431         endshape(obj,0);
2432     if(i->textid>=0)
2433         endtext(obj);
2434
2435     drawimage(obj, id, sizex, sizey, x1,y1,x2,y2,x3,y3,x4,y4);
2436 }
2437
2438 void swfoutput_setparameter(char*name, char*value)
2439 {
2440     if(!strcmp(name, "drawonlyshapes")) {
2441         config_drawonlyshapes = atoi(value);
2442     } else if(!strcmp(name, "ignoredraworder")) {
2443         config_ignoredraworder = atoi(value);
2444     } else if(!strcmp(name, "filloverlap")) {
2445         config_filloverlap = atoi(value);
2446     } else if(!strcmp(name, "linksopennewwindow")) {
2447         config_opennewwindow = atoi(value);
2448     } else if(!strcmp(name, "opennewwindow")) {
2449         config_opennewwindow = atoi(value);
2450     } else if(!strcmp(name, "storeallcharacters")) {
2451         config_storeallcharacters = atoi(value);
2452     } else if(!strcmp(name, "enablezlib")) {
2453         config_enablezlib = atoi(value);
2454     } else if(!strcmp(name, "insertstop")) {
2455         config_insertstoptag = atoi(value);
2456     } else if(!strcmp(name, "protected")) {
2457         config_protect = atoi(value);
2458     } else if(!strcmp(name, "flashversion")) {
2459         config_flashversion = atoi(value);
2460     } else if(!strcmp(name, "minlinewidth")) {
2461         config_minlinewidth = atof(value);
2462     } else if(!strcmp(name, "jpegquality")) {
2463         int val = atoi(value);
2464         if(val<0) val=0;
2465         if(val>100) val=100;
2466         config_jpegquality = val;
2467     } else if(!strcmp(name, "splinequality")) {
2468         int v = atoi(value);
2469         v = 500-(v*5); // 100% = 0.25 pixel, 0% = 25 pixel
2470         if(v<1) v = 1;
2471         config_splinemaxerror = v;
2472     } else if(!strcmp(name, "fontquality")) {
2473         int v = atoi(value);
2474         v = 500-(v*5); // 100% = 0.25 pixel, 0% = 25 pixel
2475         if(v<1) v = 1;
2476         config_fontsplinemaxerror = v;
2477     } else {
2478         fprintf(stderr, "unknown parameter: %s (=%s)\n", name, value);
2479     }
2480 }
2481