8bfd968affb26dffbc9235760b9d26ca5f0f84e2
[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     {
1733         // delete the shape again, we didn't do anything
1734         cancelshape(obj);
1735         return;
1736     }
1737     
1738     swf_ShapeSetEnd(i->tag);
1739
1740     changeRect(obj, i->tag, i->bboxrectpos, &i->bboxrect);
1741
1742     i->tag = swf_InsertTag(i->tag,ST_PLACEOBJECT2);
1743
1744     if(clipdepth)
1745         swf_ObjectPlaceClip(i->tag,i->shapeid,i->depth++,&i->page_matrix,NULL,NULL,clipdepth);
1746     else
1747         swf_ObjectPlace(i->tag,i->shapeid,/*depth*/i->depth++,&i->page_matrix,NULL,NULL);
1748
1749     swf_ShapeFree(i->shape);
1750     i->shape = 0;
1751     i->shapeid = -1;
1752     i->bboxrectpos = -1;
1753 }
1754
1755 void swfoutput_finalize(struct swfoutput*obj)
1756 {
1757     swfoutput_internal*i = (swfoutput_internal*)obj->internal;
1758
1759     if(i->tag && i->tag->id == ST_END)
1760         return; //already done
1761
1762     if(i->frameno == i->lastframeno) // fix: add missing pagefeed
1763         swfoutput_pagefeed(obj);
1764
1765     endpage(obj);
1766     fontlist_t *tmp,*iterator = i->fontlist;
1767     while(iterator) {
1768         TAG*mtag = i->swf.firstTag;
1769         if(iterator->swffont) {
1770             mtag = swf_InsertTag(mtag, ST_DEFINEFONT2);
1771             /*if(!storeallcharacters)
1772                 swf_FontReduce(iterator->swffont);*/
1773             swf_FontSetDefine2(mtag, iterator->swffont);
1774         }
1775
1776         iterator = iterator->next;
1777     }
1778     i->tag = swf_InsertTag(i->tag,ST_END);
1779     TAG* tag = i->tag->prev;
1780
1781     /* remove the removeobject2 tags between the last ST_SHOWFRAME
1782        and the ST_END- they confuse the flash player  */
1783     while(tag->id == ST_REMOVEOBJECT2) {
1784         TAG* prev = tag->prev;
1785         swf_DeleteTag(tag);
1786         tag = prev;
1787     }
1788 }
1789
1790 SWF* swfoutput_get(struct swfoutput*obj)
1791 {
1792     swfoutput_internal*i = (swfoutput_internal*)obj->internal;
1793
1794     swfoutput_finalize(obj);
1795
1796     return swf_CopySWF(&i->swf);
1797 }
1798
1799 void swfoutput_getdimensions(struct swfoutput*obj, int*x1, int*y1, int*x2, int*y2)
1800 {
1801     swfoutput_internal*i = (swfoutput_internal*)obj->internal;
1802     if(x1) *x1 = i->swf.movieSize.xmin/20;
1803     if(y1) *y1 = i->swf.movieSize.ymin/20;
1804     if(x2) *x2 = i->swf.movieSize.xmax/20;
1805     if(y2) *y2 = i->swf.movieSize.ymax/20;
1806 }
1807
1808 int swfoutput_save(struct swfoutput* obj, char*filename) 
1809 {
1810     swfoutput_internal*i = (swfoutput_internal*)obj->internal;
1811     swfoutput_finalize(obj);
1812
1813     int fi;
1814     if(filename)
1815      fi = open(filename, O_BINARY|O_CREAT|O_TRUNC|O_WRONLY, 0777);
1816     else
1817      fi = 1; // stdout
1818     
1819     if(fi<=0) {
1820      msg("<fatal> Could not create \"%s\". ", FIXNULL(filename));
1821      return 0;
1822     }
1823     
1824     if(config_enablezlib || config_flashversion>=6) {
1825       if FAILED(swf_WriteSWC(fi,&i->swf)) 
1826        msg("<error> WriteSWC() failed.\n");
1827     } else {
1828       if FAILED(swf_WriteSWF(fi,&i->swf)) 
1829        msg("<error> WriteSWF() failed.\n");
1830     }
1831
1832     if(filename)
1833      close(fi);
1834     msg("<notice> SWF written\n");
1835     return 1;
1836 }
1837
1838 /* Perform cleaning up, complete the swf, and write it out. */
1839 void swfoutput_destroy(struct swfoutput* obj) 
1840 {
1841     swfoutput_internal*i = (swfoutput_internal*)obj->internal;
1842     if(!i) {
1843         /* not initialized yet- nothing to destroy */
1844         return;
1845     }
1846
1847     fontlist_t *tmp,*iterator = i->fontlist;
1848     while(iterator) {
1849         if(iterator->swffont) {
1850             swf_FontFree(iterator->swffont);iterator->swffont=0;
1851         }
1852         tmp = iterator;
1853         iterator = iterator->next;
1854         delete tmp;
1855     }
1856     swf_FreeTags(&i->swf);
1857
1858     free(i);i=0;
1859     memset(obj, 0, sizeof(swfoutput));
1860 }
1861
1862 void swfoutput_setdrawmode(swfoutput* obj, int mode)
1863 {
1864     swfoutput_internal*i = (swfoutput_internal*)obj->internal;
1865     obj->drawmode = mode;
1866     if(mode == DRAWMODE_FILL)
1867      i->fill = 1;
1868     else if(mode == DRAWMODE_EOFILL)
1869      i->fill = 1;
1870     else if(mode == DRAWMODE_STROKE)
1871      i->fill = 0;
1872     else if(mode == DRAWMODE_CLIP)
1873      i->fill = 1;
1874     else if(mode == DRAWMODE_EOCLIP)
1875      i->fill = 1;
1876 }
1877
1878 void swfoutput_setfillcolor(swfoutput* obj, U8 r, U8 g, U8 b, U8 a)
1879 {
1880     swfoutput_internal*i = (swfoutput_internal*)obj->internal;
1881     if(obj->fillrgb.r == r &&
1882        obj->fillrgb.g == g &&
1883        obj->fillrgb.b == b &&
1884        obj->fillrgb.a == a) return;
1885     if(i->shapeid>=0)
1886      endshape(obj,0);
1887
1888     obj->fillrgb.r = r;
1889     obj->fillrgb.g = g;
1890     obj->fillrgb.b = b;
1891     obj->fillrgb.a = a;
1892 }
1893
1894 void swfoutput_setstrokecolor(swfoutput* obj, U8 r, U8 g, U8 b, U8 a)
1895 {
1896     swfoutput_internal*i = (swfoutput_internal*)obj->internal;
1897     if(obj->strokergb.r == r &&
1898        obj->strokergb.g == g &&
1899        obj->strokergb.b == b &&
1900        obj->strokergb.a == a) return;
1901
1902     if(i->shapeid>=0)
1903      endshape(obj,0);
1904     obj->strokergb.r = r;
1905     obj->strokergb.g = g;
1906     obj->strokergb.b = b;
1907     obj->strokergb.a = a;
1908 }
1909
1910 void swfoutput_setlinewidth(struct swfoutput*obj, double _linewidth)
1911 {
1912     swfoutput_internal*i = (swfoutput_internal*)obj->internal;
1913     if(i->linewidth == (U16)(_linewidth*20))
1914         return;
1915
1916     if(i->shapeid>=0)
1917         endshape(obj,0);
1918     i->linewidth = (U16)(_linewidth*20);
1919 }
1920
1921
1922 void swfoutput_startclip(swfoutput*obj, SWF_OUTLINE*outline, struct swfmatrix*m)
1923 {
1924     swfoutput_internal*i = (swfoutput_internal*)obj->internal;
1925     if(i->textid>=0)
1926      endtext(obj);
1927     if(i->shapeid>=0)
1928      endshape(obj,0);
1929
1930     if(i->clippos >= 127)
1931     {
1932         msg("<warning> Too many clip levels.");
1933         i->clippos --;
1934     } 
1935     
1936     startshape(obj);
1937     int olddrawmode = obj->drawmode;
1938     swfoutput_setdrawmode(obj, DRAWMODE_CLIP);
1939     swfoutput_drawpath(obj, outline, m);
1940     swf_ShapeSetEnd(i->tag);
1941     swfoutput_setdrawmode(obj, olddrawmode);
1942
1943     i->tag = swf_InsertTag(i->tag,ST_PLACEOBJECT2);
1944     i->cliptags[i->clippos] = i->tag;
1945     i->clipshapes[i->clippos] = i->shapeid;
1946     i->clipdepths[i->clippos] = i->depth++;
1947     i->clippos++;
1948     
1949     endshape_noput(obj);
1950 }
1951
1952 void swfoutput_endclip(swfoutput*obj)
1953 {
1954     swfoutput_internal*i = (swfoutput_internal*)obj->internal;
1955     if(i->textid>=0)
1956         endtext(obj);
1957     if(i->shapeid>=0)
1958         endshape(obj,0);
1959
1960     if(!i->clippos) {
1961         msg("<error> Invalid end of clipping region");
1962         return;
1963     }
1964     i->clippos--;
1965     swf_ObjectPlaceClip(i->cliptags[i->clippos],i->clipshapes[i->clippos],i->clipdepths[i->clippos],&i->page_matrix,NULL,NULL,i->depth++);
1966 }
1967
1968 static void drawlink(struct swfoutput*obj, ActionTAG*,ActionTAG*, swfcoord*points, char mouseover);
1969
1970 void swfoutput_linktourl(struct swfoutput*obj, char*url, swfcoord*points)
1971 {
1972     swfoutput_internal*i = (swfoutput_internal*)obj->internal;
1973     ActionTAG* actions;
1974     if(!strncmp("http://pdf2swf:", url, 15)) {
1975      char*tmp = strdup(url);
1976      int l = strlen(tmp);
1977      if(tmp[l-1] == '/')
1978         tmp[l-1] = 0;
1979      swfoutput_namedlink(obj, tmp+15, points);
1980      free(tmp);
1981      return;
1982     }
1983     
1984     if(i->shapeid>=0)
1985         endshape(obj,0);
1986     if(i->textid>=0)
1987         endtext(obj);
1988     
1989     if(config_opennewwindow)
1990       actions = action_GetUrl(0, url, "_parent");
1991     else
1992       actions = action_GetUrl(0, url, "_this");
1993     actions = action_End(actions);
1994     
1995     drawlink(obj, actions, 0, points,0);
1996 }
1997 void swfoutput_linktopage(struct swfoutput*obj, int page, swfcoord*points)
1998 {
1999     swfoutput_internal*i = (swfoutput_internal*)obj->internal;
2000     ActionTAG* actions;
2001
2002     if(i->shapeid>=0)
2003         endshape(obj,0);
2004     if(i->textid>=0)
2005         endtext(obj);
2006    
2007       actions = action_GotoFrame(0, page);
2008       actions = action_End(actions);
2009
2010     drawlink(obj, actions, 0, points,0);
2011 }
2012
2013 /* Named Links (a.k.a. Acrobatmenu) are used to implement various gadgets
2014    of the viewer objects, like subtitles, index elements etc.
2015 */
2016 void swfoutput_namedlink(struct swfoutput*obj, char*name, swfcoord*points)
2017 {
2018     swfoutput_internal*i = (swfoutput_internal*)obj->internal;
2019     ActionTAG *actions1,*actions2;
2020     char*tmp = strdup(name);
2021     char mouseover = 1;
2022
2023     if(i->shapeid>=0)
2024         endshape(obj,0);
2025     if(i->textid>=0)
2026         endtext(obj);
2027
2028     if(!strncmp(tmp, "call:", 5))
2029     {
2030         char*x = strchr(&tmp[5], ':');
2031         if(!x) {
2032             actions1 = action_PushInt(0, 0); //number of parameters (0)
2033             actions1 = action_PushString(actions1, &tmp[5]); //function name
2034             actions1 = action_CallFunction(actions1);
2035         } else {
2036             *x = 0;
2037             actions1 = action_PushString(0, x+1); //parameter
2038             actions1 = action_PushInt(actions1, 1); //number of parameters (1)
2039             actions1 = action_PushString(actions1, &tmp[5]); //function name
2040             actions1 = action_CallFunction(actions1);
2041         }
2042         actions2 = action_End(0);
2043         mouseover = 0;
2044     }
2045     else
2046     {
2047         actions1 = action_PushString(0, "/:subtitle");
2048         actions1 = action_PushString(actions1, name);
2049         actions1 = action_SetVariable(actions1);
2050         actions1 = action_End(actions1);
2051
2052         actions2 = action_PushString(0, "/:subtitle");
2053         actions2 = action_PushString(actions2, "");
2054         actions2 = action_SetVariable(actions2);
2055         actions2 = action_End(actions2);
2056     }
2057
2058     drawlink(obj, actions1, actions2, points,mouseover);
2059
2060     swf_ActionFree(actions1);
2061     swf_ActionFree(actions2);
2062     free(tmp);
2063 }
2064
2065 static void drawlink(struct swfoutput*obj, ActionTAG*actions1, ActionTAG*actions2, swfcoord*points, char mouseover)
2066 {
2067     swfoutput_internal*i = (swfoutput_internal*)obj->internal;
2068     RGBA rgb;
2069     SRECT r;
2070     int lsid=0;
2071     int fsid;
2072     struct plotxy p1,p2,p3,p4;
2073     int myshapeid;
2074     int myshapeid2;
2075     double xmin,ymin;
2076     double xmax=xmin=points[0].x,ymax=ymin=points[0].y;
2077     double posx = 0;
2078     double posy = 0;
2079     int t;
2080     int buttonid = ++i->currentswfid;
2081     for(t=1;t<4;t++)
2082     {
2083         if(points[t].x>xmax) xmax=points[t].x;
2084         if(points[t].y>ymax) ymax=points[t].y;
2085         if(points[t].x<xmin) xmin=points[t].x;
2086         if(points[t].y<ymin) ymin=points[t].y;
2087     }
2088    
2089     p1.x=points[0].x; p1.y=points[0].y; p2.x=points[1].x; p2.y=points[1].y; 
2090     p3.x=points[2].x; p3.y=points[2].y; p4.x=points[3].x; p4.y=points[3].y;
2091    
2092     /* the following code subtracts the upper left edge from all coordinates,
2093        and set's posx,posy so that ST_PLACEOBJECT is used with a matrix.
2094        Necessary for preprocessing with swfcombine. */
2095     posx = xmin; posy = ymin;
2096     p1.x-=posx;p2.x-=posx;p3.x-=posx;p4.x-=posx;
2097     p1.y-=posy;p2.y-=posy;p3.y-=posy;p4.y-=posy;
2098     xmin -= posx; ymin -= posy;
2099     xmax -= posx; ymax -= posy;
2100     
2101     /* shape */
2102     myshapeid = ++i->currentswfid;
2103     i->tag = swf_InsertTag(i->tag,ST_DEFINESHAPE3);
2104     swf_ShapeNew(&i->shape);
2105     rgb.r = rgb.b = rgb.a = rgb.g = 0; 
2106     fsid = swf_ShapeAddSolidFillStyle(i->shape,&rgb);
2107     swf_SetU16(i->tag, myshapeid);
2108     r.xmin = (int)(xmin*20);
2109     r.ymin = (int)(ymin*20);
2110     r.xmax = (int)(xmax*20);
2111     r.ymax = (int)(ymax*20);
2112     swf_SetRect(i->tag,&r);
2113     swf_SetShapeStyles(i->tag,i->shape);
2114     swf_ShapeCountBits(i->shape,NULL,NULL);
2115     swf_SetShapeBits(i->tag,i->shape);
2116     swf_ShapeSetAll(i->tag,i->shape,/*x*/0,/*y*/0,0,fsid,0);
2117     i->swflastx = i->swflasty = 0;
2118     moveto(obj, i->tag, p1);
2119     lineto(obj, i->tag, p2);
2120     lineto(obj, i->tag, p3);
2121     lineto(obj, i->tag, p4);
2122     lineto(obj, i->tag, p1);
2123     swf_ShapeSetEnd(i->tag);
2124
2125     /* shape2 */
2126     myshapeid2 = ++i->currentswfid;
2127     i->tag = swf_InsertTag(i->tag,ST_DEFINESHAPE3);
2128     swf_ShapeNew(&i->shape);
2129     rgb.r = rgb.b = rgb.a = rgb.g = 255;
2130     rgb.a = 40;
2131     fsid = swf_ShapeAddSolidFillStyle(i->shape,&rgb);
2132     swf_SetU16(i->tag, myshapeid2);
2133     r.xmin = (int)(xmin*20);
2134     r.ymin = (int)(ymin*20);
2135     r.xmax = (int)(xmax*20);
2136     r.ymax = (int)(ymax*20);
2137     swf_SetRect(i->tag,&r);
2138     swf_SetShapeStyles(i->tag,i->shape);
2139     swf_ShapeCountBits(i->shape,NULL,NULL);
2140     swf_SetShapeBits(i->tag,i->shape);
2141     swf_ShapeSetAll(i->tag,i->shape,/*x*/0,/*y*/0,0,fsid,0);
2142     i->swflastx = i->swflasty = 0;
2143     moveto(obj, i->tag, p1);
2144     lineto(obj, i->tag, p2);
2145     lineto(obj, i->tag, p3);
2146     lineto(obj, i->tag, p4);
2147     lineto(obj, i->tag, p1);
2148     swf_ShapeSetEnd(i->tag);
2149
2150     if(!mouseover)
2151     {
2152         i->tag = swf_InsertTag(i->tag,ST_DEFINEBUTTON);
2153         swf_SetU16(i->tag,buttonid); //id
2154         swf_ButtonSetFlags(i->tag, 0); //menu=no
2155         swf_ButtonSetRecord(i->tag,0x01,myshapeid,i->depth,0,0);
2156         swf_ButtonSetRecord(i->tag,0x02,myshapeid2,i->depth,0,0);
2157         swf_ButtonSetRecord(i->tag,0x04,myshapeid2,i->depth,0,0);
2158         swf_ButtonSetRecord(i->tag,0x08,myshapeid,i->depth,0,0);
2159         swf_SetU8(i->tag,0);
2160         swf_ActionSet(i->tag,actions1);
2161         swf_SetU8(i->tag,0);
2162     }
2163     else
2164     {
2165         i->tag = swf_InsertTag(i->tag,ST_DEFINEBUTTON2);
2166         swf_SetU16(i->tag,buttonid); //id
2167         swf_ButtonSetFlags(i->tag, 0); //menu=no
2168         swf_ButtonSetRecord(i->tag,0x01,myshapeid,i->depth,0,0);
2169         swf_ButtonSetRecord(i->tag,0x02,myshapeid2,i->depth,0,0);
2170         swf_ButtonSetRecord(i->tag,0x04,myshapeid2,i->depth,0,0);
2171         swf_ButtonSetRecord(i->tag,0x08,myshapeid,i->depth,0,0);
2172         swf_SetU8(i->tag,0); // end of button records
2173         swf_ButtonSetCondition(i->tag, BC_IDLE_OVERUP);
2174         swf_ActionSet(i->tag,actions1);
2175         if(actions2) {
2176             swf_ButtonSetCondition(i->tag, BC_OVERUP_IDLE);
2177             swf_ActionSet(i->tag,actions2);
2178             swf_SetU8(i->tag,0);
2179             swf_ButtonPostProcess(i->tag, 2);
2180         } else {
2181             swf_SetU8(i->tag,0);
2182             swf_ButtonPostProcess(i->tag, 1);
2183         }
2184     }
2185     
2186     i->tag = swf_InsertTag(i->tag,ST_PLACEOBJECT2);
2187
2188     if(posx!=0 || posy!=0) {
2189         SPOINT p;
2190         p.x = (int)(posx*20);
2191         p.y = (int)(posy*20);
2192         p = swf_TurnPoint(p, &i->page_matrix);
2193         MATRIX m;
2194         m = i->page_matrix;
2195         m.tx = p.x;
2196         m.ty = p.y;
2197         swf_ObjectPlace(i->tag, buttonid, i->depth++,&m,0,0);
2198     }
2199     else {
2200         swf_ObjectPlace(i->tag, buttonid, i->depth++,&i->page_matrix,0,0);
2201     }
2202 }
2203
2204 static void drawimage(struct swfoutput*obj, int bitid, int sizex,int sizey, 
2205         double x1,double y1,
2206         double x2,double y2,
2207         double x3,double y3,
2208         double x4,double y4)
2209 {
2210     swfoutput_internal*i = (swfoutput_internal*)obj->internal;
2211     RGBA rgb;
2212     SRECT r;
2213     int lsid=0;
2214     int fsid;
2215     struct plotxy p1,p2,p3,p4;
2216     int myshapeid;
2217     double xmax=x1,ymax=y1,xmin=x1,ymin=y1;
2218     if(x2>xmax) xmax=x2;
2219     if(y2>ymax) ymax=y2;
2220     if(x2<xmin) xmin=x2;
2221     if(y2<ymin) ymin=y2;
2222     if(x3>xmax) xmax=x3;
2223     if(y3>ymax) ymax=y3;
2224     if(x3<xmin) xmin=x3;
2225     if(y3<ymin) ymin=y3;
2226     if(x4>xmax) xmax=x4;
2227     if(y4>ymax) ymax=y4;
2228     if(x4<xmin) xmin=x4;
2229     if(y4<ymin) ymin=y4;
2230     p1.x=x1; p1.y=y1;
2231     p2.x=x2; p2.y=y2;
2232     p3.x=x3; p3.y=y3;
2233     p4.x=x4; p4.y=y4;
2234
2235     {p1.x = (int)(p1.x*20)/20.0;
2236      p1.y = (int)(p1.y*20)/20.0;
2237      p2.x = (int)(p2.x*20)/20.0;
2238      p2.y = (int)(p2.y*20)/20.0;
2239      p3.x = (int)(p3.x*20)/20.0;
2240      p3.y = (int)(p3.y*20)/20.0;
2241      p4.x = (int)(p4.x*20)/20.0;
2242      p4.y = (int)(p4.y*20)/20.0;}
2243     
2244     MATRIX m;
2245     m.sx = (int)(65536*20*(p4.x-p1.x)/sizex);
2246     m.r1 = -(int)(65536*20*(p4.y-p1.y)/sizex);
2247     m.r0 = (int)(65536*20*(p1.x-p2.x)/sizey);
2248     m.sy = -(int)(65536*20*(p1.y-p2.y)/sizey);
2249
2250     m.tx = (int)(p1.x*20);
2251     m.ty = (int)(p1.y*20);
2252   
2253     /* shape */
2254     myshapeid = ++i->currentswfid;
2255     i->tag = swf_InsertTag(i->tag,ST_DEFINESHAPE);
2256     SHAPE*shape;
2257     swf_ShapeNew(&shape);
2258     //lsid = ShapeAddLineStyle(shape,linewidth,&obj->strokergb);
2259     //fsid = ShapeAddSolidFillStyle(shape,&obj->fillrgb);
2260     fsid = swf_ShapeAddBitmapFillStyle(shape,&m,bitid,1);
2261     swf_SetU16(i->tag, myshapeid);
2262     r.xmin = (int)(xmin*20);
2263     r.ymin = (int)(ymin*20);
2264     r.xmax = (int)(xmax*20);
2265     r.ymax = (int)(ymax*20);
2266     swf_SetRect(i->tag,&r);
2267     swf_SetShapeStyles(i->tag,shape);
2268     swf_ShapeCountBits(shape,NULL,NULL);
2269     swf_SetShapeBits(i->tag,shape);
2270     swf_ShapeSetAll(i->tag,shape,/*x*/0,/*y*/0,lsid,fsid,0);
2271     i->swflastx = i->swflasty = 0;
2272     moveto(obj, i->tag, p1);
2273     lineto(obj, i->tag, p2);
2274     lineto(obj, i->tag, p3);
2275     lineto(obj, i->tag, p4);
2276     lineto(obj, i->tag, p1);
2277     /*
2278     ShapeMoveTo  (tag, shape, (int)(x1*20),(int)(y1*20));
2279     ShapeSetLine (tag, shape, (int)(x1*20);
2280     ShapeSetLine (tag, shape, x*20,0);
2281     ShapeSetLine (tag, shape, 0,-y*20);
2282     ShapeSetLine (tag, shape, -x*20,0);*/
2283     swf_ShapeSetEnd(i->tag);
2284     swf_ShapeFree(shape);
2285
2286     /* instance */
2287     i->tag = swf_InsertTag(i->tag,ST_PLACEOBJECT2);
2288
2289     swf_ObjectPlace(i->tag,myshapeid,/*depth*/i->depth++,&i->page_matrix,NULL,NULL);
2290 }
2291
2292 int swfoutput_drawimagejpeg_old(struct swfoutput*obj, char*filename, int sizex,int sizey, 
2293         double x1,double y1,
2294         double x2,double y2,
2295         double x3,double y3,
2296         double x4,double y4)
2297 {
2298     swfoutput_internal*i = (swfoutput_internal*)obj->internal;
2299     TAG*oldtag;
2300     if(i->shapeid>=0)
2301         endshape(obj,0);
2302     if(i->textid>=0)
2303         endtext(obj);
2304
2305     int bitid = ++i->currentswfid;
2306     oldtag = i->tag;
2307     i->tag = swf_InsertTag(i->tag,ST_DEFINEBITSJPEG2);
2308     swf_SetU16(i->tag, bitid);
2309     if(swf_SetJPEGBits(i->tag, filename, config_jpegquality)<0) {
2310         swf_DeleteTag(i->tag);
2311         i->tag = oldtag;
2312         return -1;
2313     }
2314
2315     drawimage(obj, bitid, sizex, sizey, x1,y1,x2,y2,x3,y3,x4,y4);
2316     return bitid;
2317 }
2318
2319 int swfoutput_drawimagejpeg(struct swfoutput*obj, RGBA*mem, int sizex,int sizey, 
2320         double x1,double y1,
2321         double x2,double y2,
2322         double x3,double y3,
2323         double x4,double y4)
2324 {
2325     swfoutput_internal*i = (swfoutput_internal*)obj->internal;
2326     TAG*oldtag;
2327     JPEGBITS*jpeg;
2328
2329     if(i->shapeid>=0)
2330         endshape(obj,0);
2331     if(i->textid>=0)
2332         endtext(obj);
2333
2334     int bitid = ++i->currentswfid;
2335     oldtag = i->tag;
2336     i->tag = swf_InsertTag(i->tag,ST_DEFINEBITSJPEG2);
2337     swf_SetU16(i->tag, bitid);
2338     swf_SetJPEGBits2(i->tag,sizex,sizey,mem,config_jpegquality);
2339     drawimage(obj, bitid, sizex, sizey, x1,y1,x2,y2,x3,y3,x4,y4);
2340     return bitid;
2341 }
2342
2343 int swfoutput_drawimagelossless(struct swfoutput*obj, RGBA*mem, int sizex,int sizey, 
2344         double x1,double y1,
2345         double x2,double y2,
2346         double x3,double y3,
2347         double x4,double y4)
2348 {
2349     swfoutput_internal*i = (swfoutput_internal*)obj->internal;
2350     TAG*oldtag;
2351     if(i->shapeid>=0)
2352         endshape(obj,0);
2353     if(i->textid>=0)
2354         endtext(obj);
2355
2356     int bitid = ++i->currentswfid;
2357     oldtag = i->tag;
2358     i->tag = swf_InsertTag(i->tag,ST_DEFINEBITSLOSSLESS);
2359     swf_SetU16(i->tag, bitid);
2360     if(swf_SetLosslessBits(i->tag,sizex,sizey,mem, BMF_32BIT)<0) {
2361         swf_DeleteTag(i->tag);
2362         i->tag = oldtag;
2363         return -1;
2364     }
2365     
2366     drawimage(obj, bitid, sizex, sizey, x1,y1,x2,y2,x3,y3,x4,y4);
2367     return bitid;
2368 }
2369
2370 int swfoutput_drawimagelosslessN(struct swfoutput*obj, U8*mem, RGBA*pal, int sizex,int sizey, 
2371         double x1,double y1,
2372         double x2,double y2,
2373         double x3,double y3,
2374         double x4,double y4, int n)
2375 {
2376     swfoutput_internal*i = (swfoutput_internal*)obj->internal;
2377     TAG*oldtag;
2378     U8*mem2 = 0;
2379     if(i->shapeid>=0)
2380         endshape(obj,0);
2381     if(i->textid>=0)
2382         endtext(obj);
2383
2384     if(sizex&3)
2385     { 
2386         /* SWF expects scanlines to be 4 byte aligned */
2387         int x,y;
2388         U8*ptr;
2389         mem2 = (U8*)malloc(BYTES_PER_SCANLINE(sizex)*sizey);
2390         ptr = mem2;
2391         for(y=0;y<sizey;y++)
2392         {
2393             for(x=0;x<sizex;x++)
2394                 *ptr++ = mem[y*sizex+x];
2395             ptr+= BYTES_PER_SCANLINE(sizex)-sizex;
2396         }
2397         mem = mem2;
2398     }
2399
2400     int bitid = ++i->currentswfid;
2401     oldtag = i->tag;
2402     i->tag = swf_InsertTag(i->tag,ST_DEFINEBITSLOSSLESS2);
2403     swf_SetU16(i->tag, bitid);
2404     if(swf_SetLosslessBitsIndexed(i->tag,sizex,sizey,mem, pal, n)<0) {
2405         swf_DeleteTag(i->tag);
2406         i->tag = oldtag;
2407         return -1;
2408     }
2409     if(mem2)
2410         free(mem2);
2411   
2412     drawimage(obj, bitid, sizex, sizey, x1,y1,x2,y2,x3,y3,x4,y4);
2413     return bitid;
2414 }
2415
2416 void swfoutput_drawimageagain(struct swfoutput*obj, int id, int sizex,int sizey, 
2417         double x1,double y1,
2418         double x2,double y2,
2419         double x3,double y3,
2420         double x4,double y4)
2421 {
2422     swfoutput_internal*i = (swfoutput_internal*)obj->internal;
2423     if(id<0) return;
2424     if(i->shapeid>=0)
2425         endshape(obj,0);
2426     if(i->textid>=0)
2427         endtext(obj);
2428
2429     drawimage(obj, id, sizex, sizey, x1,y1,x2,y2,x3,y3,x4,y4);
2430 }
2431
2432 void swfoutput_setparameter(char*name, char*value)
2433 {
2434     if(!strcmp(name, "drawonlyshapes")) {
2435         config_drawonlyshapes = atoi(value);
2436     } else if(!strcmp(name, "ignoredraworder")) {
2437         config_ignoredraworder = atoi(value);
2438     } else if(!strcmp(name, "filloverlap")) {
2439         config_filloverlap = atoi(value);
2440     } else if(!strcmp(name, "linksopennewwindow")) {
2441         config_opennewwindow = atoi(value);
2442     } else if(!strcmp(name, "opennewwindow")) {
2443         config_opennewwindow = atoi(value);
2444     } else if(!strcmp(name, "storeallcharacters")) {
2445         config_storeallcharacters = atoi(value);
2446     } else if(!strcmp(name, "enablezlib")) {
2447         config_enablezlib = atoi(value);
2448     } else if(!strcmp(name, "insertstop")) {
2449         config_insertstoptag = atoi(value);
2450     } else if(!strcmp(name, "protected")) {
2451         config_protect = atoi(value);
2452     } else if(!strcmp(name, "flashversion")) {
2453         config_flashversion = atoi(value);
2454     } else if(!strcmp(name, "minlinewidth")) {
2455         config_minlinewidth = atof(value);
2456     } else if(!strcmp(name, "jpegquality")) {
2457         int val = atoi(value);
2458         if(val<0) val=0;
2459         if(val>100) val=100;
2460         config_jpegquality = val;
2461     } else if(!strcmp(name, "splinequality")) {
2462         int v = atoi(value);
2463         v = 500-(v*5); // 100% = 0.25 pixel, 0% = 25 pixel
2464         if(v<1) v = 1;
2465         config_splinemaxerror = v;
2466     } else if(!strcmp(name, "fontquality")) {
2467         int v = atoi(value);
2468         v = 500-(v*5); // 100% = 0.25 pixel, 0% = 25 pixel
2469         if(v<1) v = 1;
2470         config_fontsplinemaxerror = v;
2471     } else {
2472         fprintf(stderr, "unknown parameter: %s (=%s)\n", name, value);
2473     }
2474 }
2475