* implemented definefont2 parsing
[swftools.git] / lib / modules / swftext.c
index 78b3bce..74504e4 100644 (file)
 #define FF_SHIFTJIS     0x10
 #define FF_UNICODE      0x20
 
+#define FF2_BOLD         0x01
+#define FF2_ITALIC       0x02
+#define FF2_WIDECODES    0x04
+#define FF2_WIDEOFFSETS  0x08
+#define FF2_ANSI         0x10
+#define FF2_UNICODE      0x20
+#define FF2_SHIFTJIS     0x40
+#define FF2_LAYOUT      0x80
+
+int swf_FontIsItalic(SWFFONT * f) { return f->version==2?f->flags&FF2_ITALIC:f->flags&FF_ITALIC; }
+int swf_FontIsBold(SWFFONT * f)   { return f->version==2?f->flags&FF2_BOLD:f->flags&FF_BOLD; }
+
+static const int WRITEFONTID = 0x4e46; // font id for WriteFont and ReadFont
+
 int swf_FontEnumerate(SWF * swf,void (*FontCallback) (U16,U8*))
 { int n;
   TAG * t;
@@ -56,36 +70,45 @@ int swf_FontEnumerate(SWF * swf,void (*FontCallback) (U16,U8*))
   return n;
 }
 
-int swf_FontExtract_DefineFont(int id,SWFFONT * f,TAG * t,SHAPE * * shapes)
+int swf_FontExtract_DefineFont(int id,SWFFONT * f,TAG * t, SHAPE***shapes)
 { U16 fid;
   swf_SaveTagPos(t);
   swf_SetTagPos(t,0);
 
   fid = swf_GetU16(t);
   if ((!id)||(id==fid))
-  { U16 ofs[MAX_CHAR_PER_FONT];
+  { U16 of,*ofs;
     int n,i;
       
     id = fid;
+    f->version = 1;
     f->id = fid;
 
-    ofs[0] = swf_GetU16(t);
-    n = ofs[0]/2;
+    of = swf_GetU16(t);
+    n = of/2;
+    f->numchars = n;
+    (*shapes) = (SHAPE**)malloc(sizeof(SHAPE*)*n);
+    memset((*shapes), 0, sizeof(SHAPE*)*n);
 
-    for (i=1;i<n;i++) if (i<MAX_CHAR_PER_FONT) ofs[i] = swf_GetU16(t); else swf_GetU16(t);
-    for (i=0;i<n;i++) if (i<MAX_CHAR_PER_FONT) swf_GetSimpleShape(t,&shapes[i]);
-    
+    for (i=1;i<n;i++) swf_GetU16(t);
+    for (i=0;i<n;i++) swf_GetSimpleShape(t,&((*shapes)[i]));
   }
 
   swf_RestoreTagPos(t);
   return id;
 }
 
-int swf_FontExtract_DefineFontInfo(int id,SWFFONT * f,TAG * t,SHAPE * * shapes)
+int swf_FontExtract_DefineFontInfo(int id,SWFFONT * f,TAG * t,SHAPE**shapes)
 { U16 fid;
   swf_SaveTagPos(t);
   swf_SetTagPos(t,0);
 
+  if(f->version>1) {
+      // DefineFont2 doesn't have FontInfo fields
+      fprintf(stderr, "fixme: FontInfo field for DefineFont2 encountered\n");
+      return -1;
+  }
+
   fid = swf_GetU16(t);
   if (fid==id)
   { U8 l = swf_GetU8(t);
@@ -105,15 +128,18 @@ int swf_FontExtract_DefineFontInfo(int id,SWFFONT * f,TAG * t,SHAPE * * shapes)
     }
     f->flags = swf_GetU8(t);
 
-    i = 0;
-    while (shapes[i])
-    { U16 code = ((f->flags&FF_WIDECODES)?swf_GetU16(t):swf_GetU8(t))%MAX_CHAR_PER_FONT;
-        
-      f->glyph[code].shape = shapes[i];
-      f->glyph[code].gid   = i;
-      if (i<MAX_CHAR_PER_FONT) f->codes[i] = code;
-
-      i++;
+    f->glyph = (SWFGLYPH*)malloc(sizeof(SWFGLYPH)*f->numchars);
+    memset(f->glyph, 0, sizeof(SWFGLYPH)*f->numchars);
+    f->codes = (U16*)malloc(sizeof(U16)*f->numchars);
+    memset(f->codes, 0, sizeof(U16)*f->numchars);
+    for(i=0; i < f->numchars; i++)
+    { U16 code = ((f->flags&FF_WIDECODES)?swf_GetU16(t):swf_GetU8(t));
+       
+      if(code < f->numchars) {
+         f->glyph[code].shape = shapes[i];
+         f->glyph[code].gid   = i;
+      }
+      f->codes[i] = code;
     }
   }
 
@@ -121,6 +147,90 @@ int swf_FontExtract_DefineFontInfo(int id,SWFFONT * f,TAG * t,SHAPE * * shapes)
   return id;
 }
 
+int swf_FontExtract_DefineFont2(int id,SWFFONT * font,TAG * tag,SHAPE * * * shapes)
+{
+    int t, glyphcount;
+    U8 flags1,flags2,namelen;
+    font->version=2;
+    font->id = swf_GetU16(tag);
+    flags1 = swf_GetU8(tag);
+    flags2 = swf_GetU8(tag); //reserved flags
+    namelen = swf_GetU8(tag);
+    font->flags = flags1;
+    font->name = (U8*)malloc(namelen+1);
+    font->name[namelen]=0;
+    swf_GetBlock(tag, font->name, namelen);
+    font->version = 2;
+    glyphcount = swf_GetU16(tag);
+    font->numchars = glyphcount;
+    (*shapes) = (SHAPE**)malloc(sizeof(SHAPE*)*glyphcount);
+    memset((*shapes), 0, sizeof(SHAPE*)*glyphcount);
+    if(flags1&8) { // wide offsets
+       for(t=0;t<glyphcount;t++)
+           swf_GetU32(tag); //offset[t]
+       swf_GetU32(tag); // fontcodeoffset
+    } else {
+       for(t=0;t<glyphcount;t++)
+           swf_GetU16(tag); //offset[t]
+       swf_GetU16(tag); // fontcodeoffset
+    }
+    for(t=0;t<glyphcount;t++)
+       swf_GetSimpleShape(tag,&((*shapes)[t]));
+
+    font->glyph = (SWFGLYPH*)malloc(sizeof(SWFGLYPH)*glyphcount);
+    memset(font->glyph, 0, sizeof(SWFGLYPH)*glyphcount);
+    font->codes = (U16*)malloc(sizeof(U16)*glyphcount);
+    memset(font->codes, 0, sizeof(U16)*glyphcount);
+    for(t=0;t<glyphcount;t++) {
+       U16 code;
+       if(flags1&4) // wide codes
+           code = swf_GetU16(tag);
+       else
+           code = swf_GetU8(tag);
+
+       font->glyph[code].shape = (*shapes)[t];
+       font->glyph[code].gid   = t;
+       if (t<MAX_CHAR_PER_FONT) 
+           font->codes[t] = code;
+    }
+    if(flags1&128) { // has layout
+       U16 kerningcount;
+       font->layout = (SWFLAYOUT*)malloc(sizeof(SWFLAYOUT));
+       font->layout->ascent=swf_GetU16(tag);
+       font->layout->descent=swf_GetU16(tag);
+       font->layout->leading=swf_GetU16(tag);
+       for(t=0;t<glyphcount;t++) {
+           S16 advance = swf_GetS16(tag);
+           font->glyph[t].advance = advance;
+       }
+       font->layout->bounds = malloc(glyphcount*sizeof(SRECT));
+       for(t=0;t<glyphcount;t++) {
+           swf_ResetReadBits(tag);
+           swf_GetRect(tag, font->layout->bounds);
+       }
+       kerningcount = swf_GetU16(tag);
+       font->layout->kerningcount = kerningcount;
+       font->layout->kerning = (SWFKERNING*)malloc(sizeof(SWFKERNING)*kerningcount);
+       if(kerningcount) {
+           font->layout->kerning = 
+               malloc(sizeof(*font->layout->kerning)* kerningcount);
+           for(t=0;t<kerningcount;t++)
+           {
+               if(flags1&4) { // wide codes
+                   font->layout->kerning[t].char1 = swf_GetU16(tag);
+                   font->layout->kerning[t].char2 = swf_GetU16(tag);
+               } else {
+                   font->layout->kerning[t].char1 = swf_GetU16(tag);
+                   font->layout->kerning[t].char2 = swf_GetU16(tag);
+               }
+               font->layout->kerning[t].adjustment = swf_GetS16(tag);
+           }
+       }
+    }
+    return font->id;
+}
+
+
 #define FEDTJ_PRINT  0x01
 #define FEDTJ_MODIFY 0x02
 
@@ -168,7 +278,7 @@ int swf_FontExtract_DefineText(int id,SWFFONT * f,TAG * t,int jobs)
         { int code = f->codes[glyph];
           if (jobs&FEDTJ_PRINT) printf("%c",code);
           if (jobs&FEDTJ_MODIFY)
-            /*if (f->glyph[code].advance)*/ f->glyph[code].advance = adv;
+            /*if (!f->glyph[code].advance)*/ f->glyph[code].advance = adv;
         }
       }
       if ((id==fid)&&(jobs&FEDTJ_PRINT)) printf("\n");
@@ -183,14 +293,13 @@ int swf_FontExtract_DefineText(int id,SWFFONT * f,TAG * t,int jobs)
 int swf_FontExtract(SWF * swf,int id,SWFFONT * * font)
 { TAG * t;
   SWFFONT * f;
-  SHAPE * shapes[MAX_CHAR_PER_FONT];
+  SHAPE ** shapes;
     
   if ((!swf)||(!font)) return -1;
 
   f = (SWFFONT *)malloc(sizeof(SWFFONT)); font[0] = f;
   if (!f) return -1;
   
-  memset(shapes,0x00,sizeof(shapes));
   memset(f,0x00,sizeof(SWFFONT));
 
   t = swf->firstTag;
@@ -199,7 +308,11 @@ int swf_FontExtract(SWF * swf,int id,SWFFONT * * font)
   { int nid = 0;
     switch (swf_GetTagID(t))
     { case ST_DEFINEFONT:
-        nid = swf_FontExtract_DefineFont(id,f,t,shapes);
+        nid = swf_FontExtract_DefineFont(id,f,t,&shapes);
+        break;
+    
+      case ST_DEFINEFONT2:
+        nid = swf_FontExtract_DefineFont2(id,f,t,&shapes);
         break;
         
       case ST_DEFINEFONTINFO:
@@ -208,7 +321,7 @@ int swf_FontExtract(SWF * swf,int id,SWFFONT * * font)
         
       case ST_DEFINETEXT:
       case ST_DEFINETEXT2:
-        nid = swf_FontExtract_DefineText(id,f,t,FEDTJ_MODIFY);
+        nid = swf_FontExtract_DefineText(id,f,t,f->layout?0:FEDTJ_MODIFY);
         break;
     }
     if (nid>0) id = nid;
@@ -217,21 +330,18 @@ int swf_FontExtract(SWF * swf,int id,SWFFONT * * font)
   return 0;
 }
 
-int swf_FontIsItalic(SWFFONT * f) { return f->flags&FF_ITALIC; }
-int swf_FontIsBold(SWFFONT * f)   { return f->flags&FF_BOLD; }
-
 int swf_FontSetID(SWFFONT * f,U16 id) { if (!f) return -1; f->id = id; return 0; }
 
 int swf_FontReduce(SWFFONT * f,FONTUSAGE * use)
-{ int i,j;
+{ int i,j,num;
   if ((!f)||(!use)) return -1;
 
-  memset(&f->codes,0x00,sizeof(f->codes));
+  memset(f->codes,0,sizeof(f->codes[0])*f->numchars);
 
   j = 0;
-  for (i=0;i<MAX_CHAR_PER_FONT;i++)
+  for (i=0;i<f->numchars;i++)
     if (f->glyph[i].shape)
-    { if (use->code[i])
+    { if (i<MAX_CHAR_PER_FONT && use->code[i])
       { f->glyph[i].gid = j;
         f->codes[j] = i;
         j++;
@@ -263,15 +373,15 @@ int swf_FontUse(FONTUSAGE * use,U8 * s)
 }
 
 int swf_FontSetDefine(TAG * t,SWFFONT * f)
-{ U16 ofs[MAX_CHAR_PER_FONT];
+{ U16*ofs = (U16*)malloc(f->numchars*2);
   int p,i,j;
     
   if ((!t)||(!f)) return -1;
-  swf_ResetBitcount(t);
+  swf_ResetWriteBits(t);
   swf_SetU16(t,f->id);
 
   p = 0; j = 0;
-  for (i=0;i<MAX_CHAR_PER_FONT;i++)
+  for (i=0;i<f->numchars;i++)
     if (f->glyph[i].shape)
     { ofs[j++] = p;
       p+=swf_SetSimpleShape(NULL,f->glyph[i].shape);
@@ -279,27 +389,32 @@ int swf_FontSetDefine(TAG * t,SWFFONT * f)
 
   for (i=0;i<j;i++) swf_SetU16(t,ofs[i]+j*2);
   
-  for (i=0;i<MAX_CHAR_PER_FONT;i++)
+  for (i=0;i<f->numchars;i++)
     if (f->glyph[i].shape)
       swf_SetSimpleShape(t,f->glyph[i].shape);
   
-  swf_ResetBitcount(t);
+  swf_ResetWriteBits(t);
+  free(ofs);
   return 0;
 }
 
 int swf_FontSetInfo(TAG * t,SWFFONT * f)
 { int l,i;
+  U8 wide=0;
   if ((!t)||(!f)) return -1;
-  swf_ResetBitcount(t);
+  swf_ResetWriteBits(t);
   swf_SetU16(t,f->id);
   l = strlen(f->name); if (l>255) l = 255;
   swf_SetU8(t,l);
   swf_SetBlock(t,f->name,l);
-  swf_SetU8(t,f->flags&0xfe); // no Wide-Codes
+  if(f->numchars>=256)
+      wide=1;
+  swf_SetU8(t,(f->flags&0xfe)|wide);
 
-  for (i=0;i<MAX_CHAR_PER_FONT;i++)
+  for (i=0;i<f->numchars;i++) {
     if (f->glyph[i].shape)
-      swf_SetU8(t,i);
+      wide?swf_SetU16(t,i):swf_SetU16(t,i);
+  }
   
   return 0;
 }
@@ -326,14 +441,15 @@ int swf_FontExport(int handle,SWFFONT * f)
   { l+=sizeof(SWFLAYOUT);
     if (handle>=0)
       if (write(handle,f->layout,sizeof(SWFLAYOUT))!=sizeof(SWFLAYOUT)) return -1;
-    if (f->layout->kerning.data)
+/*    new kerning struct. hope commenting this out doesn't break things
+      if (f->layout->kerning.data)
     { l+=f->layout->kerning.count*4;
       if (handle>=0)
         if (write(handle,f->layout->kerning.data,f->layout->kerning.count*4)!=f->layout->kerning.count*4) return -1;
-    }
+    }*/
   }
 
-  for (i=0;i<MAX_CHAR_PER_FONT;i++)
+  for (i=0;i<f->numchars;i++)
   { if (f->glyph[i].shape)
     { int ll = swf_ShapeExport(handle,f->glyph[i].shape);
       if (ll<0) return -1;
@@ -344,7 +460,7 @@ int swf_FontExport(int handle,SWFFONT * f)
   return l;
 }
 
-int FontImport(int handle,SWFFONT * * font)
+int swf_FontImport(int handle,SWFFONT * * font)
 { SWFFONT * f;
   int layout;
   int i = 0;
@@ -375,15 +491,16 @@ int FontImport(int handle,SWFFONT * * font)
   { f->layout = (SWFLAYOUT *)malloc(sizeof(SWFLAYOUT));
     if (!f->layout) goto fehler;
     if (read(handle,f->layout,sizeof(SWFLAYOUT))!=sizeof(SWFLAYOUT)) goto fehler;
+    /* new kerning struct. hope commenting this out doesn't break things
     if (f->layout->kerning.data)
     { int l = f->layout->kerning.count*4;
       f->layout->kerning.data = (U8*)malloc(l);
       if (!f->layout->kerning.data) goto fehler;
       if (read(handle,f->layout->kerning.data,l)!=l) goto fehler;
-    }
+    } */
   }
 
-  for (i=0;i<MAX_CHAR_PER_FONT;i++)
+  for (i=0;i<f->numchars;i++)
   { if (f->glyph[i].shape)
     { if (swf_ShapeImport(handle,&f->glyph[i].shape)<0) goto fehler;
     }
@@ -409,8 +526,10 @@ int swf_TextPrintDefineText(TAG * t,SWFFONT * f)
 
 void swf_LayoutFree(SWFLAYOUT * l)
 { if (l)
-  { if (l->kerning.data) free(l->kerning.data);
-    l->kerning.data = NULL;
+  { if (l->kerning) free(l->kerning);
+    l->kerning = NULL;
+    if (l->bounds) free(l->bounds);
+    l->bounds = NULL;
   }
   free(l);
 }
@@ -425,11 +544,19 @@ void swf_FontFree(SWFFONT * f)
     f->name = NULL;
     f->layout = NULL;
 
-    for (i=0;i<MAX_CHAR_PER_FONT;i++)
-      if (f->glyph[i].shape)
-      { swf_ShapeFree(f->glyph[i].shape);
-        f->glyph[i].shape = NULL;
-      }
+    if(f->glyph) {
+      for (i=0;i<f->numchars;i++)
+       if (f->glyph[i].shape)
+       { swf_ShapeFree(f->glyph[i].shape);
+         f->glyph[i].shape = NULL;
+       }
+      free(f->glyph);
+      f->glyph = NULL;
+    }
+    if(f->codes) {
+      free(f->codes);
+      f->codes = NULL;
+    }
   }
   free(f);
 }
@@ -484,7 +611,7 @@ int swf_TextSetCharRecord(TAG * t,SWFFONT * font,U8 * s,int scale,U8 gbits,U8 ab
     swf_SetBits(t,(((U32)font->glyph[s[i]].advance)*scale)/100,abits);
   }
 
-  swf_ResetBitcount(t);
+  swf_ResetWriteBits(t);
   return 0;
 }
 
@@ -500,3 +627,159 @@ U32 swf_TextGetWidth(SWFFONT * font,U8 * s,int scale)
   }
   return res;
 }
+
+void swf_WriteFont(SWFFONT*font, char* filename, int useDefineFont2)
+{ SWF swf;
+  TAG * t;
+  SRECT r;
+  RGBA rgb;
+  int f;
+
+  if(useDefineFont2) {
+      fprintf(stderr, "DefineFont2 is not yet supported!\n");
+      useDefineFont2 = 0;
+  }
+
+  font->id = WRITEFONTID; //"FN"
+
+  memset(&swf,0x00,sizeof(SWF));
+
+  swf.fileVersion    = 4;
+  swf.frameRate      = 0x4000;
+  swf.movieSize.xmax = 20*640;
+  swf.movieSize.ymax = 20*480;
+
+  if(!useDefineFont2)
+  /* if we use DefineFont1 to store the characters,
+     we have to build a textfield to store the
+     advance values. While at it, we can also
+     make the whole .swf viewable */
+  {
+      t = swf_InsertTag(NULL,ST_SETBACKGROUNDCOLOR);
+      swf.firstTag = t;
+           rgb.r = 0xff;
+           rgb.g = 0xff;
+           rgb.b = 0xff;
+           swf_SetRGB(t,&rgb);
+      t = swf_InsertTag(t,ST_DEFINEFONT);
+  }
+  else
+  {
+      t = swf_InsertTag(NULL,ST_DEFINEFONT);
+      swf.firstTag = t;
+  }
+
+        swf_FontSetDefine(t,font);
+  
+  t = swf_InsertTag(t,ST_DEFINEFONTINFO);
+        swf_FontSetInfo(t,font);
+
+  if(!useDefineFont2)
+  {
+       int textscale = 400;
+       int s;
+       int xmax = 0;
+       int ymax = textscale * 20;
+       U8 gbits,abits;
+       char text[257];
+       int x,y;
+       text[256]=0;
+       for(s=0;s<256;s++)
+       {
+           text[s] = s;
+           if(font->glyph[s].advance*textscale/100 > xmax)
+               xmax = font->glyph[s].advance*textscale/100;
+       }
+       swf.movieSize.xmax = xmax*20;
+       swf.movieSize.ymax = ymax;
+
+       t = swf_InsertTag(t,ST_DEFINETEXT);
+
+           swf_SetU16(t,font->id+1);            // ID
+
+           r.xmin = 0;
+           r.ymin = 0;
+           r.xmax = swf.movieSize.xmax*20;
+           r.ymax = swf.movieSize.ymax;
+           
+           swf_SetRect(t,&r);
+
+           swf_SetMatrix(t,NULL);
+
+           abits = swf_CountBits(xmax*16, 0);
+           gbits = 8;
+           
+           swf_SetU8(t,gbits);
+           swf_SetU8(t,abits);
+
+           rgb.r = 0x00;
+           rgb.g = 0x00;
+           rgb.b = 0x00;
+           for(y=0;y<16;y++)
+           {
+               int c=0,lastx=-1, firstx=0;
+               for(x=0;x<16;x++) {
+                   if(font->glyph[y*16+x].shape) {
+                       c++;
+                       if(lastx<0) 
+                           lastx = x*xmax;
+                   }
+               }
+               if(c) {
+                 swf_TextSetInfoRecord(t,font,textscale,&rgb,lastx+1,textscale*y);
+                 for(x=0;x<16;x++)
+                 {
+                     if(font->glyph[y*16+x].shape) {
+                       if(lastx != x*xmax) {
+                           swf_TextSetInfoRecord(t,0,0,0,x*xmax+1,0);
+                       }
+                       swf_SetU8(t,1);
+                       swf_SetBits(t, font->glyph[y*16+x].gid, gbits);
+                       swf_SetBits(t, font->glyph[y*16+x].advance, abits);
+                       lastx = x*xmax+font->glyph[y*16+x].advance;
+                       swf_ResetWriteBits(t);
+                     }
+                 }
+               } 
+           }
+           swf_SetU8(t,0);
+        
+       t = swf_InsertTag(t,ST_PLACEOBJECT2);
+
+           swf_ObjectPlace(t,font->id+1,1,NULL,NULL,NULL);
+     
+       t = swf_InsertTag(t,ST_SHOWFRAME);
+  }
+  
+  t = swf_InsertTag(t,ST_END);
+
+  f = open(filename, O_RDWR|O_CREAT|O_TRUNC,0644);
+  if FAILED(swf_WriteSWF(f,&swf)) fprintf(stderr,"WriteSWF() failed in writeFont().\n");
+  close(f);
+
+  swf_FreeTags(&swf);
+}
+
+SWFFONT* swf_ReadFont(char* filename)
+{
+  int f;
+  SWF swf;
+  if(!filename)
+      return 0;
+  f = open(filename,O_RDONLY);
+  
+  if (f<0 || swf_ReadSWF(f,&swf)<0)
+  { fprintf(stderr,"%s is not a valid SWF font file or contains errors.\n",filename);
+    close(f);
+    return 0;
+  }
+  else
+  { SWFFONT*font;
+    close(f);
+    if(swf_FontExtract(&swf, WRITEFONTID, &font) < 0)
+       return 0;
+    swf_FreeTags(&swf);
+    return font;
+  }
+}
+