fix for t1lib-5.0.0
[swftools.git] / pdf2swf / swfoutput.cc
index 2353efe..8a129bc 100644 (file)
@@ -35,6 +35,9 @@ int ignoredraworder=0;
 int drawonlyshapes=0;
 int jpegquality=85;
 int storeallcharacters=0;
+int enablezlib=0;
+int insertstoptag=0;
+int flashversion=4;
 static int flag_protected = 0;
 
 typedef unsigned char u8;
@@ -71,13 +74,15 @@ int clippos = 0;
 int CHARMIDX = 0;
 int CHARMIDY = 0;
 
-void startshape(struct swfoutput* obj);
-void starttext(struct swfoutput* obj);
-void endshape();
-void endtext();
+char fillstylechanged = 0;
+
+static void startshape(struct swfoutput* obj);
+static void starttext(struct swfoutput* obj);
+static void endshape();
+static void endtext();
 
 // matrix multiplication. changes p0
-void transform (plotxy*p0,struct swfmatrix*m)
+static void transform (plotxy*p0,struct swfmatrix*m)
 {
     double x,y;
     x = m->m11*p0->x+m->m12*p0->y;
@@ -87,19 +92,22 @@ void transform (plotxy*p0,struct swfmatrix*m)
 }
 
 // write a move-to command into the swf
-void moveto(TAG*tag, plotxy p0)
+static int moveto(TAG*tag, plotxy p0)
 {
     int rx = (int)(p0.x*20);
     int ry = (int)(p0.y*20);
-    if(rx!=swflastx || ry!=swflasty) {
+    if(rx!=swflastx || ry!=swflasty || fillstylechanged) {
       swf_ShapeSetMove (tag, shape, rx,ry);
+      fillstylechanged = 0;
+      swflastx=rx;
+      swflasty=ry;
+      return 1;
     }
-    swflastx=rx;
-    swflasty=ry;
+    return 0;
 }
 
 // write a line-to command into the swf
-void lineto(TAG*tag, plotxy p0)
+static void lineto(TAG*tag, plotxy p0)
 {
     int rx = ((int)(p0.x*20)-swflastx);
     int ry = ((int)(p0.y*20)-swflasty);
@@ -111,7 +119,7 @@ void lineto(TAG*tag, plotxy p0)
 }
 
 // write a spline-to command into the swf
-void splineto(TAG*tag, plotxy control,plotxy end)
+static void splineto(TAG*tag, plotxy control,plotxy end)
 {
     int cx = ((int)(control.x*20)-swflastx);
     int cy = ((int)(control.y*20)-swflasty);
@@ -121,12 +129,13 @@ void splineto(TAG*tag, plotxy control,plotxy end)
     int ey = ((int)(end.y*20)-swflasty);
     swflastx += ex;
     swflasty += ey;
-    swf_ShapeSetCurve(tag, shape, cx,cy,ex,ey);
+    if(cx || cy || ex || ey)
+       swf_ShapeSetCurve(tag, shape, cx,cy,ex,ey);
 }
 
 /* write a line, given two points and the transformation
    matrix. */
-void line(TAG*tag, plotxy p0, plotxy p1, struct swfmatrix*m)
+static void line(TAG*tag, plotxy p0, plotxy p1, struct swfmatrix*m)
 {
     transform(&p0,m);
     transform(&p1,m);
@@ -136,27 +145,45 @@ void line(TAG*tag, plotxy p0, plotxy p1, struct swfmatrix*m)
 
 /* write a cubic (!) spline. This involves calling the approximate()
    function out of spline.cc to convert it to a quadratic spline.  */
-void spline(TAG*tag,plotxy p0,plotxy p1,plotxy p2,plotxy p3,struct swfmatrix*m)
+static void spline(TAG*tag,plotxy p0,plotxy p1,plotxy p2,plotxy p3,struct swfmatrix*m)
 {
     double d;
-    struct qspline q[16];
+    struct qspline q[128];
     int num;
     int t;
     transform(&p0,m);
     transform(&p1,m);
     transform(&p2,m);
     transform(&p3,m);
-
-    num = approximate(p0,p1,p2,p3,q);
+    cspline c;
+    c.start = p3;
+    c.control1 = p2;
+    c.control2 = p1;
+    c.end = p0;
+
+    if(storefont) {
+       /* fonts use a different approximation than shapes */
+       num = cspline_approximate(&c, q, 10.0, APPROXIMATE_RECURSIVE_BINARY);
+       //num = cspline_approximate(&c, q, 10.0, APPROXIMATE_INFLECTION);
+    } else {
+       num = cspline_approximate(&c, q, 1.0, APPROXIMATE_RECURSIVE_BINARY);
+    }
     for(t=0;t<num;t++) {
-        moveto(tag,q[t].start);
+       if(!t) 
+           moveto(tag,q[t].start);
         splineto(tag,q[t].control, q[t].end);
     }
 }
 
+void resetdrawer()
+{
+    swflastx = 0;
+    swflasty = 0;
+}
+
 /* draw a T1 outline. These are generated by pdf2swf and by t1lib.
    (representing characters) */
-void drawpath(TAG*tag, T1_OUTLINE*outline, struct swfmatrix*m)
+void drawpath(TAG*tag, T1_OUTLINE*outline, struct swfmatrix*m, int log)
 {
     if(tag->id != ST_DEFINEFONT &&
         tag->id != ST_DEFINESHAPE &&
@@ -166,8 +193,6 @@ void drawpath(TAG*tag, T1_OUTLINE*outline, struct swfmatrix*m)
         logf("<error> internal error: drawpath needs a shape tag, not %d\n",tag->id);
         exit(1);
     }
-    int log = 0;
-
     double x=0,y=0;
     double lastx=0,lasty=0;
     double firstx=0,firsty=0;
@@ -247,7 +272,7 @@ void drawpath(TAG*tag, T1_OUTLINE*outline, struct swfmatrix*m)
     }
 }
 
-int colorcompare(RGBA*a,RGBA*b)
+static inline int colorcompare(RGBA*a,RGBA*b)
 {
 
     if(a->r!=b->r ||
@@ -270,7 +295,7 @@ struct chardata {
 } chardata[CHARDATAMAX];
 int chardatapos = 0;
 
-void putcharacters(TAG*tag)
+static void putcharacters(TAG*tag)
 {
     int t;
     SWFFONT font;
@@ -402,7 +427,7 @@ void putcharacters(TAG*tag)
     chardatapos = 0;
 }
 
-void putcharacter(struct swfoutput*obj, int fontid, int charid, 
+static void putcharacter(struct swfoutput*obj, int fontid, int charid, 
                     int x,int y, int size)
 {
     if(chardatapos == CHARDATAMAX)
@@ -421,7 +446,7 @@ void putcharacter(struct swfoutput*obj, int fontid, int charid,
 
 
 /* process a character. */
-void drawchar(struct swfoutput*obj, SWFFont*font, char*character, swfmatrix*m)
+static void drawchar(struct swfoutput*obj, SWFFont*font, char*character, int charnr, swfmatrix*m)
 {
     int usefonts=1;
     if(m->m12!=0 || m->m21!=0)
@@ -431,7 +456,7 @@ void drawchar(struct swfoutput*obj, SWFFont*font, char*character, swfmatrix*m)
 
     if(usefonts && ! drawonlyshapes)
     {
-        int charid = font->getSWFCharID(character);
+        int charid = font->getSWFCharID(character, charnr);
         if(shapeid>=0)
             endshape();
         if(textid<0)
@@ -441,12 +466,12 @@ void drawchar(struct swfoutput*obj, SWFFont*font, char*character, swfmatrix*m)
     }
     else
     {
-        T1_OUTLINE*outline = font->getOutline(character);
+        T1_OUTLINE*outline = font->getOutline(character, charnr);
         char* charname = character;
 
         if(!outline) {
          logf("<warning> Didn't find %s in current charset (%s)", 
-                 character,font->getName());
+                 FIXNULL(character),FIXNULL(font->getName()));
          return;
         }
         
@@ -461,13 +486,15 @@ void drawchar(struct swfoutput*obj, SWFFont*font, char*character, swfmatrix*m)
         if(shapeid<0)
             startshape(obj);
 
-        if(!lastwasfill)
+        if(!lastwasfill) {
          swf_ShapeSetStyle(tag,shape,0x8000,fillstyleid,0);
+        fillstylechanged = 1;
+       }
         lastwasfill = 1;
 
         int lf = fill;
         fill = 1;
-        drawpath(tag, outline, &m2);
+        drawpath(tag, outline, &m2, 0);
         fill = lf;
     }
 }
@@ -478,21 +505,31 @@ void swfoutput_drawpath(swfoutput*output, T1_OUTLINE*outline,
 {
     if(textid>=0)
         endtext();
+
+    /* Multiple polygons in one shape don't overlap correctly, 
+       so we better start a new shape here if the polygon is filled
+     */
+    if(shapeid>=0 && fill && !ignoredraworder) {
+       endshape();
+    }
+
     if(shapeid<0)
         startshape(output);
 
     if(lastwasfill && !fill)
     {
      swf_ShapeSetStyle(tag,shape,linestyleid,0x8000,0);
+     fillstylechanged = 1;
      lastwasfill = 0;
     }
     if(!lastwasfill && fill)
     {
      swf_ShapeSetStyle(tag,shape,0x8000,fillstyleid,0);
+     fillstylechanged = 1;
      lastwasfill = 1;
     }
 
-    drawpath(tag, outline,m); 
+    drawpath(tag, outline,m, 0); 
 }
 
 /* SWFFont: copy all t1 font outlines to a local 
@@ -505,30 +542,45 @@ SWFFont::SWFFont(char*name, int id, char*filename)
     this->name = strdup(T1_GetFontFileName(id));
     this->fontid = strdup(name);
     this->t1id = id;
-
+    
     char**a= T1_GetAllCharNames(id);
-    int t=0, outlinepos=0;
+    int t, outlinepos=0;
     char*map[256];
+
+    t=0;
     while(a[t])
         t++;
     this->charnum = t;
-    if(!t) 
+
+    if(!charnum) 
         return;
-    logf("<verbose> Font %s(%d): Storing %d outlines.\n", name, id, t);
+    logf("<verbose> Font %s(%d): Storing %d outlines.\n", FIXNULL(name), id, charnum);
+
+    this->standardtablesize = 256;
+    if(this->charnum < this->standardtablesize)
+       this->standardtablesize = this->charnum;
+    this->standardtable = (char**)malloc(standardtablesize*sizeof(char*));
+
+    for(t = 0; t < this->standardtablesize; t++) {
+       char*name = T1_GetCharName(id,t);
+       if(!name)
+           name = "";
+       standardtable[t] = strdup(name);
+    }
     
-    outline = (T1_OUTLINE**)malloc(t*sizeof(T1_OUTLINE*));
-    charname = (char**)malloc(t*sizeof(char*));
-    memset(charname, t*sizeof(char*), 0);
-    used = (char*)malloc(t*sizeof(char));
-    char2swfcharid = (U16*)malloc(t*2);
-    swfcharid2char = (U16*)malloc(t*2);
+    outline = (T1_OUTLINE**)malloc(charnum*sizeof(T1_OUTLINE*));
+    charname = (char**)malloc(charnum*sizeof(char*));
+    width = (int*)malloc(charnum*sizeof(int));
+    memset(width, 0, charnum*sizeof(int));
+    memset(charname, 0, charnum*sizeof(char*));
+    used = (char*)malloc(charnum*sizeof(char));
+    char2swfcharid = (U16*)malloc(charnum*2);
+    swfcharid2char = (U16*)malloc(charnum*2);
     swfcharpos = 0;
 
-    memset(used,0,t*sizeof(char));
+    memset(used,0,charnum*sizeof(char));
 
     this->swfid = ++currentswfid;
-
     
     t=0;
     while(*a)
@@ -553,8 +605,11 @@ SWFFont::SWFFont(char*name, int id, char*filename)
             // parsecharacters
             for(s=0;s<t;s++)
             {
+               char* name = T1_GetCharName(id, s);
+               if(!name) name = "";
                 this->outline[outlinepos] = T1_CopyOutline(T1_GetCharOutline(id, s, 100.0, 0));
-                this->charname[outlinepos] = strdup(T1_GetCharName(id, s));
+               this->width[outlinepos] = T1_GetCharWidth(id, s);
+                this->charname[outlinepos] = strdup(name);
                 outlinepos++;
             }
             t=0;
@@ -574,7 +629,7 @@ SWFFont::~SWFFont()
        for(t=0;t<this->charnum;t++) 
        {
            if(this->charname[t])
-             getSWFCharID(this->charname[t]);
+             getSWFCharID(this->charname[t], -1);
        }
     }
     
@@ -585,10 +640,10 @@ SWFFont::~SWFFont()
 
     if(usednum && !drawonlyshapes)
     {
-        logf("<verbose> Font %s has %d used characters",fontid, usednum);
+        logf("<verbose> Font %s has %d used characters",FIXNULL(fontid), usednum);
         TAG*ftag = swf_InsertTag(swf.firstTag,ST_DEFINEFONT);
         swf_SetU16(ftag, this->swfid);
-        int initpos = swf_GetDataSize(ftag);
+        int initpos = swf_GetTagLen(ftag);
         swfmatrix m;
         m.m11 = m.m22 = 1;
         m.m21 = m.m12 = 0;
@@ -597,23 +652,26 @@ SWFFont::~SWFFont()
 
         for(t=0;t<swfcharpos;t++) 
         {
-            ptr[t] = swf_GetDataSize(ftag);
+            ptr[t] = swf_GetTagLen(ftag);
             swf_SetU16(ftag, 0x1234);
         }
         for(t=0;t<swfcharpos;t++)
         {
-            *(U16*)&ftag->data[ptr[t]] = swf_GetDataSize(ftag)-initpos;
+            *(U16*)&ftag->data[ptr[t]] = 
+               SWAP16(swf_GetTagLen(ftag)-initpos);
+
             swflastx=0;
             swflasty=0;
-            swf_SetU8(ftag,0x10); //0 fill bits, 0 linestyle bits
+            swf_SetU8(ftag,0x10); //1 fill bits, 0 linestyle bits
             SHAPE s;
             s.bits.fill = 1;
             s.bits.line = 0;
             swf_ShapeSetStyle(ftag,&s,0,1,0);
+           fillstylechanged = 1;
             int lastfill = fill;
             fill = 1;
             storefont = 1;
-            drawpath(ftag, outline[swfcharid2char[t]],&m);
+            drawpath(ftag, outline[swfcharid2char[t]],&m, 0);
             storefont = 0;
             fill = lastfill;
             swf_ShapeSetEnd(ftag);
@@ -644,31 +702,45 @@ SWFFont::~SWFFont()
     free(outline);
     for(t=0;t<charnum;t++)
         free(charname[t]);
+    for(t=0;t<standardtablesize;t++)
+       if(standardtable[t]) {
+           free(standardtable[t]);
+       }
+    free(standardtable);
     free(charname);
+    free(width);
     free(used);
     free(swfcharid2char);
     free(char2swfcharid);
 }
 
-T1_OUTLINE*SWFFont::getOutline(char*name)
+T1_OUTLINE*SWFFont::getOutline(char*name, int charnr)
 {
     int t;
     for(t=0;t<this->charnum;t++) {
         if(!strcmp(this->charname[t],name)) {
-            if(!used[t])
-            {
-                swfcharid2char[swfcharpos] = t;
-                char2swfcharid[t] = swfcharpos;
-                swfcharpos++;
-                used[t] = 1;
-            }
             return outline[t];
         }
     }
+    
+    /* if we didn't find the character, maybe
+       we can find the capitalized version */
+    for(t=0;t<this->charnum;t++) {
+        if(!strcasecmp(this->charname[t],name))
+           return outline[t];
+    }
+
+    /* if we didn't find it by name, use the names of the first 256 characters
+       of the font to try a new name based on charnr */
+    if(this->standardtable && charnr>=0 && charnr < this->standardtablesize) {
+       return getOutline(this->standardtable[charnr], -1);
+    }
+
+    logf("<warning> Didn't find character '%s' in font '%s'", FIXNULL(name), this->name);
     return 0;
 }
 
-int SWFFont::getSWFCharID(char*name)
+int SWFFont::getSWFCharID(char*name, int charnr)
 {
     int t;
     for(t=0;t<this->charnum;t++) {
@@ -682,7 +754,38 @@ int SWFFont::getSWFCharID(char*name)
             return char2swfcharid[t];
         }
     }
-    logf("<warning> Didn't find character '%s' in font '%s'", name, this->name);
+
+    /* if we didn't find the character, maybe
+       we can find the capitalized version */
+    for(t=0;t<this->charnum;t++) {
+        if(!strcasecmp(this->charname[t],name)) {
+            if(!used[t])
+            {
+                swfcharid2char[swfcharpos] = t;
+                char2swfcharid[t] = swfcharpos++;
+                used[t] = 1;
+            }
+            return char2swfcharid[t];
+        }
+    }
+
+    /* if we didn't find it by name, use the names of the first 256 (or so) characters
+       of the font to try a new name based on charnr */
+    if(this->standardtable && charnr>=0 && charnr < this->standardtablesize) {
+       return getSWFCharID(this->standardtable[charnr], -1);
+    }
+    logf("<warning> Didn't find character '%s' in font '%s'", FIXNULL(name), this->name);
+    return 0;
+}
+
+int SWFFont::getWidth(char*name)
+{
+    int t;
+    for(t=0;t<this->charnum;t++) {
+        if(!strcmp(this->charname[t],name)) {
+            return this->width[t];
+        }
+    }
     return 0;
 }
 
@@ -718,7 +821,7 @@ void swfoutput_setfont(struct swfoutput*obj, char*fontid, int t1id, char*filenam
     }
 
     if(t1id<0) {
-        logf("<error> internal error: t1id:%d, fontid:%s\n", t1id,fontid);
+        logf("<error> internal error: t1id:%d, fontid:%s\n", t1id,FIXNULL(fontid));
     }
     
     SWFFont*font = new SWFFont(fontid, t1id, filename);
@@ -763,7 +866,7 @@ void swfoutput_setfontmatrix(struct swfoutput*obj,double m11,double m12,
 }
 
 /* draws a character at x,y. */
-void swfoutput_drawchar(struct swfoutput* obj,double x,double y,char*character) 
+void swfoutput_drawchar(struct swfoutput* obj,double x,double y,char*character, int charnr) 
 {
     swfmatrix m;
     m.m11 = obj->fontm11;
@@ -772,7 +875,7 @@ void swfoutput_drawchar(struct swfoutput* obj,double x,double y,char*character)
     m.m22 = obj->fontm22;
     m.m13 = x;
     m.m23 = y;
-    drawchar(obj, obj->font, character, &m);
+    drawchar(obj, obj->font, character, charnr, &m);
 }
 
 /* initialize the swf writer */
@@ -792,7 +895,7 @@ void swfoutput_init(struct swfoutput* obj, char*_filename, int _sizex, int _size
   
   memset(&swf,0x00,sizeof(SWF));
 
-  swf.fileVersion    = 4;
+  swf.fileVersion    = flashversion;
   swf.frameRate      = 0x0040; // 1 frame per 4 seconds
   swf.movieSize.xmax = 20*sizex;
   swf.movieSize.ymax = 20*sizey;
@@ -814,7 +917,7 @@ void swfoutput_setprotected() //write PROTECT tag
   flag_protected = 1;
 }
 
-void startshape(struct swfoutput*obj)
+static void startshape(struct swfoutput*obj)
 {
   RGBA rgb;
   SRECT r;
@@ -850,7 +953,7 @@ void startshape(struct swfoutput*obj)
   lastwasfill = 0;
 }
 
-void starttext(struct swfoutput*obj)
+static void starttext(struct swfoutput*obj)
 {
   SRECT r;
   MATRIX m;
@@ -878,7 +981,7 @@ void starttext(struct swfoutput*obj)
   swflastx=swflasty=0;
 }
 
-void endshape()
+static void endshape()
 {
     if(shapeid<0) 
         return;
@@ -888,7 +991,7 @@ void endshape()
     shapeid = -1;
 }
 
-void endtext()
+static void endtext()
 {
     if(textid<0)
         return;
@@ -899,7 +1002,7 @@ void endtext()
     textid = -1;
 }
 
-void endpage(struct swfoutput*obj)
+static void endpage(struct swfoutput*obj)
 {
     if(shapeid>=0)
       endshape();
@@ -907,6 +1010,14 @@ void endpage(struct swfoutput*obj)
       endtext();
     while(clippos)
         swfoutput_endclip(obj);
+
+    if(insertstoptag) {
+       ActionTAG*atag=0;
+       atag = action_Stop(atag);
+       atag = action_End(atag);
+       tag = swf_InsertTag(tag,ST_DOACTION);
+       swf_ActionSet(tag,atag);
+    }
     tag = swf_InsertTag(tag,ST_SHOWFRAME);
 }
 
@@ -941,19 +1052,25 @@ void swfoutput_destroy(struct swfoutput* obj)
     if(!filename) 
         return;
     if(filename)
-     fi = open(filename, O_CREAT|O_TRUNC|O_WRONLY, 0777);
+     fi = open(filename, O_BINARY|O_CREAT|O_TRUNC|O_WRONLY, 0777);
     else
      fi = 1; // stdout
     
     if(fi<=0) {
-     logf("<fatal> Could not create \"%s\". ", filename);
+     logf("<fatal> Could not create \"%s\". ", FIXNULL(filename));
      exit(1);
     }
  
     tag = swf_InsertTag(tag,ST_END);
 
-    if FAILED(swf_WriteSWF(fi,&swf)) 
-     logf("<error> WriteSWF() failed.\n");
+    if(enablezlib) {
+      if FAILED(swf_WriteSWC(fi,&swf)) 
+       logf("<error> WriteSWC() failed.\n");
+    } else {
+      if FAILED(swf_WriteSWF(fi,&swf)) 
+       logf("<error> WriteSWF() failed.\n");
+    }
+
     if(filename)
      close(fi);
     logf("<notice> SWF written\n");
@@ -980,9 +1097,9 @@ void swfoutput_setfillcolor(swfoutput* obj, u8 r, u8 g, u8 b, u8 a)
        obj->fillrgb.g == g &&
        obj->fillrgb.b == b &&
        obj->fillrgb.a == a) return;
-
     if(shapeid>=0)
      endshape();
+
     obj->fillrgb.r = r;
     obj->fillrgb.g = g;
     obj->fillrgb.b = b;
@@ -1058,26 +1175,33 @@ void swfoutput_endclip(swfoutput*obj)
     swf_ObjectPlaceClip(cliptags[clippos],clipshapes[clippos],clipdepths[clippos],NULL,NULL,NULL,depth++);
 }
 
-void drawlink(struct swfoutput*obj, ActionTAG*, swfcoord*points);
+static void drawlink(struct swfoutput*obj, ActionTAG*,ActionTAG*, swfcoord*points, char mouseover);
 
 void swfoutput_linktourl(struct swfoutput*obj, char*url, swfcoord*points)
 {
     ActionTAG* actions;
+    if(!strncmp("http://pdf2swf:", url, 15)) {
+     char*tmp = strdup(url);
+     int l = strlen(tmp);
+     if(tmp[l-1] == '/')
+        tmp[l-1] = 0;
+     swfoutput_namedlink(obj, tmp+15, points);
+     free(tmp);
+     return;
+    }
     
     if(shapeid>=0)
      endshape();
     if(textid>=0)
      endtext();
     
-    actions = swf_ActionStart();
     if(opennewwindow)
-      action_GetUrl(url, "_parent");
+      actions = action_GetUrl(0, url, "_parent");
     else
-      action_GetUrl(url, "_this");
-      action_End();
-    swf_ActionEnd();
+      actions = action_GetUrl(0, url, "_this");
+    actions = action_End(actions);
     
-    drawlink(obj, actions, points);
+    drawlink(obj, actions, 0, points,0);
 }
 void swfoutput_linktopage(struct swfoutput*obj, int page, swfcoord*points)
 {
@@ -1088,15 +1212,64 @@ void swfoutput_linktopage(struct swfoutput*obj, int page, swfcoord*points)
     if(textid>=0)
      endtext();
    
-    actions = swf_ActionStart();
-      action_GotoFrame(page);
-      action_End();
-    swf_ActionEnd();
+      actions = action_GotoFrame(0, page);
+      actions = action_End(actions);
+
+    drawlink(obj, actions, 0, points,0);
+}
+
+/* Named Links (a.k.a. Acrobatmenu) are used to implement various gadgets
+   of the viewer objects, like subtitles, index elements etc.
+*/
+void swfoutput_namedlink(struct swfoutput*obj, char*name, swfcoord*points)
+{
+    ActionTAG *actions1,*actions2;
+    char*tmp = strdup(name);
+    char mouseover = 1;
+
+    if(shapeid>=0)
+     endshape();
+    if(textid>=0)
+     endtext();
+
+    if(!strncmp(tmp, "call:", 5))
+    {
+       char*x = strchr(&tmp[5], ':');
+       if(!x) {
+           actions1 = action_PushInt(0, 0); //number of parameters (0)
+           actions1 = action_PushString(actions1, &tmp[5]); //function name
+           actions1 = action_CallFunction(actions1);
+       } else {
+           *x = 0;
+           actions1 = action_PushString(0, x+1); //parameter
+           actions1 = action_PushInt(actions1, 1); //number of parameters (1)
+           actions1 = action_PushString(actions1, &tmp[5]); //function name
+           actions1 = action_CallFunction(actions1);
+       }
+       actions2 = action_End(0);
+       mouseover = 0;
+    }
+    else
+    {
+       actions1 = action_PushString(0, "/:subtitle");
+       actions1 = action_PushString(actions1, name);
+       actions1 = action_SetVariable(actions1);
+       actions1 = action_End(actions1);
+
+       actions2 = action_PushString(0, "/:subtitle");
+       actions2 = action_PushString(actions2, "");
+       actions2 = action_SetVariable(actions2);
+       actions2 = action_End(actions2);
+    }
 
-    drawlink(obj, actions, points);
+    drawlink(obj, actions1, actions2, points,mouseover);
+
+    swf_ActionFree(actions1);
+    swf_ActionFree(actions2);
+    free(tmp);
 }
 
-void drawlink(struct swfoutput*obj, ActionTAG*actions, swfcoord*points)
+static void drawlink(struct swfoutput*obj, ActionTAG*actions1, ActionTAG*actions2, swfcoord*points, char mouseover)
 {
     RGBA rgb;
     SRECT r;
@@ -1107,6 +1280,8 @@ void drawlink(struct swfoutput*obj, ActionTAG*actions, swfcoord*points)
     int myshapeid2;
     double xmin,ymin;
     double xmax=xmin=points[0].x,ymax=ymin=points[0].y;
+    double posx = 0;
+    double posy = 0;
     int t;
     int buttonid = ++currentswfid;
     for(t=1;t<4;t++)
@@ -1116,8 +1291,18 @@ void drawlink(struct swfoutput*obj, ActionTAG*actions, swfcoord*points)
         if(points[t].x<xmin) xmin=points[t].x;
         if(points[t].y<ymin) ymin=points[t].y;
     }
+   
     p1.x=points[0].x; p1.y=points[0].y; p2.x=points[1].x; p2.y=points[1].y; 
     p3.x=points[2].x; p3.y=points[2].y; p4.x=points[3].x; p4.y=points[3].y;
+   
+    /* the following code subtracts the upper left edge from all coordinates,
+       and set's posx,posy so that ST_PLACEOBJECT is used with a matrix.
+       Necessary for preprocessing with swfcombine. */
+    posx = xmin; posy = ymin;
+    p1.x-=posx;p2.x-=posx;p3.x-=posx;p4.x-=posx;
+    p1.y-=posy;p2.y-=posy;p3.y-=posy;p4.y-=posy;
+    xmin -= posx; ymin -= posy;
+    xmax -= posx; ymax -= posy;
     
     /* shape */
     myshapeid = ++currentswfid;
@@ -1168,22 +1353,57 @@ void drawlink(struct swfoutput*obj, ActionTAG*actions, swfcoord*points)
     lineto(tag, p1);
     swf_ShapeSetEnd(tag);
 
-    tag = swf_InsertTag(tag,ST_DEFINEBUTTON);
-    swf_SetU16(tag,buttonid); //id
-    swf_ButtonSetFlags(tag, 0); //menu=no
-    swf_ButtonSetRecord(tag,0x01,myshapeid,depth,0,0);
-    swf_ButtonSetRecord(tag,0x02,myshapeid2,depth,0,0);
-    swf_ButtonSetRecord(tag,0x04,myshapeid2,depth,0,0);
-    swf_ButtonSetRecord(tag,0x08,myshapeid,depth,0,0);
-    swf_SetU8(tag,0);
-    swf_ActionSet(tag,actions);
-    swf_SetU8(tag,0);
+    if(!mouseover)
+    {
+       tag = swf_InsertTag(tag,ST_DEFINEBUTTON);
+       swf_SetU16(tag,buttonid); //id
+       swf_ButtonSetFlags(tag, 0); //menu=no
+       swf_ButtonSetRecord(tag,0x01,myshapeid,depth,0,0);
+       swf_ButtonSetRecord(tag,0x02,myshapeid2,depth,0,0);
+       swf_ButtonSetRecord(tag,0x04,myshapeid2,depth,0,0);
+       swf_ButtonSetRecord(tag,0x08,myshapeid,depth,0,0);
+       swf_SetU8(tag,0);
+       swf_ActionSet(tag,actions1);
+       swf_SetU8(tag,0);
+    }
+    else
+    {
+       tag = swf_InsertTag(tag,ST_DEFINEBUTTON2);
+       swf_SetU16(tag,buttonid); //id
+       swf_ButtonSetFlags(tag, 0); //menu=no
+       swf_ButtonSetRecord(tag,0x01,myshapeid,depth,0,0);
+       swf_ButtonSetRecord(tag,0x02,myshapeid2,depth,0,0);
+       swf_ButtonSetRecord(tag,0x04,myshapeid2,depth,0,0);
+       swf_ButtonSetRecord(tag,0x08,myshapeid,depth,0,0);
+       swf_SetU8(tag,0); // end of button records
+        swf_ButtonSetCondition(tag, BC_IDLE_OVERUP);
+       swf_ActionSet(tag,actions1);
+       if(actions2) {
+           swf_ButtonSetCondition(tag, BC_OVERUP_IDLE);
+           swf_ActionSet(tag,actions2);
+           swf_SetU8(tag,0);
+           swf_ButtonPostProcess(tag, 2);
+       } else {
+           swf_SetU8(tag,0);
+            swf_ButtonPostProcess(tag, 1);
+       }
+    }
     
     tag = swf_InsertTag(tag,ST_PLACEOBJECT2);
-    swf_ObjectPlace(tag, buttonid, depth++,0,0,0);
+
+    if(posx!=0 || posy!=0) {
+       MATRIX m;
+       swf_GetMatrix(0,&m);
+       m.tx = (int)(posx*20);
+       m.ty = (int)(posy*20);
+       swf_ObjectPlace(tag, buttonid, depth++,&m,0,0);
+    }
+    else {
+       swf_ObjectPlace(tag, buttonid, depth++,0,0,0);
+    }
 }
 
-void drawimage(struct swfoutput*obj, int bitid, int sizex,int sizey, 
+static void drawimage(struct swfoutput*obj, int bitid, int sizex,int sizey, 
         double x1,double y1,
         double x2,double y2,
         double x3,double y3,
@@ -1208,23 +1428,28 @@ void drawimage(struct swfoutput*obj, int bitid, int sizex,int sizey,
     if(y4>ymax) ymax=y4;
     if(x4<xmin) xmin=x4;
     if(y4<ymin) ymin=y4;
-    p1.x=x1;
-    p1.y=y1;
-    p2.x=x2;
-    p2.y=y2;
-    p3.x=x3;
-    p3.y=y3;
-    p4.x=x4;
-    p4.y=y4;
+    p1.x=x1; p1.y=y1;
+    p2.x=x2; p2.y=y2;
+    p3.x=x3; p3.y=y3;
+    p4.x=x4; p4.y=y4;
+
+    {p1.x = (int)(p1.x*20)/20.0;
+     p1.y = (int)(p1.y*20)/20.0;
+     p2.x = (int)(p2.x*20)/20.0;
+     p2.y = (int)(p2.y*20)/20.0;
+     p3.x = (int)(p3.x*20)/20.0;
+     p3.y = (int)(p3.y*20)/20.0;
+     p4.x = (int)(p4.x*20)/20.0;
+     p4.y = (int)(p4.y*20)/20.0;}
     
     MATRIX m;
-    m.sx = (int)(65536*20*(x4-x1))/sizex;
-    m.r1 = -(int)(65536*20*(y4-y1))/sizex;
-    m.r0 = (int)(65536*20*(x1-x2))/sizey;
-    m.sy = -(int)(65536*20*(y1-y2))/sizey;
+    m.sx = (int)(65536*20*(p4.x-p1.x)/sizex);
+    m.r1 = -(int)(65536*20*(p4.y-p1.y)/sizex);
+    m.r0 = (int)(65536*20*(p1.x-p2.x)/sizey);
+    m.sy = -(int)(65536*20*(p1.y-p2.y)/sizey);
 
-    m.tx = (int)(x1*20);
-    m.ty = (int)(y1*20);
+    m.tx = (int)(p1.x*20);
+    m.ty = (int)(p1.y*20);
   
     /* shape */
     myshapeid = ++currentswfid;
@@ -1232,7 +1457,7 @@ void drawimage(struct swfoutput*obj, int bitid, int sizex,int sizey,
     swf_ShapeNew(&shape);
     //lsid = ShapeAddLineStyle(shape,obj->linewidth,&obj->strokergb);
     //fsid = ShapeAddSolidFillStyle(shape,&obj->fillrgb);
-    fsid = swf_ShapeAddBitmapFillStyle(shape,&m,bitid,0);
+    fsid = swf_ShapeAddBitmapFillStyle(shape,&m,bitid,1);
     swf_SetU16(tag, myshapeid);
     r.xmin = (int)(xmin*20);
     r.ymin = (int)(ymin*20);
@@ -1262,22 +1487,51 @@ void drawimage(struct swfoutput*obj, int bitid, int sizex,int sizey,
     swf_ObjectPlace(tag,myshapeid,/*depth*/depth++,NULL,NULL,NULL);
 }
 
-int swfoutput_drawimagejpeg(struct swfoutput*obj, char*filename, int sizex,int sizey, 
+int swfoutput_drawimagejpeg_old(struct swfoutput*obj, char*filename, int sizex,int sizey, 
         double x1,double y1,
         double x2,double y2,
         double x3,double y3,
         double x4,double y4)
 {
+    TAG*oldtag;
     if(shapeid>=0)
      endshape();
     if(textid>=0)
      endtext();
 
     int bitid = ++currentswfid;
+    oldtag = tag;
     tag = swf_InsertTag(tag,ST_DEFINEBITSJPEG2);
     swf_SetU16(tag, bitid);
-    swf_SetJPEGBits(tag, filename, jpegquality);
+    if(swf_SetJPEGBits(tag, filename, jpegquality)<0) {
+       swf_DeleteTag(tag);
+       tag = oldtag;
+       return -1;
+    }
+
+    drawimage(obj, bitid, sizex, sizey, x1,y1,x2,y2,x3,y3,x4,y4);
+    return bitid;
+}
+
+int swfoutput_drawimagejpeg(struct swfoutput*obj, RGBA*mem, int sizex,int sizey, 
+        double x1,double y1,
+        double x2,double y2,
+        double x3,double y3,
+        double x4,double y4)
+{
+    TAG*oldtag;
+    JPEGBITS*jpeg;
 
+    if(shapeid>=0)
+     endshape();
+    if(textid>=0)
+     endtext();
+
+    int bitid = ++currentswfid;
+    oldtag = tag;
+    tag = swf_InsertTag(tag,ST_DEFINEBITSJPEG2);
+    swf_SetU16(tag, bitid);
+    swf_SetJPEGBits2(tag,sizex,sizey,mem,jpegquality);
     drawimage(obj, bitid, sizex, sizey, x1,y1,x2,y2,x3,y3,x4,y4);
     return bitid;
 }
@@ -1288,35 +1542,66 @@ int swfoutput_drawimagelossless(struct swfoutput*obj, RGBA*mem, int sizex,int si
         double x3,double y3,
         double x4,double y4)
 {
+    TAG*oldtag;
     if(shapeid>=0)
      endshape();
     if(textid>=0)
      endtext();
 
     int bitid = ++currentswfid;
+    oldtag = tag;
     tag = swf_InsertTag(tag,ST_DEFINEBITSLOSSLESS);
     swf_SetU16(tag, bitid);
-    swf_SetLosslessBits(tag,sizex,sizey,mem, BMF_32BIT);
+    if(swf_SetLosslessBits(tag,sizex,sizey,mem, BMF_32BIT)<0) {
+       swf_DeleteTag(tag);
+       tag = oldtag;
+       return -1;
+    }
     
     drawimage(obj, bitid, sizex, sizey, x1,y1,x2,y2,x3,y3,x4,y4);
     return bitid;
 }
 
-int swfoutput_drawimagelossless256(struct swfoutput*obj, U8*mem, RGBA*pal, int sizex,int sizey, 
+int swfoutput_drawimagelosslessN(struct swfoutput*obj, U8*mem, RGBA*pal, int sizex,int sizey, 
         double x1,double y1,
         double x2,double y2,
         double x3,double y3,
-        double x4,double y4)
+        double x4,double y4, int n)
 {
+    TAG*oldtag;
+    U8*mem2 = 0;
     if(shapeid>=0)
      endshape();
     if(textid>=0)
      endtext();
 
+    if(sizex&3)
+    { 
+       /* SWF expects scanlines to be 4 byte aligned */
+       int x,y;
+       U8*ptr;
+       mem2 = (U8*)malloc(BYTES_PER_SCANLINE(sizex)*sizey);
+       ptr = mem2;
+       for(y=0;y<sizey;y++)
+       {
+           for(x=0;x<sizex;x++)
+               *ptr++ = mem[y*sizex+x];
+           ptr+= BYTES_PER_SCANLINE(sizex)-sizex;
+       }
+       mem = mem2;
+    }
+
     int bitid = ++currentswfid;
+    oldtag = tag;
     tag = swf_InsertTag(tag,ST_DEFINEBITSLOSSLESS2);
     swf_SetU16(tag, bitid);
-    swf_SetLosslessBitsIndexed(tag,sizex,sizey,mem, pal, 256);
+    if(swf_SetLosslessBitsIndexed(tag,sizex,sizey,mem, pal, n)<0) {
+       swf_DeleteTag(tag);
+       tag = oldtag;
+       return -1;
+    }
+    if(mem2)
+       free(mem2);
   
     drawimage(obj, bitid, sizex, sizey, x1,y1,x2,y2,x3,y3,x4,y4);
     return bitid;
@@ -1328,6 +1613,7 @@ void swfoutput_drawimageagain(struct swfoutput*obj, int id, int sizex,int sizey,
         double x3,double y3,
         double x4,double y4)
 {
+    if(id<0) return;
     if(shapeid>=0)
      endshape();
     if(textid>=0)