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