* moved non-gfxdevice specific font handling to SWFOutputDev.cc
authorkramm <kramm>
Sun, 8 May 2005 13:48:03 +0000 (13:48 +0000)
committerkramm <kramm>
Sun, 8 May 2005 13:48:03 +0000 (13:48 +0000)
* added font reduction

pdf2swf/SWFOutputDev.cc
pdf2swf/swfoutput.cc

index fd753d5..fe6f296 100644 (file)
@@ -29,7 +29,7 @@
 #ifdef HAVE_SYS_STAT_H
 #include <sys/stat.h>
 #endif
-#ifdef HAVE_FONTCONFIG_H
+#ifdef HAVE_FONTCONFIG
 #include <fontconfig.h>
 #endif
 //xpdf header files
@@ -65,7 +65,8 @@
 #include "swfoutput.h"
 #include "../lib/log.h"
 #include "../lib/gfxdevice.h"
-#include "gfxtools.h"
+#include "../lib/gfxtools.h"
+#include "../lib/gfxfont.h"
 
 #include <math.h>
 
@@ -124,6 +125,14 @@ class SWFOutputState {
     }
 };
 
+typedef struct _fontlist
+{
+    char*id;
+    char*filename;
+    gfxfont_t*font;
+    _fontlist*next;
+} fontlist_t;
+
 class SWFOutputDev:  public OutputDev {
   int outputstarted;
   struct swfoutput output;
@@ -227,6 +236,11 @@ public:
   void drawGeneralImage(GfxState *state, Object *ref, Stream *str,
                                   int width, int height, GfxImageColorMap*colorMap, GBool invert,
                                   GBool inlineImg, int mask, int *maskColors);
+  int SWFOutputDev::setGfxFont(char*id, char*filename);
+  void strokeGfxline(GfxState *state, gfxline_t*line);
+  void clipToGfxLine(GfxState *state, gfxline_t*line);
+  void fillGfxLine(GfxState *state, gfxline_t*line);
+
   SWFOutputState states[64];
   int statepos;
 
@@ -257,6 +271,13 @@ public:
 
   int user_movex,user_movey;
   int user_clipx1,user_clipx2,user_clipy1,user_clipy2;
+
+  gfxline_t* current_text_stroke;
+  char* current_font_id;
+  gfxfont_t* current_gfxfont;
+  gfxmatrix_t current_font_matrix;
+
+  fontlist_t* fontlist;
 };
 
 static char*getFontID(GfxFont*font);
@@ -339,6 +360,8 @@ SWFOutputDev::SWFOutputDev()
     user_clipy1 = 0;
     user_clipx2 = 0;
     user_clipy2 = 0;
+    current_text_stroke = 0;
+    fontlist = 0;
     memset(&output, 0, sizeof(output));
 //    printf("SWFOutputDev::SWFOutputDev() \n");
 };
@@ -675,6 +698,13 @@ gfxline_t* gfxPath_to_gfxline(GfxState*state, GfxPath*path, int closed)
 void SWFOutputDev::stroke(GfxState *state) 
 {
     GfxPath * path = state->getPath();
+    gfxline_t*line= gfxPath_to_gfxline(state, path, 0);
+    strokeGfxline(state, line);
+    gfxline_free(line);
+}
+
+void SWFOutputDev::strokeGfxline(GfxState *state, gfxline_t*line)
+{
     int lineCap = state->getLineCap(); // 0=butt, 1=round 2=square
     int lineJoin = state->getLineJoin(); // 0=miter, 1=round 2=bevel
     double miterLimit = state->getMiterLimit();
@@ -702,8 +732,6 @@ void SWFOutputDev::stroke(GfxState *state)
     else if(lineJoin == 1) joinType = gfx_joinRound;
     else if(lineJoin == 2) joinType = gfx_joinBevel;
 
-    gfxline_t*line= gfxPath_to_gfxline(state, path, 0);
-    
     int dashnum = 0;
     double dashphase = 0;
     double * ldash = 0;
@@ -746,41 +774,42 @@ void SWFOutputDev::stroke(GfxState *state)
     }
    
     swfoutput_drawgfxline(&output, line, width, &col, capType, joinType, miterLimit);
-    gfxline_free(line);
 }
-void SWFOutputDev::fill(GfxState *state) 
+
+gfxcolor_t getFillColor(GfxState * state)
 {
-    GfxPath * path = state->getPath();
-    double opaq = state->getFillOpacity();
     GfxRGB rgb;
+    double opaq = state->getFillOpacity();
     state->getFillRGB(&rgb);
     gfxcolor_t col;
     col.r = (unsigned char)(rgb.r*255);
     col.g = (unsigned char)(rgb.g*255);
     col.b = (unsigned char)(rgb.b*255);
     col.a = (unsigned char)(opaq*255);
+    return col;
+}
 
-    gfxline_t*line= gfxPath_to_gfxline(state, path, 1);
+void SWFOutputDev::fillGfxLine(GfxState *state, gfxline_t*line) 
+{
+    gfxcolor_t col = getFillColor(state);
 
     if(getLogLevel() >= LOGLEVEL_TRACE)  {
         msg("<trace> fill %02x%02x%02x%02x\n", col.r, col.g, col.b, col.a);
         dump_outline(line);
     }
-
     swfoutput_fillgfxline(&output, line, &col);
+}
+void SWFOutputDev::fill(GfxState *state) 
+{
+    GfxPath * path = state->getPath();
+    gfxline_t*line= gfxPath_to_gfxline(state, path, 1);
+    fillGfxLine(state, line);
     gfxline_free(line);
 }
 void SWFOutputDev::eoFill(GfxState *state) 
 {
     GfxPath * path = state->getPath();
-    double opaq = state->getFillOpacity();
-    GfxRGB rgb;
-    state->getFillRGB(&rgb);
-    gfxcolor_t col;
-    col.r = (unsigned char)(rgb.r*255);
-    col.g = (unsigned char)(rgb.g*255);
-    col.b = (unsigned char)(rgb.b*255);
-    col.a = (unsigned char)(opaq*255);
+    gfxcolor_t col = getFillColor(state);
 
     gfxline_t*line= gfxPath_to_gfxline(state, path, 1);
 
@@ -792,11 +821,17 @@ void SWFOutputDev::eoFill(GfxState *state)
     swfoutput_fillgfxline(&output, line, &col);
     gfxline_free(line);
 }
+
 void SWFOutputDev::clip(GfxState *state) 
 {
     GfxPath * path = state->getPath();
     gfxline_t*line = gfxPath_to_gfxline(state, path, 1);
+    clipToGfxLine(state, line);
+    gfxline_free(line);
+}
 
+void SWFOutputDev::clipToGfxLine(GfxState *state, gfxline_t*line) 
+{
     if(getLogLevel() >= LOGLEVEL_TRACE)  {
         msg("<trace> clip\n");
         dump_outline(line);
@@ -804,7 +839,6 @@ void SWFOutputDev::clip(GfxState *state)
 
     swfoutput_startclip(&output, line);
     states[statepos].clipping++;
-    gfxline_free(line);
 }
 void SWFOutputDev::eoClip(GfxState *state) 
 {
@@ -839,6 +873,17 @@ SWFOutputDev::~SWFOutputDev()
 {
     swfoutput_destroy(&output);
     outputstarted = 0;
+
+    fontlist_t*l = this->fontlist;
+    while(l) {
+       fontlist_t*next = l->next;
+       l->next = 0;
+       gfxfont_free(l->font);
+       free(l->id);
+       free(l->filename);
+       free(l);
+       l = next;
+    }
 };
 GBool SWFOutputDev::upsideDown() 
 {
@@ -862,6 +907,12 @@ GBool SWFOutputDev::useGradients()
 char*renderModeDesc[]= {"fill", "stroke", "fill+stroke", "invisible",
                       "clip+fill", "stroke+clip", "fill+stroke+clip", "clip"};
 
+#define RENDER_FILL 0
+#define RENDER_STROKE 1
+#define RENDER_FILLSTROKE 2
+#define RENDER_INVISIBLE 3
+#define RENDER_CLIP 4
+
 static char tmp_printstr[4096];
 char* makeStringPrintable(char*str)
 {
@@ -888,30 +939,74 @@ char* makeStringPrintable(char*str)
     return tmp_printstr;
 }
 
+
+int getGfxCharID(gfxfont_t*font, int charnr, char *charname, int u)
+{
+    int t;
+    if(charname) {
+       for(t=0;t<font->num_glyphs;t++) {
+           if(font->glyphs[t].name && !strcmp(font->glyphs[t].name,charname)) {
+               msg("<debug> Char [%d,>%s<,%d] maps to %d\n", charnr, charname, u, t);
+               return t;
+           }
+       }
+       /* if we didn't find the character, maybe
+          we can find the capitalized version */
+       for(t=0;t<font->num_glyphs;t++) {
+           if(font->glyphs[t].name && !strcasecmp(font->glyphs[t].name,charname)) {
+               msg("<debug> Char [%d,>>%s<<,%d] maps to %d\n", charnr, charname, u, t);
+               return t;
+           }
+       }
+    }
+
+    /* try to use the unicode id */
+    if(u>=0 && u<font->max_unicode && font->unicode2glyph[u]>=0) {
+       msg("<debug> Char [%d,%s,>%d<] maps to %d\n", charnr, charname, u, font->unicode2glyph[u]);
+       return font->unicode2glyph[u];
+    }
+
+    if(charnr>=0 && charnr<font->num_glyphs) {
+       msg("<debug> Char [>%d<,%s,%d] maps to %d\n", charnr, charname, u, charnr);
+       return charnr;
+    }
+    
+    return -1;
+}
+
+
 void SWFOutputDev::beginString(GfxState *state, GString *s) 
 { 
     int render = state->getRender();
-    msg("<trace> beginString(%s) render=%d", s->getCString(), render);
+    if(current_text_stroke)
+       msg("<error> Error: Incompatible change of text rendering to %d while inside cliptext", render);
+
+    msg("<trace> beginString(%s) render=%d", makeStringPrintable(s->getCString()), render);
     double m11,m21,m12,m22;
 //    msg("<debug> %s beginstring \"%s\"\n", gfxstate2str(state), s->getCString());
     state->getFontTransMat(&m11, &m12, &m21, &m22);
     m11 *= state->getHorizScaling();
     m21 *= state->getHorizScaling();
-    swfoutput_setfontmatrix(&output, m11, -m21, m12, -m22);
-    if(render != 3 && render != 0)
-       msg("<warning> Text rendering mode %d (%s) not fully supported yet (for text \"%s\")", render, renderModeDesc[render&7], makeStringPrintable(s->getCString()));
+
+    this->current_font_matrix.m00 = m11 / 1024.0;
+    this->current_font_matrix.m01 = m12 / 1024.0;
+    this->current_font_matrix.m10 = -m21 / 1024.0;
+    this->current_font_matrix.m11 = -m22 / 1024.0;
+    this->current_font_matrix.tx = 0;
+    this->current_font_matrix.ty = 0;
+
+    gfxmatrix_t m = this->current_font_matrix;
+
+    /*if(render != 3 && render != 0)
+       msg("<warning> Text rendering mode %d (%s) not fully supported yet (for text \"%s\")", render, renderModeDesc[render&7], makeStringPrintable(s->getCString()));*/
     states[statepos].textRender = render;
 }
 
-static int textCount = 0;
-
 void SWFOutputDev::drawChar(GfxState *state, double x, double y,
                        double dx, double dy,
                        double originX, double originY,
                        CharCode c, Unicode *_u, int uLen)
 {
-    textCount++;
-
     int render = state->getRender();
     // check for invisible text -- this is used by Acrobat Capture
     if (render == 3)
@@ -920,14 +1015,7 @@ void SWFOutputDev::drawChar(GfxState *state, double x, double y,
     if(states[statepos].textRender != render)
        msg("<error> Internal error: drawChar.render!=beginString.render");
 
-    GfxRGB rgb;
-    double opaq = state->getFillOpacity();
-    state->getFillRGB(&rgb);
-    gfxcolor_t col;
-    col.r = (unsigned char)(rgb.r*255);
-    col.g = (unsigned char)(rgb.g*255);
-    col.b = (unsigned char)(rgb.b*255);
-    col.a = (unsigned char)(opaq*255);
+    gfxcolor_t col = getFillColor(state);
 
     Gushort *CIDToGIDMap = 0;
     GfxFont*font = state->getFont();
@@ -937,10 +1025,6 @@ void SWFOutputDev::drawChar(GfxState *state, double x, double y,
        msg("<debug> type3 char at %f/%f", x, y);
        return;
     }
-    double x1,y1;
-    x1 = x;
-    y1 = y;
-    state->transform(x, y, &x1, &y1);
     
     Unicode u=0;
     char*name=0;
@@ -973,24 +1057,79 @@ void SWFOutputDev::drawChar(GfxState *state, double x, double y,
        if(enc && enc[c])
           name = enc[c];
     }
     if (CIDToGIDMap) {
        msg("<debug> drawChar(%f, %f, c='%c' (%d), GID=%d, u=%d <%d>) CID=%d name=\"%s\" render=%d\n", x, y, (c&127)>=32?c:'?', c, CIDToGIDMap[c], u, uLen, font->isCIDFont(), FIXNULL(name), render);
-       swfoutput_drawchar(&output, x1, y1, name, CIDToGIDMap[c], u, &col);
+       c = CIDToGIDMap[c];
     } else {
        msg("<debug> drawChar(%f,%f,c='%c' (%d), u=%d <%d>) CID=%d name=\"%s\" render=%d\n",x,y,(c&127)>=32?c:'?',c,u, uLen, font->isCIDFont(), FIXNULL(name), render);
-       swfoutput_drawchar(&output, x1, y1, name, c, u, &col);
+    }
+
+    int charid = getGfxCharID(current_gfxfont, c, name, u);
+    if(charid<0) {
+       msg("<warning> Didn't find character '%s' (c=%d,u=%d) in current charset (%s, %d characters)", 
+               FIXNULL(name),c, u, FIXNULL((char*)current_font_id), current_gfxfont->num_glyphs);
+       return;
+    }
+
+    gfxmatrix_t m = this->current_font_matrix;
+    state->transform(x, y, &m.tx, &m.ty);
+
+    if(render == RENDER_FILL) {
+       swfoutput_gfxdrawchar(&output, current_font_id, charid, &col, &m);
+    } else {
+       msg("<debug> Drawing glyph %d as shape", charid);
+       gfxline_t*glyph = current_gfxfont->glyphs[charid].line;
+       gfxline_t*tglyph = gfxline_clone(glyph);
+       gfxline_transform(tglyph, &m);
+       current_text_stroke = gfxline_append(current_text_stroke, tglyph);
     }
 }
 
 void SWFOutputDev::endString(GfxState *state) 
 { 
-    msg("<trace> endString()");
+    int render = state->getRender();
+    msg("<trace> endString() render=%d textstroke=%08x", render, current_text_stroke);
+    if(states[statepos].textRender != render)
+       msg("<error> Internal error: drawChar.render!=beginString.render");
+    
+    if(current_text_stroke) {
+       /* fillstroke and stroke text rendering objects we can process right
+          now (as there may be texts of other rendering modes in this
+          text object)- clipping objects have to wait until endTextObject,
+          however */
+       if(render == RENDER_FILLSTROKE) {
+           fillGfxLine(state, current_text_stroke);
+           strokeGfxline(state, current_text_stroke);
+           gfxline_free(current_text_stroke);
+           current_text_stroke = 0;
+       } else if(render == RENDER_STROKE) {
+           strokeGfxline(state, current_text_stroke);
+           gfxline_free(current_text_stroke);
+           current_text_stroke = 0;
+       }
+    }
 }    
 
 void SWFOutputDev::endTextObject(GfxState *state)
 {
-    msg("<trace> endTextObject()");
+    int render = state->getRender();
+    msg("<trace> endTextObject() render=%d textstroke=%08x", render, current_text_stroke);
+    if(states[statepos].textRender != render)
+       msg("<error> Internal error: drawChar.render!=beginString.render");
+    
+    if(current_text_stroke) {
+       if((render&3) == RENDER_FILL || (render&3) == RENDER_FILLSTROKE) {
+           fillGfxLine(state, current_text_stroke);
+       }
+       if((render&3) == RENDER_STROKE || (render&3) == RENDER_FILLSTROKE) {
+           strokeGfxline(state, current_text_stroke);
+       }
+       if((render&4) == RENDER_CLIP) {
+           clipToGfxLine(state, current_text_stroke);
+       }
+       gfxline_free(current_text_stroke);
+       current_text_stroke = 0;
+    }
 }
 
 /* the logic seems to be as following:
@@ -1598,6 +1737,40 @@ void SWFOutputDev::setXRef(PDFDoc*doc, XRef *xref)
     this->xref = xref;
 }
 
+int SWFOutputDev::setGfxFont(char*id, char*filename)
+{
+    gfxfont_t*font = 0;
+    fontlist_t*last=0,*l = this->fontlist;
+
+    /* TODO: should this be part of the state? */
+    while(l) {
+       last = l;
+       if(!strcmp(l->id, id)) {
+           current_font_id = l->id;
+           current_gfxfont = l->font;
+           font = l->font;
+           swfoutput_gfxaddfont(&this->output, id, current_gfxfont);
+           return 1;
+       }
+       l = l->next;
+    }
+    if(!filename) return 0;
+    font = gfxfont_load(filename);
+    l = new fontlist_t;
+    l->font = font;
+    l->filename = strdup(filename);
+    l->id = strdup(id);
+    l->next = 0;
+    current_font_id = l->id;
+    current_gfxfont = l->font;
+    if(last) {
+       last->next = l;
+    } else {
+       this->fontlist = l;
+    }
+    swfoutput_gfxaddfont(&this->output, id, current_gfxfont);
+    return 1;
+}
 
 void SWFOutputDev::updateFont(GfxState *state) 
 {
@@ -1620,16 +1793,18 @@ void SWFOutputDev::updateFont(GfxState *state)
        }
     }
 
-    /* second, see if swfoutput already has this font
-       cached- if so, we are done */
-    if(swfoutput_queryfont(&output, fontid))
-    {
+    /* second, see if this is a font which was used before-
+       if so, we are done */
+    if(setGfxFont(fontid, 0)) {
+       free(fontid);
+       return;
+    }
+/*    if(swfoutput_queryfont(&output, fontid))
        swfoutput_setfont(&output, fontid, 0);
        
        msg("<debug> updateFont(%s) [cached]", fontid);
-       free(fontid);
        return;
-    }
+    }*/
 
     // look for Type 3 font
     if (gfxFont->getType() == fontType3) {
@@ -1682,7 +1857,11 @@ void SWFOutputDev::updateFont(GfxState *state)
     msg("<verbose> updateFont(%s) -> %s", fontid, fileName);
     dumpFontInfo("<verbose>", gfxFont);
 
-    swfoutput_setfont(&output, fontid, fileName);
+    //swfoutput_setfont(&output, fontid, fileName);
+    
+    if(!setGfxFont(fontid, 0)) {
+       setGfxFont(fontid, fileName);
+    }
    
     if(fileName && del)
        unlinkfont(fileName);
index f855461..c2a8259 100644 (file)
@@ -156,6 +156,10 @@ void swf_stroke(gfxdevice_t*dev, gfxline_t*line, gfxcoord_t width, gfxcolor_t*co
 void swf_fill(gfxdevice_t*dev, gfxline_t*line, gfxcolor_t*color);
 void swf_fillbitmap(gfxdevice_t*dev, gfxline_t*line, gfximage_t*img, gfxmatrix_t*matrix, gfxcxform_t*cxform);
 void swf_fillgradient(gfxdevice_t*dev, gfxline_t*line, gfxgradient_t*gradient, gfxgradienttype_t type, gfxmatrix_t*matrix);
+void swf_drawchar(gfxdevice_t*dev, char*fontid, int glyph, gfxcolor_t*color, gfxmatrix_t*matrix);
+void swf_addfont(gfxdevice_t*dev, char*fontid, gfxfont_t*font);
+
+int getCharID(SWFFONT *font, int charnr, char *charname, int u);
 
 static swfoutput_internal* init_internal_struct()
 {
@@ -198,6 +202,8 @@ static swfoutput_internal* init_internal_struct()
     i->device.fill = swf_fill;
     i->device.fillbitmap = swf_fillbitmap;
     i->device.fillgradient = swf_fillgradient;
+    i->device.addfont = swf_addfont;
+    i->device.drawchar = swf_drawchar;
 
     return i;
 };
@@ -238,8 +244,8 @@ static void transform (plotxy*p0,struct swfmatrix*m)
     double x,y;
     x = m->m11*p0->x+m->m12*p0->y;
     y = m->m21*p0->x+m->m22*p0->y;
-    p0->x = x + m->m13;
-    p0->y = y + m->m23;
+    p0->x = x + m->m31;
+    p0->y = y + m->m32;
 }
 
 // write a move-to command into the swf
@@ -756,8 +762,8 @@ static int drawchar(struct swfoutput*obj, SWFFONT *swffont, char*character, int
     if(i->textid<0)
        starttext(obj);
 
-    float x = m->m13;
-    float y = m->m23;
+    float x = m->m31;
+    float y = m->m32;
     float det = ((m->m11*m->m22)-(m->m21*m->m12));
     if(fabs(det) < 0.0005) { 
        /* x direction equals y direction- the text is invisible */
@@ -1009,9 +1015,13 @@ int swfoutput_queryfont(struct swfoutput*obj, char*fontid)
 
 /* set's the matrix which is to be applied to characters drawn by
    swfoutput_drawchar() */
-void swfoutput_setfontmatrix(struct swfoutput*obj,double m11,double m12,
-                                                  double m21,double m22)
+void swfoutput_setfontmatrix(struct swfoutput*obj,double m11,double m21,
+                                                  double m12,double m22)
 {
+    m11 *= 1024;
+    m12 *= 1024;
+    m21 *= 1024;
+    m22 *= 1024;
     swfoutput_internal*i = (swfoutput_internal*)obj->internal;
     if(obj->fontm11 == m11 &&
        obj->fontm12 == m12 &&
@@ -1042,8 +1052,8 @@ int swfoutput_drawchar(struct swfoutput* obj,double x,double y,char*character, i
     m.m12 = obj->fontm12;
     m.m21 = obj->fontm21;
     m.m22 = obj->fontm22;
-    m.m13 = x;
-    m.m23 = y;
+    m.m31 = x;
+    m.m32 = y;
     return drawchar(obj, obj->swffont, character, charnr, u, &m, color);
 }
 
@@ -1456,8 +1466,8 @@ void swfoutput_finalize(struct swfoutput*obj)
        TAG*mtag = i->swf.firstTag;
        if(iterator->swffont) {
            mtag = swf_InsertTag(mtag, ST_DEFINEFONT2);
-           /*if(!storeallcharacters)
-               swf_FontReduce(iterator->swffont);*/
+           if(!config_storeallcharacters)
+               swf_FontReduce(iterator->swffont);
            swf_FontSetDefine2(mtag, iterator->swffont);
        }
 
@@ -1930,6 +1940,18 @@ void swfoutput_endclip(struct swfoutput*obj)
     gfxdevice_t*dev = &i->device;
     dev->endclip(dev);
 }
+void swfoutput_gfxaddfont(struct swfoutput*obj, char*fontid, gfxfont_t*font)
+{
+    swfoutput_internal*i = (swfoutput_internal*)obj->internal;
+    gfxdevice_t*dev = &i->device;
+    dev->addfont(dev, fontid, font);
+}
+void swfoutput_gfxdrawchar(struct swfoutput*obj, char*fontid, int glyph, gfxcolor_t*c, gfxmatrix_t*m)
+{
+    swfoutput_internal*i = (swfoutput_internal*)obj->internal;
+    gfxdevice_t*dev = &i->device;
+    dev->drawchar(dev, fontid, glyph, c, m);
+}
 
 #define IMAGE_TYPE_JPEG 0
 #define IMAGE_TYPE_LOSSLESS 1
@@ -2225,7 +2247,7 @@ static int add_image(swfoutput_internal*i, gfximage_t*img, int targetwidth, int
     *newheight  = sizey;
     
     if(newsizex<sizex || newsizey<sizey) {
-       msg("<notice> Scaling %dx%d image to %dx%d", sizex, sizey, newsizex, newsizey);
+       msg("<verbose> Scaling %dx%d image to %dx%d", sizex, sizey, newsizex, newsizey);
        newpic = swf_ImageScale(mem, sizex, sizey, newsizex, newsizey);
        *newwidth = sizex = newsizex;
        *newheight  = sizey = newsizey;
@@ -2534,3 +2556,154 @@ void swf_fillgradient(gfxdevice_t*dev, gfxline_t*line, gfxgradient_t*gradient, g
 {
     msg("<error> Gradient filling not implemented yet");
 }
+
+static SWFFONT* gfxfont_to_swffont(gfxfont_t*font, char* id)
+{
+    SWFFONT*swffont = (SWFFONT*)rfx_calloc(sizeof(SWFFONT));
+    int t;
+    swffont->id = -1;
+    swffont->version = 2;
+    swffont->name = (U8*)strdup(id);
+    swffont->layout = (SWFLAYOUT*)rfx_calloc(sizeof(SWFLAYOUT));
+    swffont->layout->ascent = 0; /* ? */
+    swffont->layout->descent = 0;
+    swffont->layout->leading = 0;
+    swffont->layout->bounds = (SRECT*)rfx_calloc(sizeof(SRECT)*font->num_glyphs);
+    swffont->encoding = FONT_ENCODING_UNICODE;
+    swffont->numchars = font->num_glyphs;
+    swffont->maxascii = font->max_unicode;
+    swffont->ascii2glyph = (int*)rfx_calloc(sizeof(int)*swffont->maxascii);
+    swffont->glyph2ascii = (U16*)rfx_calloc(sizeof(U16)*swffont->numchars);
+    swffont->glyph = (SWFGLYPH*)rfx_calloc(sizeof(SWFGLYPH)*swffont->numchars);
+    swffont->glyphnames = (char**)rfx_calloc(sizeof(char*)*swffont->numchars);
+    for(t=0;t<font->max_unicode;t++) {
+       swffont->ascii2glyph[t] = font->unicode2glyph[t];
+    }
+    for(t=0;t<font->num_glyphs;t++) {
+       drawer_t draw;
+       gfxline_t*line;
+       swffont->glyph2ascii[t] = font->glyphs[t].unicode;
+       if(font->glyphs[t].name) {
+           swffont->glyphnames[t] = strdup(font->glyphs[t].name);
+       } else {
+           swffont->glyphnames[t] = 0;
+       }
+       swffont->glyph[t].advance = (int)(font->glyphs[t].advance * 20);
+
+       swf_Shape01DrawerInit(&draw, 0);
+       line = font->glyphs[t].line;
+       while(line) {
+           FPOINT c,to;
+           c.x = line->sx; c.y = line->sy;
+           to.x = line->x; to.y = line->y;
+           if(line->type == gfx_moveTo) {
+               draw.moveTo(&draw, &to);
+           } else if(line->type == gfx_lineTo) {
+               draw.lineTo(&draw, &to);
+           } else if(line->type == gfx_splineTo) {
+               draw.splineTo(&draw, &c, &to);
+           }
+           line = line->next;
+       }
+       draw.finish(&draw);
+       swffont->glyph[t].shape = swf_ShapeDrawerToShape(&draw);
+       draw.dealloc(&draw);
+    }
+    return swffont;
+}
+
+void swf_addfont(gfxdevice_t*dev, char*fontid, gfxfont_t*font)
+{
+    swfoutput_internal*i = (swfoutput_internal*)dev->internal;
+
+    if(i->obj->swffont && i->obj->swffont->name && !strcmp((char*)i->obj->swffont->name,fontid))
+       return; // the requested font is the current font
+    
+    fontlist_t*last=0,*l = i->fontlist;
+    while(l) {
+       last = l;
+       if(!strcmp((char*)l->swffont->name, fontid)) {
+           return; // we already know this font
+       }
+       l = l->next;
+    }
+    l = (fontlist_t*)rfx_calloc(sizeof(fontlist_t));
+    l->swffont = gfxfont_to_swffont(font, fontid);
+    l->next = 0;
+    if(last) {
+       last->next = l;
+    } else {
+       i->fontlist = l;
+    }
+    swf_FontSetID(l->swffont, getNewID(i->obj));
+
+    if(getScreenLogLevel() >= LOGLEVEL_DEBUG)  {
+       // print font information
+       msg("<debug> Font %s",fontid);
+       msg("<debug> |   ID: %d", l->swffont->id);
+       msg("<debug> |   Version: %d", l->swffont->version);
+       msg("<debug> |   Name: %s", l->swffont->name);
+       msg("<debug> |   Numchars: %d", l->swffont->numchars);
+       msg("<debug> |   Maxascii: %d", l->swffont->maxascii);
+       msg("<debug> |   Style: %d", l->swffont->style);
+       msg("<debug> |   Encoding: %d", l->swffont->encoding);
+       for(int iii=0; iii<l->swffont->numchars;iii++) {
+           msg("<debug> |   Glyph %d) name=%s, unicode=%d size=%d bbox=(%.2f,%.2f,%.2f,%.2f)\n", iii, l->swffont->glyphnames?l->swffont->glyphnames[iii]:"<nonames>", l->swffont->glyph2ascii[iii], l->swffont->glyph[iii].shape->bitlen, 
+                   l->swffont->layout->bounds[iii].xmin/20.0,
+                   l->swffont->layout->bounds[iii].ymin/20.0,
+                   l->swffont->layout->bounds[iii].xmax/20.0,
+                   l->swffont->layout->bounds[iii].ymax/20.0
+                   );
+           int t;
+           for(t=0;t<l->swffont->maxascii;t++) {
+               if(l->swffont->ascii2glyph[t] == iii)
+                   msg("<debug> | - maps to %d",t);
+           }
+       }
+    }
+}
+
+static void swf_switchfont(gfxdevice_t*dev, char*fontid)
+{
+    swfoutput_internal*i = (swfoutput_internal*)dev->internal;
+    swfoutput*obj = i->obj;
+
+    if(obj->swffont && obj->swffont->name && !strcmp((char*)obj->swffont->name,fontid))
+       return; // the requested font is the current font
+    
+    fontlist_t*l = i->fontlist;
+    while(l) {
+       if(!strcmp((char*)l->swffont->name, fontid)) {
+           obj->swffont = l->swffont;
+           return; //done!
+       }
+       l = l->next;
+    }
+    msg("<error> Unknown font id: %s", fontid);
+    return;
+}
+
+void swf_drawchar(gfxdevice_t*dev, char*fontid, int glyph, gfxcolor_t*color, gfxmatrix_t*matrix)
+{
+    swfoutput_internal*i = (swfoutput_internal*)dev->internal;
+    swfoutput*obj = i->obj;
+       
+    if(!obj->swffont || !obj->swffont->name || strcmp((char*)obj->swffont->name,fontid)) // not equal to current font
+    {
+       /* TODO: remove the need for this (enhance getcharacterbbox so that it can cope
+                with multiple fonts */
+       endtext(obj);
+
+       swf_switchfont(dev, fontid); // set the current font
+    }
+    swfoutput_setfontmatrix(obj, matrix->m00, matrix->m01, matrix->m10, matrix->m11);
+   
+    swfmatrix m;
+    m.m11 = obj->fontm11;
+    m.m12 = obj->fontm12;
+    m.m21 = obj->fontm21;
+    m.m22 = obj->fontm22;
+    m.m31 = matrix->tx;
+    m.m32 = matrix->ty;
+    drawchar(obj, obj->swffont, 0, glyph, -1, &m, color);
+}