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