fonts now work. output size is a small fraction of what it used to be.
[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
31 int ignoredraworder=0;
32 int drawonlyshapes=0;
33 int jpegquality=85;
34
35 typedef unsigned char u8;
36 typedef unsigned short int u16;
37 typedef unsigned long int u32;
38
39 static int drawmode;
40 static int fi;
41 static int flag_protected;
42 static SWF swf;
43 static TAG *tag;
44 static int currentswfid = 0;
45
46 static SHAPE* shape;
47 static int shapeid = -1;
48 static int textid = -1;
49
50 static int fillstyleid;
51 static int linestyleid;
52 static int swflastx=0;
53 static int swflasty=0;
54 static int lastwasfill = 0;
55 static char* filename = 0;
56 static int sizex;
57 static int sizey;
58 static char fill = 0;
59 static char storefont = 0;
60 static int depth = 1;
61 static int startdepth = 1;
62 TAG* cliptags[128];
63 int clipshapes[128];
64 u32 clipdepths[128];
65 int clippos = 0;
66
67 int CHARMIDX = 0;
68 int CHARMIDY = 0;
69
70 void startshape(struct swfoutput* obj);
71 void starttext(struct swfoutput* obj);
72 void endshape();
73 void endtext();
74
75 // matrix multiplication. changes p0
76 void transform (plotxy*p0,struct swfmatrix*m)
77 {
78     double x,y;
79     x = m->m11*p0->x+m->m12*p0->y;
80     y = m->m21*p0->x+m->m22*p0->y;
81     p0->x = x + m->m13;
82     p0->y = y + m->m23;
83 }
84
85 // write a move-to command into the swf
86 void moveto(TAG*tag, plotxy p0)
87 {
88     int rx = (int)(p0.x*20);
89     int ry = (int)(p0.y*20);
90     if(rx!=swflastx || ry!=swflasty) {
91       ShapeSetMove (tag, shape, rx,ry);
92     }
93     swflastx=rx;
94     swflasty=ry;
95 }
96
97 // write a line-to command into the swf
98 void lineto(TAG*tag, plotxy p0)
99 {
100     int rx = ((int)(p0.x*20)-swflastx);
101     int ry = ((int)(p0.y*20)-swflasty);
102     /* we can't skip this for rx=0,ry=0, those
103        are plots */
104     ShapeSetLine (tag, shape, rx,ry);
105     swflastx+=rx;
106     swflasty+=ry;
107 }
108
109 // write a spline-to command into the swf
110 void splineto(TAG*tag, plotxy control,plotxy end)
111 {
112     int cx = ((int)(control.x*20)-swflastx);
113     int cy = ((int)(control.y*20)-swflasty);
114     swflastx += cx;
115     swflasty += cy;
116     int ex = ((int)(end.x*20)-swflastx);
117     int ey = ((int)(end.y*20)-swflasty);
118     swflastx += ex;
119     swflasty += ey;
120     ShapeSetCurve(tag, shape, cx,cy,ex,ey);
121 }
122
123 /* write a line, given two points and the transformation
124    matrix. */
125 void line(TAG*tag, plotxy p0, plotxy p1, struct swfmatrix*m)
126 {
127     transform(&p0,m);
128     transform(&p1,m);
129     moveto(tag, p0);
130     lineto(tag, p1);
131 }
132
133 /* write a cubic (!) spline. This involves calling the approximate()
134    function out of spline.cc to convert it to a quadratic spline.  */
135 void spline(TAG*tag,plotxy p0,plotxy p1,plotxy p2,plotxy p3,struct swfmatrix*m)
136 {
137     double d;
138     struct qspline q[16];
139     int num;
140     int t;
141     transform(&p0,m);
142     transform(&p1,m);
143     transform(&p2,m);
144     transform(&p3,m);
145
146     num = approximate(p0,p1,p2,p3,q);
147     for(t=0;t<num;t++) {
148         moveto(tag,q[t].start);
149         splineto(tag,q[t].control, q[t].end);
150     }
151 }
152
153 /* draw a T1 outline. These are generated by pdf2swf and by t1lib.
154    (representing characters) */
155 void drawpath(TAG*tag, T1_OUTLINE*outline, struct swfmatrix*m)
156 {
157     if(tag->id != ST_DEFINEFONT &&
158         tag->id != ST_DEFINESHAPE &&
159         tag->id != ST_DEFINESHAPE2 &&
160         tag->id != ST_DEFINESHAPE3)
161     {
162         logf("<error> internal error: drawpath needs a shape tag, not %d\n",tag->id);
163         exit(1);
164     }
165     int log = 0;
166
167     double x=0,y=0;
168     double lastx=0,lasty=0;
169     double firstx=0,firsty=0;
170     int init=1;
171     if(log) printf("shape-start %d\n", fill);
172
173     while (outline)
174     {
175         x += (outline->dest.x/(float)0xffff);
176         y += (outline->dest.y/(float)0xffff);
177         if(outline->type == T1_PATHTYPE_MOVE)
178         {
179             if(((int)(lastx*20) != (int)(firstx*20) ||
180                 (int)(lasty*20) != (int)(firsty*20)) &&
181                      fill)
182             {
183                 plotxy p0;
184                 plotxy p1;
185                 p0.x=lastx;
186                 p0.y=lasty;
187                 p1.x=firstx;
188                 p1.y=firsty;
189                 if(log) printf("fix: %f,%f -> %f,%f\n",p0.x,p0.y,p1.x,p1.y);
190                 line(tag, p0, p1, m);
191             }
192             firstx=x;
193             firsty=y;
194         }
195         else if(outline->type == T1_PATHTYPE_LINE) 
196         {
197             plotxy p0;
198             plotxy p1;
199             p0.x=lastx;
200             p0.y=lasty;
201             p1.x=x;
202             p1.y=y;
203             if(log) printf("line: %f,%f -> %f,%f\n",p0.x,p0.y,p1.x,p1.y);
204             line(tag, p0,p1,m);
205         }
206         else if(outline->type == T1_PATHTYPE_BEZIER)
207         {
208             plotxy p0;
209             plotxy p1;
210             plotxy p2;
211             plotxy p3;
212             T1_BEZIERSEGMENT*o2 = (T1_BEZIERSEGMENT*)outline;
213             p0.x=x; 
214             p0.y=y;
215             p1.x=o2->C.x/(float)0xffff+lastx;
216             p1.y=o2->C.y/(float)0xffff+lasty;
217             p2.x=o2->B.x/(float)0xffff+lastx;
218             p2.y=o2->B.y/(float)0xffff+lasty;
219             p3.x=lastx;
220             p3.y=lasty;
221             if(log) printf("spline: %f,%f -> %f,%f\n",p3.x,p3.y,p0.x,p0.y);
222             spline(tag,p0,p1,p2,p3,m);
223         } 
224         else {
225          logf("<error> drawpath: unknown outline type:%d\n", outline->type);
226         }
227         lastx=x;
228         lasty=y;
229         outline = outline->link;
230     }
231     if(((int)(lastx*20) != (int)(firstx*20) ||
232         (int)(lasty*20) != (int)(firsty*20)) &&
233              fill)
234     {
235         plotxy p0;
236         plotxy p1;
237         p0.x=lastx;
238         p0.y=lasty;
239         p1.x=firstx;
240         p1.y=firsty;
241         if(log) printf("fix: %f,%f -> %f,%f\n",p0.x,p0.y,p1.x,p1.y);
242         line(tag, p0, p1, m);
243     }
244     if(log) printf("shape-end\n");
245 }
246
247 int colorcompare(RGBA*a,RGBA*b)
248 {
249
250     if(a->r!=b->r ||
251        a->g!=b->g ||
252        a->b!=b->b ||
253        a->a!=b->a) {
254         return 0;
255     }
256     return 1;
257 }
258
259 static const int CHARDATAMAX = 1024;
260 struct chardata {
261     int charid;
262     int fontid;
263     int x;
264     int y;
265     int size;
266     RGBA color;
267 } chardata[CHARDATAMAX];
268 int chardatapos = 0;
269
270 int once=0;
271 void putcharacters(TAG*tag)
272 {
273     int t;
274     SWFFONT font;
275     RGBA color;
276     color.r = chardata[0].color.r^255;
277     color.g = 0;
278     color.b = 0;
279     color.a = 0;
280     int lastfontid;
281     int lastx;
282     int lasty;
283     int lastsize;
284     int charids[128];
285     int charadvance[128];
286     int charstorepos;
287     int pass;
288     int glyphbits=1; //TODO: can this be zero?
289     int advancebits=1;
290
291     if(tag->id != ST_DEFINETEXT &&
292         tag->id != ST_DEFINETEXT2) {
293         logf("<error> internal error: putcharacters needs an text tag, not %d\n",tag->id);
294         exit(1);
295     }
296
297     for(pass = 0; pass < 2; pass++)
298     {
299         charstorepos = 0;
300         lastfontid = -1;
301         lastx = CHARMIDX;
302         lasty = CHARMIDY;
303         lastsize = -1;
304
305         if(pass==1)
306         {
307             advancebits++;
308             SetU8(tag, glyphbits);
309             SetU8(tag, advancebits);
310         }
311
312         for(t=0;t<=chardatapos;t++)
313         {
314             if(lastfontid != chardata[t].fontid || 
315                     lastx!=chardata[t].x ||
316                     lasty!=chardata[t].y ||
317                     !colorcompare(&color, &chardata[t].color) ||
318                     charstorepos==127 ||
319                     lastsize != chardata[t].size ||
320                     t == chardatapos)
321             {
322                 if(charstorepos && pass==0)
323                 {
324                     int s;
325                     for(s=0;s<charstorepos;s++)
326                     {
327                         while(charids[s]>=(1<<glyphbits))
328                             glyphbits++;
329                         while(charadvance[s]>=(1<<advancebits))
330                             advancebits++;
331                     }
332                 }
333                 if(charstorepos && pass==1)
334                 {
335                     tag->bitcount = 0;
336                     SetBits(tag, 0, 1); // GLYPH Record
337                     SetBits(tag, charstorepos, 7); // one glyph
338                     int s;
339                     for(s=0;s<charstorepos;s++)
340                     {
341                         SetBits(tag, charids[s], glyphbits);
342                         SetBits(tag, charadvance[s], advancebits);
343                     }
344                 }
345                 charstorepos = 0;
346
347                 if(pass == 1 && t<chardatapos)
348                 {
349                     RGBA*newcolor=0;
350                     SWFFONT*newfont=0;
351                     int newx = 0;
352                     int newy = 0;
353                     if(lastx != chardata[t].x ||
354                        lasty != chardata[t].y)
355                     {
356                         newx=chardata[t].x;
357                         newy=chardata[t].y;
358                     }
359                     if(!colorcompare(&color, &chardata[t].color)) 
360                     {
361                         color = chardata[t].color;
362                         newcolor = &color;
363                     }
364                     font.id = chardata[t].fontid;
365                     if(lastfontid != chardata[t].fontid || lastsize != chardata[t].size)
366                         newfont = &font;
367
368                     tag->bitcount = 0;
369                     TextSetInfoRecord(tag, newfont, chardata[t].size, newcolor, newx,newy);
370                 }
371
372                 lastfontid = chardata[t].fontid;
373                 lastx = chardata[t].x;
374                 lasty = chardata[t].y;
375                 lastsize = chardata[t].size;
376             }
377
378             if(t==chardatapos)
379                     break;
380
381             int advance;
382             int nextt = t==chardatapos-1?t:t+1;
383             int rel = chardata[nextt].x-chardata[t].x;
384             if(rel>=0 && (rel<(1<<(advancebits-1)) || pass==0)) {
385                advance = rel;
386                lastx=chardata[nextt].x;
387             }
388             else {
389                advance = 0;
390                lastx=chardata[t].x;
391             }
392             charids[charstorepos] = chardata[t].charid;
393             charadvance[charstorepos] = advance;
394             charstorepos ++;
395         }
396     }
397     chardatapos = 0;
398 }
399
400 void putcharacter(struct swfoutput*obj, int fontid, int charid, int x,int y, int size)
401 {
402     if(chardatapos == CHARDATAMAX)
403     {
404         endtext();
405         starttext(obj);
406     }
407     chardata[chardatapos].fontid = fontid;
408     chardata[chardatapos].charid = charid;
409     chardata[chardatapos].x = x;
410     chardata[chardatapos].y = y;
411     chardata[chardatapos].color = obj->fillrgb;
412     chardata[chardatapos].size = size;
413     chardatapos++;
414 }
415
416
417 /* process a character. */
418 void drawchar(struct swfoutput*obj, SWFFont*font, char*character, swfmatrix*m)
419 {
420     int usefonts=1;
421     if(m->m12!=0 || m->m21!=0)
422         usefonts=0;
423     if(m->m11 != m->m22)
424         usefonts=0;
425
426     if(usefonts && ! drawonlyshapes)
427     {
428         int charid = font->getSWFCharID(character);
429         if(shapeid>=0)
430             endshape();
431         if(textid<0)
432             starttext(obj);
433         putcharacter(obj, font->swfid, charid, (int)(m->m13*20),(int)(m->m23*20),
434                 (int)(m->m11*20/2+0.5)); //where does the /2 come from?
435     }
436     else
437     {
438         T1_OUTLINE*outline = font->getOutline(character);
439         char* charname = character;
440
441         if(!outline) {
442          logf("Didn't find %s in current charset (%s)", character,font->getName());
443          return;
444         }
445         
446         swfmatrix m2=*m;    
447         m2.m11/=100;
448         m2.m21/=100;
449         m2.m12/=100;
450         m2.m22/=100;
451
452         if(textid>=0)
453             endtext();
454         if(shapeid<0)
455             startshape(obj);
456
457         if(!lastwasfill)
458          ShapeSetStyle(tag,shape,0x8000,fillstyleid,0);
459         lastwasfill = 1;
460
461         int lf = fill;
462         fill = 1;
463         drawpath(tag, outline, &m2);
464         fill = lf;
465     }
466 }
467
468 /* draw a curved polygon. */
469 void swfoutput_drawpath(swfoutput*output, T1_OUTLINE*outline, struct swfmatrix*m)
470 {
471     if(textid>=0)
472         endtext();
473     if(shapeid<0)
474         startshape(output);
475
476     if(lastwasfill && !fill)
477     {
478      ShapeSetStyle(tag,shape,linestyleid,0x8000,0);
479      lastwasfill = 0;
480     }
481     if(!lastwasfill && fill)
482     {
483      ShapeSetStyle(tag,shape,0x8000,fillstyleid,0);
484      lastwasfill = 1;
485     }
486
487     drawpath(tag, outline,m); 
488 }
489
490 /* SWFFont: copy all t1 font outlines to a local 
491    array. */
492 SWFFont::SWFFont(char*name, int id, char*filename)
493 {
494     if(!T1_GetFontName(id))
495         T1_LoadFont(id);
496
497     this->name = strdup(T1_GetFontFileName(id));
498     this->fontid = strdup(name);
499     this->t1id = id;
500
501     char**a= T1_GetAllCharNames(id);
502     int t=0, outlinepos=0;
503     char*map[256];
504     while(a[t])
505         t++;
506  
507     this->charnum = t;
508     if(!t) 
509         return;
510     logf("<verbose> Font %s(%d): Storing %d outlines.\n", name, id, t);
511     
512     outline = (T1_OUTLINE**)malloc(t*sizeof(T1_OUTLINE*));
513     charname = (char**)malloc(t*sizeof(char*));
514     used = (char*)malloc(t*sizeof(char));
515     char2swfcharid = (U16*)malloc(t*2);
516     swfcharid2char = (U16*)malloc(t*2);
517     swfcharpos = 0;
518
519     memset(used,0,t*sizeof(char));
520
521     this->swfid = ++currentswfid;
522
523     
524     t=0;
525     while(*a)
526     {
527         map[t] = *a;
528         a++;
529         t++;
530         if(t==256 || !*a) {
531             int s;
532             for(s=t;s<256;s++)
533                 map[s] = ".notdef";
534
535             int ret = T1_ReencodeFont(id, map);
536             if(ret) {
537              T1_DeleteFont(id);
538              T1_LoadFont(id);
539              int ret = T1_ReencodeFont(id, map);
540              if(ret)
541                fprintf(stderr,"Can't reencode font: (%s) ret:%d\n",filename, ret);
542             }
543
544             // parsecharacters
545             for(s=0;s<t;s++)
546             {
547                 this->outline[outlinepos] = T1_CopyOutline(T1_GetCharOutline(id, s, 100.0, 0));
548                 this->charname[outlinepos] = strdup(T1_GetCharName(id, s));
549                 outlinepos++;
550             }
551             t=0;
552         }
553     }
554 }
555
556 /* free all tables, write out definefont tags */
557 SWFFont::~SWFFont()
558 {
559     int t,usednum=0;
560     int*ptr = (int*)malloc(swfcharpos*sizeof(int));
561
562     for(t=0;t<charnum;t++)
563         if(used[t]) usednum++;
564
565     if(usednum && !drawonlyshapes)
566     {
567         logf("<verbose> Font %s has %d used characters",fontid, usednum);
568         TAG*ftag = InsertTag(swf.FirstTag,ST_DEFINEFONT);
569         SetU16(ftag, this->swfid);
570         int initpos = GetDataSize(ftag);
571         swfmatrix m;
572         m.m11 = m.m22 = 1;
573         m.m21 = m.m12 = 0;
574         m.m13 = CHARMIDX;
575         m.m23 = CHARMIDY;
576         for(t=0;t<swfcharpos;t++) 
577         {
578             ptr[t] = GetDataSize(ftag);
579             SetU16(ftag, 0x1234);
580         }
581         for(t=0;t<swfcharpos;t++)
582         {
583             *(U16*)&ftag->data[ptr[t]] = GetDataSize(ftag)-initpos;
584             swflastx=0;
585             swflasty=0;
586             SetU8(ftag,0x10); //0 fill bits, 0 linestyle bits
587             SHAPE s;
588             s.bits.fill = 1;
589             s.bits.line = 0;
590             ShapeSetStyle(ftag,&s,0,1,0);
591             int lastfill = fill;
592             fill = 1;
593             storefont = 1;
594             drawpath(ftag, outline[swfcharid2char[t]],&m);
595             storefont = 0;
596             fill = lastfill;
597             ShapeSetEnd(ftag);
598         }
599     }
600
601     free(ptr);
602     free(outline);
603     for(t=0;t<charnum;t++)
604         free(charname[t]);
605     free(charname);
606     free(used);
607     free(swfcharid2char);
608     free(char2swfcharid);
609 }
610
611 T1_OUTLINE*SWFFont::getOutline(char*name)
612 {
613     int t;
614     for(t=0;t<this->charnum;t++) {
615         if(!strcmp(this->charname[t],name)) {
616
617             if(!used[t])
618             {
619                 swfcharid2char[swfcharpos] = t;
620                 char2swfcharid[t] = swfcharpos;
621                 swfcharpos++;
622                 used[t] = 1;
623             }
624             return outline[t];
625         }
626     }
627     return 0;
628 }
629
630 int SWFFont::getSWFCharID(char*name)
631 {
632     int t;
633     for(t=0;t<this->charnum;t++) {
634         if(!strcmp(this->charname[t],name)) {
635            
636             if(!used[t])
637             {
638                 swfcharid2char[swfcharpos] = t;
639                 char2swfcharid[t] = swfcharpos++;
640                 used[t] = 1;
641             }
642             return char2swfcharid[t];
643         }
644     }
645     return 0;
646 }
647
648 char*SWFFont::getName()
649 {
650     return this->name;
651 }
652
653 struct fontlist_t 
654 {
655     SWFFont * font;
656     fontlist_t*next;
657 } *fontlist = 0;
658
659 /* set's the t1 font index of the font to use for swfoutput_drawchar(). */
660 void swfoutput_setfont(struct swfoutput*obj, char*fontid, int t1id, char*filename)
661 {
662     fontlist_t*last=0,*iterator;
663     if(obj->font && !strcmp(obj->font->fontid,fontid))
664         return;
665
666     iterator = fontlist;
667     while(iterator) {
668         if(!strcmp(iterator->font->fontid,fontid))
669             break;
670         last = iterator;
671         iterator = iterator->next;
672     }
673     if(iterator) 
674     {
675         obj->font = iterator->font;
676         return ;
677     }
678
679     if(t1id<0) {
680         logf("<error> internal error: t1id:%d, fontid:%s\n", t1id,fontid);
681     }
682     
683     SWFFont*font = new SWFFont(fontid, t1id, filename);
684     iterator = new fontlist_t;
685     iterator->font = font;
686     iterator->next = 0;
687
688     if(last) 
689         last->next = iterator;
690     else 
691         fontlist = iterator;
692     obj->font = font;
693 }
694
695 int swfoutput_queryfont(struct swfoutput*obj, char*fontid)
696 {
697     fontlist_t *iterator = fontlist;
698     while(iterator) {
699         if(!strcmp(iterator->font->fontid,fontid))
700             return 1;
701         iterator = iterator->next;
702     }
703     return 0;
704 }
705
706 /* set's the matrix which is to be applied to characters drawn by
707    swfoutput_drawchar() */
708 void swfoutput_setfontmatrix(struct swfoutput*obj,double m11,double m12,
709                                                   double m21,double m22)
710 {
711     if(obj->fontm11 == m11 &&
712        obj->fontm12 == m12 &&
713        obj->fontm21 == m21 &&
714        obj->fontm22 == m22)
715         return;
716 //    if(textid>=0)
717 //      endtext();
718     obj->fontm11 = m11;
719     obj->fontm12 = m12;
720     obj->fontm21 = m21;
721     obj->fontm22 = m22;
722 }
723
724 /* draws a character at x,y. */
725 void swfoutput_drawchar(struct swfoutput* obj,double x,double y,char*character) 
726 {
727     swfmatrix m;
728     m.m11 = obj->fontm11;
729     m.m12 = obj->fontm12;
730     m.m21 = obj->fontm21;
731     m.m22 = obj->fontm22;
732     m.m13 = x;
733     m.m23 = y;
734     drawchar(obj, obj->font, character, &m);
735 }
736
737 /* initialize the swf writer */
738 void swfoutput_init(struct swfoutput* obj, char*_filename, int _sizex, int _sizey) 
739 {
740   GLYPH *glyph;
741   RGBA rgb;
742   SRECT r;
743   memset(obj, 0, sizeof(struct swfoutput));
744   filename = _filename;
745   sizex = _sizex;
746   sizey = _sizey;
747
748   logf("<verbose> initializing swf output for size %d*%d\n", sizex,sizey);
749
750   obj->font = 0;
751   
752   memset(&swf,0x00,sizeof(SWF));
753
754   swf.FileVersion    = 4;
755 //  swf.FrameRate      = 0x1900;
756   swf.FrameRate      = 0x0040; // 1 frame per 4 seconds
757   swf.MovieSize.xmax = 20*sizex;
758   swf.MovieSize.ymax = 20*sizey;
759   
760   swf.FirstTag = InsertTag(NULL,ST_SETBACKGROUNDCOLOR);
761   tag = swf.FirstTag;
762   rgb.r = 0xff;
763   rgb.g = 0xff;
764   rgb.b = 0xff;
765   SetRGB(tag,&rgb);
766   if(flag_protected)
767     tag = InsertTag(tag, ST_PROTECT);
768   depth = 1;
769   startdepth = depth;
770 }
771
772 void swfoutput_setprotected() //write PROTECT tag
773 {
774   flag_protected = 1;
775 }
776
777 void startshape(struct swfoutput*obj)
778 {
779   RGBA rgb;
780   SRECT r;
781
782   if(textid>=0)
783       endtext();
784
785   tag = InsertTag(tag,ST_DEFINESHAPE);
786
787   NewShape(&shape);
788   linestyleid = ShapeAddLineStyle(shape,obj->linewidth,&obj->strokergb);
789   rgb.r = obj->fillrgb.r;
790   rgb.g = obj->fillrgb.g;
791   rgb.b = obj->fillrgb.b;
792   fillstyleid = ShapeAddSolidFillStyle(shape,&obj->fillrgb);
793
794   shapeid = ++currentswfid;
795   SetU16(tag,shapeid);  // ID
796
797   r.xmin = 0;
798   r.ymin = 0;
799   r.xmax = 20*sizex;
800   r.ymax = 20*sizey;
801   
802   SetRect(tag,&r);
803
804   SetShapeStyles(tag,shape);
805   ShapeCountBits(shape,NULL,NULL);
806   SetShapeBits(tag,shape);
807
808   ShapeSetAll(tag,shape,/*x*/0,/*y*/0,linestyleid,0,0);
809   swflastx=swflasty=0;
810   lastwasfill = 0;
811 }
812
813 void starttext(struct swfoutput*obj)
814 {
815   SRECT r;
816   MATRIX m;
817   if(shapeid>=0)
818       endshape();
819   tag = InsertTag(tag,ST_DEFINETEXT);
820   textid = ++currentswfid;
821   SetU16(tag, textid);
822
823   r.xmin = 0;
824   r.ymin = 0;
825   r.xmax = 20*sizex;
826   r.ymax = 20*sizey;
827   
828   SetRect(tag,&r);
829
830   m.sx = 65536;
831   m.sy = 65536;
832   m.r0 = 0;
833   m.r1 = 0;
834   m.tx = 0;
835   m.ty = 0;
836  
837   SetMatrix(tag,&m);
838   swflastx=swflasty=0;
839 }
840
841 void endshape()
842 {
843     if(shapeid<0) 
844         return;
845     ShapeSetEnd(tag);
846     tag = InsertTag(tag,ST_PLACEOBJECT2);
847     ObjectPlace(tag,shapeid,/*depth*/depth++,NULL,NULL,NULL);
848     shapeid = -1;
849 }
850
851 void endtext()
852 {
853     if(textid<0)
854         return;
855     putcharacters(tag);
856     SetU8(tag,0);
857     tag = InsertTag(tag,ST_PLACEOBJECT2);
858     ObjectPlace(tag,textid,/*depth*/depth++,NULL,NULL,NULL);
859     textid = -1;
860 }
861
862 void endpage(struct swfoutput*obj)
863 {
864     if(shapeid>=0)
865       endshape();
866     if(textid>=0)
867       endtext();
868     while(clippos)
869         swfoutput_endclip(obj);
870     tag = InsertTag(tag,ST_SHOWFRAME);
871 }
872
873 void swfoutput_newpage(struct swfoutput*obj)
874 {
875     endpage(obj);
876
877     for(depth--;depth>=startdepth;depth--) {
878         tag = InsertTag(tag,ST_REMOVEOBJECT2);
879         SetU16(tag,depth);
880     }
881
882     depth = 1;
883     startdepth = depth;
884 }
885
886 /* "destroy" like in (oo-terminology) "destructor". Perform cleaning
887    up, complete the swf, and write it out. */
888 void swfoutput_destroy(struct swfoutput* obj) 
889 {
890     endpage(obj);
891     fontlist_t *tmp,*iterator = fontlist;
892     while(iterator) {
893         delete iterator->font;
894         iterator->font = 0;
895         tmp = iterator;
896         iterator = iterator->next;
897         delete tmp;
898     }
899
900     T1_CloseLib();
901     if(!filename) 
902         return;
903     if(filename)
904      fi = open(filename, O_CREAT|O_TRUNC|O_WRONLY, 0777);
905     else
906      fi = 1; // stdout
907     
908     if(fi<=0) {
909      logf("<fatal> Could not create \"%s\". ", filename);
910      exit(1);
911     }
912  
913     tag = InsertTag(tag,ST_END);
914
915     if FAILED(WriteSWF(fi,&swf)) 
916      logf("<error> WriteSWF() failed.\n");
917     if(filename)
918      close(fi);
919     logf("<notice> SWF written\n");
920 }
921
922 void swfoutput_setdrawmode(swfoutput* obj, int mode)
923 {
924     drawmode = mode;
925     if(mode == DRAWMODE_FILL)
926      fill = 1;
927     else if(mode == DRAWMODE_EOFILL)
928      fill = 1;
929     else if(mode == DRAWMODE_STROKE)
930      fill = 0;
931     else if(mode == DRAWMODE_CLIP)
932      fill = 1;
933     else if(mode == DRAWMODE_EOCLIP)
934      fill = 1;
935 }
936
937 void swfoutput_setfillcolor(swfoutput* obj, u8 r, u8 g, u8 b, u8 a)
938 {
939     if(obj->fillrgb.r == r &&
940        obj->fillrgb.g == g &&
941        obj->fillrgb.b == b &&
942        obj->fillrgb.a == a) return;
943
944     if(shapeid>=0)
945      endshape();
946     obj->fillrgb.r = r;
947     obj->fillrgb.g = g;
948     obj->fillrgb.b = b;
949     obj->fillrgb.a = a;
950 }
951
952 void swfoutput_setstrokecolor(swfoutput* obj, u8 r, u8 g, u8 b, u8 a)
953 {
954     if(obj->strokergb.r == r &&
955        obj->strokergb.g == g &&
956        obj->strokergb.b == b &&
957        obj->strokergb.a == a) return;
958
959     if(shapeid>=0)
960      endshape();
961     obj->strokergb.r = r;
962     obj->strokergb.g = g;
963     obj->strokergb.b = b;
964     obj->strokergb.a = a;
965 }
966
967 void swfoutput_setlinewidth(struct swfoutput*obj, double linewidth)
968 {
969     if(obj->linewidth == (u16)(linewidth*20))
970         return;
971
972     if(shapeid>=0)
973      endshape();
974     obj->linewidth = (u16)(linewidth*20);
975 }
976
977
978 void swfoutput_startclip(swfoutput*obj, T1_OUTLINE*outline, struct swfmatrix*m)
979 {
980     if(textid>=0)
981      endtext();
982     if(shapeid>=0)
983      endshape();
984
985     if(clippos >= 127)
986     {
987         logf("<warning> Too many clip levels.");
988         clippos --;
989     } 
990
991     startshape(obj);
992     swfoutput_setdrawmode(obj, DRAWMODE_CLIP);
993     swfoutput_drawpath(obj, outline, m);
994     ShapeSetEnd(tag);
995
996     tag = InsertTag(tag,ST_PLACEOBJECT2);
997     cliptags[clippos] = tag;
998     clipshapes[clippos] = shapeid;
999     clipdepths[clippos] = depth++;
1000     clippos++;
1001     shapeid = -1;
1002 }
1003
1004 void swfoutput_endclip(swfoutput*obj)
1005 {
1006     if(textid>=0)
1007      endtext();
1008     if(shapeid>=0)
1009      endshape();
1010
1011     if(!clippos) {
1012         logf("<error> Invalid end of clipping region");
1013         return;
1014     }
1015     clippos--;
1016     PlaceObject(cliptags[clippos],clipshapes[clippos],clipdepths[clippos],NULL,NULL,NULL,depth++);
1017 }
1018
1019 void swfoutput_drawimagefile(struct swfoutput*, char*filename, int sizex,int sizey, 
1020         double x1,double y1,
1021         double x2,double y2,
1022         double x3,double y3,
1023         double x4,double y4)
1024 {
1025     if(shapeid>=0)
1026      endshape();
1027     if(textid>=0)
1028      endtext();
1029
1030     RGBA rgb;
1031     SRECT r;
1032     int lsid=0;
1033     int fsid;
1034     int bitid;
1035     struct plotxy p1,p2,p3,p4;
1036     int myshapeid;
1037     double xmax=x1,ymax=y1,xmin=x1,ymin=y1;
1038     if(x2>xmax) xmax=x2;
1039     if(y2>ymax) ymax=y2;
1040     if(x2<xmin) xmin=x2;
1041     if(y2<ymin) ymin=y2;
1042     if(x3>xmax) xmax=x3;
1043     if(y3>ymax) ymax=y3;
1044     if(x3<xmin) xmin=x3;
1045     if(y3<ymin) ymin=y3;
1046     if(x4>xmax) xmax=x4;
1047     if(y4>ymax) ymax=y4;
1048     if(x4<xmin) xmin=x4;
1049     if(y4<ymin) ymin=y4;
1050     p1.x=x1;
1051     p1.y=y1;
1052     p2.x=x2;
1053     p2.y=y2;
1054     p3.x=x3;
1055     p3.y=y3;
1056     p4.x=x4;
1057     p4.y=y4;
1058     
1059     MATRIX m;
1060     m.sx = (int)(65536*20*(x4-x1))/sizex;
1061     m.r1 = -(int)(65536*20*(y4-y1))/sizex;
1062     m.r0 = (int)(65536*20*(x1-x2))/sizey;
1063     m.sy = -(int)(65536*20*(y1-y2))/sizey;
1064
1065     m.tx = (int)(x1*20);
1066     m.ty = (int)(y1*20);
1067
1068     bitid = ++currentswfid;
1069   
1070     /* bitmap */
1071     tag = InsertTag(tag,ST_DEFINEBITSJPEG2);
1072     SetU16(tag, bitid);
1073     SetJPEGBits(tag, filename, jpegquality);
1074
1075     /* shape */
1076     myshapeid = ++currentswfid;
1077     tag = InsertTag(tag,ST_DEFINESHAPE);
1078     NewShape(&shape);
1079     //lsid = ShapeAddLineStyle(shape,obj->linewidth,&obj->strokergb);
1080     //fsid = ShapeAddSolidFillStyle(shape,&obj->fillrgb);
1081     fsid = ShapeAddBitmapFillStyle(shape,&m,bitid,0);
1082     SetU16(tag, myshapeid);
1083     r.xmin = (int)(xmin*20);
1084     r.ymin = (int)(ymin*20);
1085     r.xmax = (int)(xmax*20);
1086     r.ymax = (int)(ymax*20);
1087     SetRect(tag,&r);
1088     SetShapeStyles(tag,shape);
1089     ShapeCountBits(shape,NULL,NULL);
1090     SetShapeBits(tag,shape);
1091     ShapeSetAll(tag,shape,/*x*/0,/*y*/0,lsid,fsid,0);
1092     swflastx = swflasty = 0;
1093     moveto(tag, p1);
1094     lineto(tag, p2);
1095     lineto(tag, p3);
1096     lineto(tag, p4);
1097     lineto(tag, p1);
1098     /*
1099     ShapeMoveTo  (tag, shape, (int)(x1*20),(int)(y1*20));
1100     ShapeSetLine (tag, shape, (int)(x1*20);
1101     ShapeSetLine (tag, shape, x*20,0);
1102     ShapeSetLine (tag, shape, 0,-y*20);
1103     ShapeSetLine (tag, shape, -x*20,0);*/
1104     ShapeSetEnd(tag);
1105
1106     /* instance */
1107     tag = InsertTag(tag,ST_PLACEOBJECT2);
1108     ObjectPlace(tag,myshapeid,/*depth*/depth++,NULL,NULL,NULL);
1109 }
1110