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