omit drawing of objects outside the page.
[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         if(tmp) {
812             x += (tmp->dest.x/(float)0xffff);
813             y += (tmp->dest.y/(float)0xffff);
814         }
815         if(!tmp || tmp->type == SWF_PATHTYPE_MOVE) {
816             if(valid && last) {
817                 if(last->type == SWF_PATHTYPE_LINE && t1linelen(obj,last)>line_width*2 &&
818                    lastwasline && line_cap != LINE_CAP_ROUND)
819                     drawShortPathWithStraightEnds(obj, lastx, lasty, m, last, valid, line_cap, line_join, line_width);
820                 else
821                     drawShortPathWithEnds(obj, lastx, lasty, m, last, valid, line_cap, line_join, line_width);
822             }
823             if(!tmp)
824                 break;
825             valid = 0;
826             last = 0;
827             lastx = x;
828             lasty = y;
829         } else {
830             if(!last)
831                 last = tmp;
832             valid++;
833         }
834
835         if(tmp && tmp->type == SWF_PATHTYPE_LINE && t1linelen(obj,tmp)>line_width*2)
836             lastwasline = 1;
837         else
838             lastwasline = 0;
839
840         if(tmp->link)
841             tmp->link->last = tmp; // make sure list is properly linked in both directions
842         tmp = tmp->link;
843     }
844 }
845
846 static inline int colorcompare(struct swfoutput*obj, RGBA*a,RGBA*b)
847 {
848
849     if(a->r!=b->r ||
850        a->g!=b->g ||
851        a->b!=b->b ||
852        a->a!=b->a) {
853         return 0;
854     }
855     return 1;
856 }
857
858 static SRECT getcharacterbbox(struct swfoutput*obj, SWFFONT*font)
859 {
860     swfoutput_internal*i = (swfoutput_internal*)obj->internal;
861     SRECT r;
862     char debug = 0;
863     memset(&r, 0, sizeof(r));
864
865     int t;
866     if(debug) printf("\n");
867     for(t=0;t<i->chardatapos;t++)
868     {
869         if(i->chardata[t].fontid != font->id) {
870             msg("<error> Internal error: fontid %d != fontid %d", i->chardata[t].fontid, font->id);
871             exit(1);
872         }
873         SRECT b = font->layout->bounds[i->chardata[t].charid];
874         b.xmin *= i->chardata[t].size;
875         b.ymin *= i->chardata[t].size;
876         b.xmax *= i->chardata[t].size;
877         b.ymax *= i->chardata[t].size;
878         b.xmin /= 1024;
879         b.ymin /= 1024;
880         b.xmax /= 1024;
881         b.ymax /= 1024;
882         b.xmin += i->chardata[t].x;
883         b.ymin += i->chardata[t].y;
884         b.xmax += i->chardata[t].x;
885         b.ymax += i->chardata[t].y;
886
887         /* until we solve the INTERNAL_SCALING problem (see below)
888            make sure the bounding box is big enough */
889         b.xmin -= 20;
890         b.ymin -= 20;
891         b.xmax += 20;
892         b.ymax += 20;
893
894         if(debug) printf("(%f,%f,%f,%f) -> (%f,%f,%f,%f) [font %d/%d, char %d]\n",
895                 font->layout->bounds[i->chardata[t].charid].xmin/20.0,
896                 font->layout->bounds[i->chardata[t].charid].ymin/20.0,
897                 font->layout->bounds[i->chardata[t].charid].xmax/20.0,
898                 font->layout->bounds[i->chardata[t].charid].ymax/20.0,
899                 b.xmin/20.0,
900                 b.ymin/20.0,
901                 b.xmax/20.0,
902                 b.ymax/20.0,
903                 i->chardata[t].fontid,
904                 font->id,
905                 i->chardata[t].charid
906                 );
907         swf_ExpandRect2(&r, &b);
908     }
909     if(debug) printf("-----> (%f,%f,%f,%f)\n",
910             r.xmin/20.0,
911             r.ymin/20.0,
912             r.xmax/20.0,
913             r.ymax/20.0);
914     return r;
915 }
916
917 static void putcharacters(struct swfoutput*obj, TAG*tag)
918 {
919     swfoutput_internal*i = (swfoutput_internal*)obj->internal;
920     int t;
921     SWFFONT font;
922     RGBA color;
923     color.r = i->chardata[0].color.r^255;
924     color.g = 0;
925     color.b = 0;
926     color.a = 0;
927     int lastfontid;
928     int lastx;
929     int lasty;
930     int lastsize;
931     int charids[128];
932     int charadvance[128];
933     int charstorepos;
934     int pass;
935     int glyphbits=1; //TODO: can this be zero?
936     int advancebits=1;
937
938     if(tag->id != ST_DEFINETEXT &&
939         tag->id != ST_DEFINETEXT2) {
940         msg("<error> internal error: putcharacters needs an text tag, not %d\n",tag->id);
941         exit(1);
942     }
943     if(!i->chardatapos) {
944         msg("<warning> putcharacters called with zero characters");
945     }
946
947     for(pass = 0; pass < 2; pass++)
948     {
949         charstorepos = 0;
950         lastfontid = -1;
951         lastx = CHARMIDX;
952         lasty = CHARMIDY;
953         lastsize = -1;
954
955         if(pass==1)
956         {
957             advancebits++; // add sign bit
958             swf_SetU8(tag, glyphbits);
959             swf_SetU8(tag, advancebits);
960         }
961
962         for(t=0;t<=i->chardatapos;t++)
963         {
964             if(lastfontid != i->chardata[t].fontid || 
965                     lastx!=i->chardata[t].x ||
966                     lasty!=i->chardata[t].y ||
967                     !colorcompare(obj,&color, &i->chardata[t].color) ||
968                     charstorepos==127 ||
969                     lastsize != i->chardata[t].size ||
970                     t == i->chardatapos)
971             {
972                 if(charstorepos && pass==0)
973                 {
974                     int s;
975                     for(s=0;s<charstorepos;s++)
976                     {
977                         while(charids[s]>=(1<<glyphbits))
978                             glyphbits++;
979                         while(charadvance[s]>=(1<<advancebits))
980                             advancebits++;
981                     }
982                 }
983                 if(charstorepos && pass==1)
984                 {
985                     tag->writeBit = 0; // Q&D
986                     swf_SetBits(tag, 0, 1); // GLYPH Record
987                     swf_SetBits(tag, charstorepos, 7); // number of glyphs
988                     int s;
989                     for(s=0;s<charstorepos;s++)
990                     {
991                         swf_SetBits(tag, charids[s], glyphbits);
992                         swf_SetBits(tag, charadvance[s], advancebits);
993                     }
994                 }
995                 charstorepos = 0;
996
997                 if(pass == 1 && t<i->chardatapos)
998                 {
999                     RGBA*newcolor=0;
1000                     SWFFONT*newfont=0;
1001                     int newx = 0;
1002                     int newy = 0;
1003                     if(lastx != i->chardata[t].x ||
1004                        lasty != i->chardata[t].y)
1005                     {
1006                         newx = i->chardata[t].x;
1007                         newy = i->chardata[t].y;
1008                         if(newx == 0)
1009                             newx = SET_TO_ZERO;
1010                         if(newy == 0)
1011                             newy = SET_TO_ZERO;
1012                     }
1013                     if(!colorcompare(obj,&color, &i->chardata[t].color)) 
1014                     {
1015                         color = i->chardata[t].color;
1016                         newcolor = &color;
1017                     }
1018                     font.id = i->chardata[t].fontid;
1019                     if(lastfontid != i->chardata[t].fontid || lastsize != i->chardata[t].size)
1020                         newfont = &font;
1021
1022                     tag->writeBit = 0; // Q&D
1023                     swf_TextSetInfoRecord(tag, newfont, i->chardata[t].size, newcolor, newx,newy);
1024                 }
1025
1026                 lastfontid = i->chardata[t].fontid;
1027                 lastx = i->chardata[t].x;
1028                 lasty = i->chardata[t].y;
1029                 lastsize = i->chardata[t].size;
1030             }
1031
1032             if(t==i->chardatapos)
1033                     break;
1034
1035             int advance;
1036             int nextt = t==i->chardatapos-1?t:t+1;
1037             int rel = i->chardata[nextt].x-i->chardata[t].x;
1038             if(rel>=0 && (rel<(1<<(advancebits-1)) || pass==0)) {
1039                advance = rel;
1040                lastx=i->chardata[nextt].x;
1041             }
1042             else {
1043                advance = 0;
1044                lastx=i->chardata[t].x;
1045             }
1046             charids[charstorepos] = i->chardata[t].charid;
1047             charadvance[charstorepos] = advance;
1048             charstorepos ++;
1049         }
1050     }
1051     i->chardatapos = 0;
1052 }
1053
1054 static void putcharacter(struct swfoutput*obj, int fontid, int charid, 
1055                     int x,int y, int size)
1056 {
1057     swfoutput_internal*i = (swfoutput_internal*)obj->internal;
1058     if(i->chardatapos == CHARDATAMAX)
1059     {
1060         msg("<warning> Character buffer too small. SWF will be slightly bigger");
1061         endtext(obj);
1062         starttext(obj);
1063     }
1064     i->chardata[i->chardatapos].fontid = fontid;
1065     i->chardata[i->chardatapos].charid = charid;
1066     i->chardata[i->chardatapos].x = x;
1067     i->chardata[i->chardatapos].y = y;
1068     i->chardata[i->chardatapos].color = obj->fillrgb;
1069     i->chardata[i->chardatapos].size = size;
1070     i->chardatapos++;
1071 }
1072
1073 /* Notice: we can only put chars in the range -1639,1638 (-32768/20,32768/20).
1074    So if we set this value to high, the char coordinates will overflow.
1075    If we set it to low, however, the char positions will be inaccurate */
1076 #define FONT_INTERNAL_SIZE 4
1077
1078 /* process a character. */
1079 static int drawchar(struct swfoutput*obj, SWFFONT *swffont, char*character, int charnr, int u, swfmatrix*m)
1080 {
1081     swfoutput_internal*i = (swfoutput_internal*)obj->internal;
1082     if(!swffont) {
1083         msg("<warning> Font is NULL");
1084         return 0;
1085     }
1086
1087     int charid = getCharID(swffont, charnr, character, u); 
1088     
1089     if(charid<0) {
1090         msg("<warning> Didn't find character '%s' (c=%d,u=%d) in current charset (%s, %d characters)", 
1091                 FIXNULL(character),charnr, u, FIXNULL((char*)swffont->name), swffont->numchars);
1092         return 0;
1093     }
1094
1095     if(i->shapeid>=0)
1096         endshape(obj,0);
1097     if(i->textid<0)
1098         starttext(obj);
1099
1100     float x = m->m13;
1101     float y = m->m23;
1102     float det = ((m->m11*m->m22)-(m->m21*m->m12));
1103     if(fabs(det) < 0.0005) { 
1104         /* x direction equals y direction- the text is invisible */
1105         return 1;
1106     }
1107     det = 20*FONT_INTERNAL_SIZE / det;
1108
1109     SPOINT p;
1110     p.x = (SCOORD)((  x * m->m22 - y * m->m12)*det);
1111     p.y = (SCOORD)((- x * m->m21 + y * m->m11)*det);
1112
1113     putcharacter(obj, swffont->id, charid,p.x,p.y,FONT_INTERNAL_SIZE);
1114     swf_FontUseGlyph(swffont, charid);
1115     return 1;
1116
1117     /*else
1118     {
1119         SWF_OUTLINE*outline = font->getOutline(character, charnr);
1120         char* charname = character;
1121
1122         if(!outline) {
1123          msg("<warning> Didn't find character '%s' (%d) in current charset (%s)", 
1124                  FIXNULL(character),charnr,FIXNULL(font->getName()));
1125          return;
1126         }
1127         
1128         swfmatrix m2=*m;    
1129         m2.m11/=100;
1130         m2.m21/=100;
1131         m2.m12/=100;
1132         m2.m22/=100;
1133
1134         if(textid>=0)
1135             endtext(obj);
1136         if(shapeid<0)
1137             startshape(obj);
1138
1139         startFill(obj);
1140
1141         int lf = fill;
1142         fill = 1;
1143         drawpath(tag, outline, &m2, 0);
1144         fill = lf;
1145     }*/
1146 }
1147
1148 static void endtext(swfoutput*obj)
1149 {
1150     swfoutput_internal*i = (swfoutput_internal*)obj->internal;
1151     if(i->textid<0)
1152         return;
1153
1154     i->tag = swf_InsertTag(i->tag,ST_DEFINETEXT);
1155     swf_SetU16(i->tag, i->textid);
1156
1157     SRECT r;
1158     r = getcharacterbbox(obj, obj->swffont);
1159     
1160     swf_SetRect(i->tag,&r);
1161
1162     MATRIX m;
1163     swf_GetMatrix(0, &m); /* set unit matrix- the real matrix is in the placeobject */
1164     swf_SetMatrix(i->tag,&m);
1165
1166     putcharacters(obj, i->tag);
1167     swf_SetU8(i->tag,0);
1168     i->tag = swf_InsertTag(i->tag,ST_PLACEOBJECT2);
1169     //swf_ObjectPlace(i->tag,i->textid,/*depth*/i->depth++,&i->page_matrix,NULL,NULL);
1170     MATRIX m2;
1171     swf_MatrixJoin(&m2,&obj->fontmatrix, &i->page_matrix);
1172
1173     swf_ObjectPlace(i->tag,i->textid,/*depth*/i->depth++,&m2,NULL,NULL);
1174     i->textid = -1;
1175 }
1176
1177
1178 /* draw a curved polygon. */
1179 void swfoutput_drawpath(swfoutput*obj, SWF_OUTLINE*outline, 
1180                             struct swfmatrix*m)
1181 {
1182     swfoutput_internal*i = (swfoutput_internal*)obj->internal;
1183     if(i->textid>=0)
1184         endtext(obj);
1185
1186     /* Multiple polygons in one shape don't overlap correctly, 
1187        so we better start a new shape here if the polygon is filled
1188      */
1189     if(i->shapeid>=0 && i->fill && !config_ignoredraworder) {
1190         endshape(obj,0);
1191     }
1192
1193     if(i->shapeid<0) {
1194         startshape(obj);
1195     }
1196
1197     if(!i->fill)
1198         stopFill(obj);
1199     else
1200         startFill(obj);
1201
1202     drawpath(obj, outline,m, 0); 
1203 }
1204
1205 void swfoutput_drawpath2poly(struct swfoutput*obj, SWF_OUTLINE*outline, struct swfmatrix*m, int line_join, int line_cap, double line_width, double miter_limit)
1206 {
1207     swfoutput_internal*i = (swfoutput_internal*)obj->internal;
1208     if(i->textid>=0)
1209         endtext(obj);
1210     if(i->shapeid>=0)
1211         endshape(obj,0);
1212     assert(i->shapeid<0);
1213     startshape(obj);
1214     stopFill(obj);
1215
1216     drawpath2poly(obj, outline, m, 0, line_join, line_cap, line_width, miter_limit); 
1217 }
1218
1219 int getCharID(SWFFONT *font, int charnr, char *charname, int u)
1220 {
1221     int t;
1222     if(charname && font->glyphnames) {
1223         for(t=0;t<font->numchars;t++) {
1224             if(font->glyphnames[t] && !strcmp(font->glyphnames[t],charname)) {
1225                 msg("<debug> Char [%d,>%s<,%d] maps to %d\n", charnr, charname, u, t);
1226                 return t;
1227             }
1228         }
1229         /* if we didn't find the character, maybe
1230            we can find the capitalized version */
1231         for(t=0;t<font->numchars;t++) {
1232             if(font->glyphnames[t] && !strcasecmp(font->glyphnames[t],charname)) {
1233                 msg("<debug> Char [%d,>>%s<<,%d] maps to %d\n", charnr, charname, u, t);
1234                 return t;
1235             }
1236         }
1237     }
1238
1239     if(u>0 && font->encoding != 255) {
1240         /* try to use the unicode id */
1241         if(u>=0 && u<font->maxascii && font->ascii2glyph[u]>=0) {
1242             msg("<debug> Char [%d,%s,>%d<] maps to %d\n", charnr, charname, u, font->ascii2glyph[u]);
1243             return font->ascii2glyph[u];
1244         }
1245     }
1246
1247     if(font->encoding != FONT_ENCODING_UNICODE) {
1248         /* the following only works if the font encoding
1249            is US-ASCII based. It's needed for fonts which return broken unicode
1250            indices */
1251         if(charnr>=0 && charnr<font->maxascii && font->ascii2glyph[charnr]>=0) {
1252             msg("<debug> Char [>%d<,%s,%d] maps to %d\n", charnr, charname, u, font->ascii2glyph[charnr]);
1253             return font->ascii2glyph[charnr];
1254         }
1255     } 
1256     
1257     if(charnr>=0 && charnr<font->numchars) {
1258         msg("<debug> Char [>%d<,%s,%d] maps to %d\n", charnr, charname, u, charnr);
1259         return charnr;
1260     }
1261     
1262     return -1;
1263 }
1264
1265
1266 /* set's the t1 font index of the font to use for swfoutput_drawchar(). */
1267 void swfoutput_setfont(struct swfoutput*obj, char*fontid, char*filename)
1268 {
1269     swfoutput_internal*i = (swfoutput_internal*)obj->internal;
1270     fontlist_t*last=0,*iterator;
1271     if(!fontid) {
1272         msg("<error> No fontid");
1273         return;
1274     }
1275
1276     if(obj->swffont && obj->swffont->name && !strcmp((char*)obj->swffont->name,fontid))
1277         return;
1278
1279     /* TODO: remove the need for this (enhance getcharacterbbox so that it can cope
1280              with multiple fonts */
1281     endtext(obj);
1282
1283     iterator = i->fontlist;
1284     while(iterator) {
1285         if(!strcmp((char*)iterator->swffont->name,fontid)) {
1286             obj->swffont = iterator->swffont; 
1287             return;
1288         }
1289         last = iterator;
1290         iterator = iterator->next;
1291     }
1292
1293     if(!filename) {
1294         msg("<error> No filename given for font- internal error?");
1295         return;
1296     }
1297
1298     swf_SetLoadFontParameters(64,/*skip unused*/0,/*full unicode*/1);
1299     SWFFONT*swffont = swf_LoadFont(filename);
1300
1301     if(swffont == 0) {
1302         msg("<warning> Couldn't load font %s (%s)", fontid, filename);
1303         swffont = swf_LoadFont(0);
1304     }
1305     
1306     if(swffont->glyph2ascii) {
1307         int t;
1308         int bad = 0;
1309         /* check whether the Unicode indices look o.k.
1310            If they don't, disable the unicode lookup by setting
1311            the encoding to 255 */
1312         for(t=0;t<swffont->numchars;t++) {
1313             int c = swffont->glyph2ascii[t];
1314             if(c && c < 32 && swffont->glyph[t].shape->bitlen > 16) {
1315                 // the character maps into the unicode control character range
1316                 // between 0001-001f. Yet it is not empty. Treat the one
1317                 // mapping as broken, and look how many of those we find.
1318                 bad ++;
1319             }
1320         }
1321         if(bad>5) {
1322             msg("<warning> Font %s has bad unicode mapping", swffont->name);
1323             swffont->encoding = 255;
1324         }
1325     }
1326
1327     swf_FontSetID(swffont, ++i->currentswfid);
1328     
1329     if(getScreenLogLevel() >= LOGLEVEL_DEBUG)  {
1330         // print font information
1331         msg("<debug> Font %s (%s)",swffont->name, filename);
1332         msg("<debug> |   ID: %d", swffont->id);
1333         msg("<debug> |   Version: %d", swffont->version);
1334         msg("<debug> |   Name: %s", fontid);
1335         msg("<debug> |   Numchars: %d", swffont->numchars);
1336         msg("<debug> |   Maxascii: %d", swffont->maxascii);
1337         msg("<debug> |   Style: %d", swffont->style);
1338         msg("<debug> |   Encoding: %d", swffont->encoding);
1339         for(int iii=0; iii<swffont->numchars;iii++) {
1340             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, 
1341                     swffont->layout->bounds[iii].xmin/20.0,
1342                     swffont->layout->bounds[iii].ymin/20.0,
1343                     swffont->layout->bounds[iii].xmax/20.0,
1344                     swffont->layout->bounds[iii].ymax/20.0
1345                     );
1346             int t;
1347             for(t=0;t<swffont->maxascii;t++) {
1348                 if(swffont->ascii2glyph[t] == iii)
1349                     msg("<debug> | - maps to %d",t);
1350             }
1351         }
1352     }
1353
1354     /* set the font name to the ID we use here */
1355     if(swffont->name) free(swffont->name);
1356     swffont->name = (U8*)strdup(fontid);
1357
1358     iterator = new fontlist_t;
1359     iterator->swffont = swffont;
1360     iterator->next = 0;
1361
1362     if(last) 
1363         last->next = iterator;
1364     else 
1365         i->fontlist = iterator;
1366
1367     obj->swffont = swffont; 
1368 }
1369
1370 int swfoutput_queryfont(struct swfoutput*obj, char*fontid)
1371 {
1372     swfoutput_internal*i = (swfoutput_internal*)obj->internal;
1373     fontlist_t *iterator = i->fontlist;
1374     while(iterator) {
1375         if(!strcmp((char*)iterator->swffont->name,fontid))
1376             return 1;
1377         iterator = iterator->next;
1378     }
1379     return 0;
1380 }
1381
1382 /* set's the matrix which is to be applied to characters drawn by
1383    swfoutput_drawchar() */
1384 void swfoutput_setfontmatrix(struct swfoutput*obj,double m11,double m12,
1385                                                   double m21,double m22)
1386 {
1387     swfoutput_internal*i = (swfoutput_internal*)obj->internal;
1388     if(obj->fontm11 == m11 &&
1389        obj->fontm12 == m12 &&
1390        obj->fontm21 == m21 &&
1391        obj->fontm22 == m22)
1392         return;
1393    if(i->textid>=0)
1394         endtext(obj);
1395     obj->fontm11 = m11;
1396     obj->fontm12 = m12;
1397     obj->fontm21 = m21;
1398     obj->fontm22 = m22;
1399     
1400     MATRIX m;
1401     m.sx = (U32)(((obj->fontm11)*65536)/FONT_INTERNAL_SIZE); m.r1 = (U32)(((obj->fontm12)*65536)/FONT_INTERNAL_SIZE);
1402     m.r0 = (U32)(((obj->fontm21)*65536)/FONT_INTERNAL_SIZE); m.sy = (U32)(((obj->fontm22)*65536)/FONT_INTERNAL_SIZE); 
1403     m.tx = 0;
1404     m.ty = 0;
1405     obj->fontmatrix = m;
1406 }
1407
1408 /* draws a character at x,y. */
1409 int swfoutput_drawchar(struct swfoutput* obj,double x,double y,char*character, int charnr, int u) 
1410 {
1411     swfoutput_internal*i = (swfoutput_internal*)obj->internal;
1412     swfmatrix m;
1413     m.m11 = obj->fontm11;
1414     m.m12 = obj->fontm12;
1415     m.m21 = obj->fontm21;
1416     m.m22 = obj->fontm22;
1417     m.m13 = x;
1418     m.m23 = y;
1419     return drawchar(obj, obj->swffont, character, charnr, u, &m);
1420 }
1421
1422 static void endpage(struct swfoutput*obj)
1423 {
1424     swfoutput_internal*i = (swfoutput_internal*)obj->internal;
1425     if(i->shapeid>=0)
1426       endshape(obj,0);
1427     if(i->textid>=0)
1428       endtext(obj);
1429     while(i->clippos)
1430         swfoutput_endclip(obj);
1431     i->pagefinished = 1;
1432 }
1433
1434 void swfoutput_pagefeed(struct swfoutput*obj)
1435 {
1436     swfoutput_internal*i = (swfoutput_internal*)obj->internal;
1437     
1438     if(!i->pagefinished)
1439         endpage(obj);
1440
1441     if(config_insertstoptag) {
1442         ActionTAG*atag=0;
1443         atag = action_Stop(atag);
1444         atag = action_End(atag);
1445         i->tag = swf_InsertTag(i->tag,ST_DOACTION);
1446         swf_ActionSet(i->tag,atag);
1447     }
1448     i->tag = swf_InsertTag(i->tag,ST_SHOWFRAME);
1449     i->frameno ++;
1450     
1451     for(i->depth--;i->depth>=i->startdepth;i->depth--) {
1452         i->tag = swf_InsertTag(i->tag,ST_REMOVEOBJECT2);
1453         swf_SetU16(i->tag,i->depth);
1454     }
1455     i->depth = i->startdepth;
1456 }
1457
1458 static void setBackground(struct swfoutput*obj, int x1, int y1, int x2, int y2)
1459 {
1460     swfoutput_internal*i = (swfoutput_internal*)obj->internal;
1461     RGBA rgb;
1462     rgb.a = rgb.r = rgb.g = rgb.b = 0xff;
1463     SRECT r;
1464     SHAPE* s;
1465     int ls1=0,fs1=0;
1466     int shapeid = ++i->currentswfid;
1467     r.xmin = x1;
1468     r.ymin = y1;
1469     r.xmax = x2;
1470     r.ymax = y2;
1471     i->tag = swf_InsertTag(i->tag, ST_DEFINESHAPE);
1472     swf_ShapeNew(&s);
1473     fs1 = swf_ShapeAddSolidFillStyle(s, &rgb);
1474     swf_SetU16(i->tag,shapeid);
1475     swf_SetRect(i->tag,&r);
1476     swf_SetShapeHeader(i->tag,s);
1477     swf_ShapeSetAll(i->tag,s,x1,y1,ls1,fs1,0);
1478     swf_ShapeSetLine(i->tag,s,(x2-x1),0);
1479     swf_ShapeSetLine(i->tag,s,0,(y2-y1));
1480     swf_ShapeSetLine(i->tag,s,(x1-x2),0);
1481     swf_ShapeSetLine(i->tag,s,0,(y1-y2));
1482     swf_ShapeSetEnd(i->tag);
1483     swf_ShapeFree(s);
1484     i->tag = swf_InsertTag(i->tag, ST_PLACEOBJECT2);
1485     swf_ObjectPlace(i->tag,shapeid,i->depth++,0,0,0);
1486     i->tag = swf_InsertTag(i->tag, ST_PLACEOBJECT2);
1487     swf_ObjectPlaceClip(i->tag,shapeid,i->depth++,0,0,0,65535);
1488     i->cliptag = i->tag;
1489 }
1490
1491 void swfoutput_newpage(struct swfoutput*obj, int pageNum, int movex, int movey, int x1, int y1, int x2, int y2)
1492 {
1493     swfoutput_internal*i = (swfoutput_internal*)obj->internal;
1494     if(!i->firstpage && !i->pagefinished)
1495         endpage(obj);
1496
1497     swf_GetMatrix(0, &i->page_matrix);
1498     i->page_matrix.tx = movex*20;
1499     i->page_matrix.ty = movey*20;
1500
1501     if(i->cliptag && i->frameno == i->lastframeno) {
1502         SWFPLACEOBJECT obj;
1503         swf_GetPlaceObject(i->cliptag, &obj);
1504         obj.clipdepth = i->depth++;
1505         swf_ResetTag(i->cliptag, i->cliptag->id);
1506         swf_SetPlaceObject(i->cliptag, &obj);
1507         swf_PlaceObjectFree(&obj);
1508     }
1509
1510     i->min_x = x1;
1511     i->min_y = y1;
1512     i->max_x = x2;
1513     i->max_y = y2;
1514     
1515     msg("<notice> processing page %d (%dx%d:%d:%d)", pageNum,x2-x1,y2-y1, x1, y1);
1516
1517     x1*=20;y1*=20;x2*=20;y2*=20;
1518
1519     /* set clipping/background rectangle */
1520     /* TODO: this should all be done in SWFOutputDev */
1521     setBackground(obj, x1, y1, x2, y2);
1522
1523     /* increase SWF's bounding box */
1524     SRECT r;
1525     r.xmin = x1;
1526     r.ymin = y1;
1527     r.xmax = x2;
1528     r.ymax = y2;
1529     swf_ExpandRect2(&i->swf.movieSize, &r);
1530
1531     i->lastframeno = i->frameno;
1532     i->firstpage = 0;
1533     i->pagefinished = 0;
1534 }
1535
1536 /* initialize the swf writer */
1537 void swfoutput_init(struct swfoutput* obj)
1538 {
1539     memset(obj, 0, sizeof(struct swfoutput));
1540     obj->internal = init_internal_struct();
1541
1542     swfoutput_internal*i = (swfoutput_internal*)obj->internal;
1543
1544     SRECT r;
1545     RGBA rgb;
1546
1547     msg("<verbose> initializing swf output for size %d*%d\n", i->max_x,i->max_y);
1548
1549     obj->swffont = 0;
1550     obj->drawmode = -1;
1551     
1552     memset(&i->swf,0x00,sizeof(SWF));
1553
1554     i->swf.fileVersion    = config_flashversion;
1555     i->swf.frameRate      = 0x0040; // 1 frame per 4 seconds
1556     i->swf.movieSize.xmin = 0;
1557     i->swf.movieSize.ymin = 0;
1558     i->swf.movieSize.xmax = 0;
1559     i->swf.movieSize.ymax = 0;
1560     
1561     i->swf.firstTag = swf_InsertTag(NULL,ST_SETBACKGROUNDCOLOR);
1562     i->tag = i->swf.firstTag;
1563     rgb.a = rgb.r = rgb.g = rgb.b = 0xff;
1564     swf_SetRGB(i->tag,&rgb);
1565
1566     i->startdepth = i->depth = 3; /* leave room for clip and background rectangle */
1567     
1568     if(config_protect)
1569       i->tag = swf_InsertTag(i->tag, ST_PROTECT);
1570 }
1571
1572 static void startshape(struct swfoutput*obj)
1573 {
1574     swfoutput_internal*i = (swfoutput_internal*)obj->internal;
1575     RGBA rgb;
1576     SRECT r;
1577
1578     if(i->textid>=0)
1579         endtext(obj);
1580
1581     i->tag = swf_InsertTag(i->tag,ST_DEFINESHAPE);
1582
1583     swf_ShapeNew(&i->shape);
1584     i->linestyleid = swf_ShapeAddLineStyle(i->shape,i->linewidth,&obj->strokergb);
1585     rgb.r = obj->fillrgb.r;
1586     rgb.g = obj->fillrgb.g;
1587     rgb.b = obj->fillrgb.b;
1588     i->fillstyleid = swf_ShapeAddSolidFillStyle(i->shape,&obj->fillrgb);
1589
1590     i->shapeid = ++i->currentswfid;
1591     swf_SetU16(i->tag,i->shapeid);  // ID
1592
1593     i->bboxrectpos = i->tag->len;
1594     r.xmin = 0;
1595     r.ymin = 0;
1596     r.xmax = 20*i->max_x;
1597     r.ymax = 20*i->max_y;
1598     swf_SetRect(i->tag,&r);
1599    
1600     memset(&i->bboxrect, 0, sizeof(i->bboxrect));
1601
1602     swf_SetShapeStyles(i->tag,i->shape);
1603     swf_ShapeCountBits(i->shape,NULL,NULL);
1604     swf_SetShapeBits(i->tag,i->shape);
1605
1606     /* TODO: do we really need this? */
1607     swf_ShapeSetAll(i->tag,i->shape,/*x*/0,/*y*/0,i->linestyleid,0,0);
1608     i->swflastx=i->swflasty=0;
1609     i->lastwasfill = 0;
1610     i->shapeisempty = 1;
1611 }
1612
1613 static void starttext(struct swfoutput*obj)
1614 {
1615     swfoutput_internal*i = (swfoutput_internal*)obj->internal;
1616     if(i->shapeid>=0)
1617         endshape(obj,0);
1618       
1619     i->textid = ++i->currentswfid;
1620
1621     i->swflastx=i->swflasty=0;
1622 }
1623             
1624
1625 /* TODO: move to ../lib/rfxswf */
1626 void changeRect(struct swfoutput*obj, TAG*tag, int pos, SRECT*newrect)
1627 {
1628     swfoutput_internal*i = (swfoutput_internal*)obj->internal;
1629     /* determine length of old rect */
1630     tag->pos = pos;
1631     tag->readBit = 0;
1632     SRECT old;
1633     swf_GetRect(tag, &old);
1634     swf_ResetReadBits(tag);
1635     int pos_end = tag->pos;
1636
1637     int len = tag->len - pos_end;
1638     U8*data = (U8*)malloc(len);
1639     memcpy(data, &tag->data[pos_end], len);
1640     tag->writeBit = 0;
1641     tag->len = pos;
1642     swf_SetRect(tag, newrect);
1643     swf_SetBlock(tag, data, len);
1644     free(data);
1645     tag->pos = tag->readBit = 0;
1646 }
1647
1648 void cancelshape(swfoutput*obj)
1649 {
1650     swfoutput_internal*i = (swfoutput_internal*)obj->internal;
1651     /* delete old shape tag */
1652     TAG*todel = i->tag;
1653     i->tag = i->tag->prev;
1654     swf_DeleteTag(todel);
1655     if(i->shape) {swf_ShapeFree(i->shape);i->shape=0;}
1656     i->shapeid = -1;
1657     i->bboxrectpos = -1;
1658 }
1659
1660 void fixAreas(swfoutput*obj)
1661 {
1662     swfoutput_internal*i = (swfoutput_internal*)obj->internal;
1663     if(!i->shapeisempty && i->fill &&
1664        (i->bboxrect.xmin == i->bboxrect.xmax ||
1665         i->bboxrect.ymin == i->bboxrect.ymax) &&
1666         config_minlinewidth >= 0.001
1667        ) {
1668         msg("<debug> Shape has size 0: width=%.2f height=%.2f",
1669                 (i->bboxrect.xmax-i->bboxrect.xmin)/20.0,
1670                 (i->bboxrect.ymax-i->bboxrect.ymin)/20.0
1671                 );
1672     
1673         SRECT r = i->bboxrect;
1674         
1675         if(r.xmin == r.xmax && r.ymin == r.ymax) {
1676             /* this thing comes down to a single dot- nothing to fix here */
1677             return;
1678         }
1679
1680         cancelshape(obj);
1681
1682         RGBA save_col = obj->strokergb;
1683         int  save_width = i->linewidth;
1684
1685         obj->strokergb = obj->fillrgb;
1686         i->linewidth = (int)(config_minlinewidth*20);
1687         if(i->linewidth==0) i->linewidth = 1;
1688         
1689         startshape(obj);
1690
1691         moveto(obj, i->tag, r.xmin/20.0,r.ymin/20.0);
1692         lineto(obj, i->tag, r.xmax/20.0,r.ymax/20.0);
1693
1694         obj->strokergb = save_col;
1695         i->linewidth = save_width;
1696     }
1697     
1698 }
1699
1700 static void endshape_noput(swfoutput*obj)
1701 {
1702     swfoutput_internal*i = (swfoutput_internal*)obj->internal;
1703     if(i->shapeid<0) 
1704         return;
1705     //changeRect(obj, i->tag, i->bboxrectpos, &i->bboxrect);
1706     i->shapeid = -1;
1707     if(i->shape) {
1708         swf_ShapeFree(i->shape);
1709         i->shape=0;
1710     }
1711 }
1712
1713 static void endshape(swfoutput*obj, int clipdepth)
1714 {
1715     swfoutput_internal*i = (swfoutput_internal*)obj->internal;
1716     if(i->shapeid<0) 
1717         return;
1718
1719     if(!clipdepth)
1720         fixAreas(obj);
1721         
1722     if(i->shapeisempty ||
1723        /*bbox empty?*/
1724        (i->bboxrect.xmin == i->bboxrect.xmax && 
1725         i->bboxrect.ymin == i->bboxrect.ymax) ||
1726        /*bbox outside page?*/
1727        (i->bboxrect.xmax <= i->min_x ||
1728         i->bboxrect.ymax <= i->min_y ||
1729         i->bboxrect.xmin >= i->max_x ||
1730         i->bboxrect.ymin >= i->max_y)
1731        ) 
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