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