fixed some error messages.
[swftools.git] / pdf2swf / SWFOutputDev.cc
index 75d19e6..8226c66 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
 #else
 #include "FoFiType1C.h"
 #include "FoFiTrueType.h"
+#include "GHash.h"
 #endif
 #include "SWFOutputDev.h"
 
 //swftools header files
 #include "swfoutput.h"
 #include "../lib/log.h"
+#include "../lib/gfxdevice.h"
+#include "../lib/gfxtools.h"
+#include "../lib/gfxfont.h"
 
 #include <math.h>
 
@@ -73,25 +77,32 @@ typedef struct _fontfile
     int used;
 } fontfile_t;
 
+typedef struct _parameter
+{
+    char*name;
+    char*value;
+    struct _parameter*next;
+} parameter_t;
+
+static parameter_t* device_config = 0;
+static parameter_t* device_config_next = 0;
+
 // for pdfswf_addfont
 static fontfile_t fonts[2048];
 static int fontnum = 0;
 
 static int config_use_fontconfig = 1;
 
-// swf <-> pdf pages
-// TODO: move into pdf_doc_t
-static int*pages = 0;
-static int pagebuflen = 0;
-static int pagepos = 0;
-
 /* config */
 static double caplinewidth = 3.0;
-static int zoom = 72; /* xpdf: 86 */
+static double zoom = 72; /* xpdf: 86 */
+static int forceType0Fonts = 1;
 
 static void printInfoString(Dict *infoDict, char *key, char *fmt);
 static void printInfoDate(Dict *infoDict, char *key, char *fmt);
 
+static char* lastfontdir = 0;
+
 struct mapping {
     char*pdffont;
     char*filename;
@@ -111,10 +122,29 @@ struct mapping {
 {"Symbol",                "s050000l"},
 {"ZapfDingbats",          "d050000l"}};
 
+class SWFOutputState {
+    public:
+    int clipping;
+    int textRender;
+    SWFOutputState() {
+       this->clipping = 0;
+       this->textRender = 0;
+    }
+};
+
+typedef struct _fontlist
+{
+    char*id;
+    char*filename;
+    gfxfont_t*font;
+    _fontlist*next;
+} fontlist_t;
+
+class InfoOutputDev;
+
 class SWFOutputDev:  public OutputDev {
-  int outputstarted;
-  struct swfoutput output;
 public:
+  gfxdevice_t* output;
 
   // Constructor.
   SWFOutputDev();
@@ -124,10 +154,18 @@ public:
 
   void setMove(int x,int y);
   void setClip(int x1,int y1,int x2,int y2);
+
+  void setInfo(InfoOutputDev*info) {this->info = info;}
   
   int save(char*filename);
+    
+  // Start a page.
+  void startFrame(int width, int height);
+
+  virtual void startPage(int pageNum, GfxState *state, double x1, double y1, double x2, double y2) ;
 
-  void getDimensions(int*x1,int*y1,int*x2,int*y2);
+  void endframe();
+  void* get(char*name);
 
   //----- get info about output device
 
@@ -147,9 +185,6 @@ public:
 
   void setXRef(PDFDoc*doc, XRef *xref);
 
-  // Start a page.
-  virtual void startPage(int pageNum, GfxState *state, double x1, double y1, double x2, double y2) ;
-
   //----- link borders
   virtual void drawLink(Link *link, Catalog *catalog) ;
 
@@ -188,6 +223,7 @@ public:
   //----- text drawing
   virtual void beginString(GfxState *state, GString *s) ;
   virtual void endString(GfxState *state) ;
+  virtual void endTextObject(GfxState *state);
   virtual void drawChar(GfxState *state, double x, double y,
                        double dx, double dy,
                        double originX, double originY,
@@ -201,16 +237,30 @@ public:
                         int width, int height, GfxImageColorMap *colorMap,
                         int *maskColors, GBool inlineImg);
   
-  virtual GBool beginType3Char(GfxState *state,
-                              CharCode code, Unicode *u, int uLen);
+  virtual GBool beginType3Char(GfxState *state, double x, double y, double dx, double dy, CharCode code, Unicode *u, int uLen);
   virtual void endType3Char(GfxState *state);
 
+  virtual void type3D0(GfxState *state, double wx, double wy);
+  virtual void type3D1(GfxState *state, double wx, double wy, double llx, double lly, double urx, double ury);
+
   private:
   void drawGeneralImage(GfxState *state, Object *ref, Stream *str,
                                   int width, int height, GfxImageColorMap*colorMap, GBool invert,
-                                  GBool inlineImg, int mask);
-  int clipping[64];
-  int clippos;
+                                  GBool inlineImg, int mask, int *maskColors);
+  int SWFOutputDev::setGfxFont(char*id, char*filename, double quality);
+  void strokeGfxline(GfxState *state, gfxline_t*line);
+  void clipToGfxLine(GfxState *state, gfxline_t*line);
+  void fillGfxLine(GfxState *state, gfxline_t*line);
+
+  void finish();
+
+  gfxresult_t*result; //filled when complete
+
+  char outer_clip_box; //whether the page clip box is still on
+
+  InfoOutputDev*info;
+  SWFOutputState states[64];
+  int statepos;
 
   int currentpage;
 
@@ -221,6 +271,7 @@ public:
   char* substituteFont(GfxFont*gfxFont, char*oldname);
   char* writeEmbeddedFontToFile(XRef*ref, GfxFont*font);
   int t1id;
+  int textmodeinfo; // did we write "Text will be rendered as polygon" yet?
   int jpeginfo; // did we write "File contains jpegs" yet?
   int pbminfo; // did we write "File contains jpegs" yet?
   int linkinfo; // did we write "File contains links" yet?
@@ -231,13 +282,6 @@ public:
 
   GfxState *laststate;
 
-  int pic_xids[1024];
-  int pic_yids[1024];
-  int pic_ids[1024];
-  int pic_width[1024];
-  int pic_height[1024];
-  int picpos;
-  int pic_id;
   char type3Warning;
 
   char* substitutetarget[256];
@@ -246,12 +290,79 @@ public:
 
   int user_movex,user_movey;
   int user_clipx1,user_clipx2,user_clipy1,user_clipy2;
+
+  gfxline_t* current_text_stroke;
+  gfxline_t* current_text_clip;
+  char* current_font_id;
+  gfxfont_t* current_gfxfont;
+  gfxmatrix_t current_font_matrix;
+
+  fontlist_t* fontlist;
+
+  int*pages;
+  int pagebuflen;
+  int pagepos;
+
+  friend void swf_output_preparepage(swf_output_t*swf, int pdfpage, int outputpage);
+};
+
+typedef struct _drawnchar
+{
+    gfxcoord_t x,y;
+    int charid;
+    gfxcolor_t color;
+} drawnchar_t;
+
+class CharBuffer
+{
+    drawnchar_t * chars;
+    int buf_size;
+    int num_chars;
+
+public:
+
+    CharBuffer()
+    {
+       buf_size = 32;
+       chars = (drawnchar_t*)malloc(sizeof(drawnchar_t)*buf_size);
+       memset(chars, 0, sizeof(drawnchar_t)*buf_size);
+       num_chars = 0;
+    }
+    ~CharBuffer()
+    {
+       free(chars);chars = 0;
+    }
+
+    void grow(int size)
+    {
+       if(size>=buf_size) {
+           buf_size += 32;
+           chars = (drawnchar_t*)realloc(chars, sizeof(drawnchar_t)*buf_size);
+       }
+    }
+
+    void addChar(int charid, gfxcoord_t x, gfxcoord_t y, gfxcolor_t color)
+    {
+       grow(num_chars);
+       chars[num_chars].x = x;
+       chars[num_chars].y = y;
+       chars[num_chars].color = color;
+       chars[num_chars].charid = charid;
+    }
 };
 
 static char*getFontID(GfxFont*font);
 
+struct FontInfo
+{
+    GfxFont*font;
+    double max_size;
+};
+
 class InfoOutputDev:  public OutputDev 
 {
+  GHash* id2font;
+  FontInfo* currentfont;
   public:
   int x1,y1,x2,y2;
   int num_links;
@@ -263,9 +374,11 @@ class InfoOutputDev:  public OutputDev
       num_links = 0;
       num_images = 0;
       num_fonts = 0;
+      id2font = new GHash();
   }
   virtual ~InfoOutputDev() 
   {
+      delete id2font;
   }
   virtual GBool upsideDown() {return gTrue;}
   virtual GBool useDrawChar() {return gTrue;}
@@ -287,14 +400,53 @@ class InfoOutputDev:  public OutputDev
   {
       num_links++;
   }
+  virtual double getMaximumFontSize(char*id)
+  {
+      FontInfo*info = (FontInfo*)id2font->lookup(id);
+      if(!info) {
+         msg("<error> Unknown font id: %s", id);
+         return 0.0;
+      }
+      return info->max_size;
+  }
+
   virtual void updateFont(GfxState *state) 
   {
       GfxFont*font = state->getFont();
       if(!font)
           return;
       char*id = getFontID(font);
-      /* FIXME*/
-      num_fonts++;
+      
+      FontInfo*info = (FontInfo*)id2font->lookup(id);
+      if(!info) {
+       GString* idStr = new GString(id);
+       info = new FontInfo;
+       info->font = font;
+       info->max_size = 0;
+       id2font->add(idStr, (void*)info);
+       free(id);
+       num_fonts++;
+      }
+      currentfont = info;
+  }
+  virtual void drawChar(GfxState *state, double x, double y,
+                       double dx, double dy,
+                       double originX, double originY,
+                       CharCode code, Unicode *u, int uLen)
+  {
+      int render = state->getRender();
+      if (render == 3)
+         return;
+      double m11,m21,m12,m22;
+      state->getFontTransMat(&m11, &m12, &m21, &m22);
+      m11 *= state->getHorizScaling();
+      m21 *= state->getHorizScaling();
+      double lenx = sqrt(m11*m11 + m12*m12);
+      double leny = sqrt(m21*m21 + m22*m22);
+      double len = lenx>leny?lenx:leny;
+      if(currentfont && currentfont->max_size < len) {
+         currentfont->max_size = len;
+      }
   }
   virtual void drawImageMask(GfxState *state, Object *ref, Stream *str,
                             int width, int height, GBool invert,
@@ -313,16 +465,13 @@ class InfoOutputDev:  public OutputDev
 SWFOutputDev::SWFOutputDev()
 {
     jpeginfo = 0;
+    textmodeinfo = 0;
     ttfinfo = 0;
     linkinfo = 0;
     pbminfo = 0;
     type3active = 0;
-    clippos = 0;
-    clipping[clippos] = 0;
-    outputstarted = 0;
+    statepos = 0;
     xref = 0;
-    picpos = 0;
-    pic_id = 0;
     substitutepos = 0;
     type3Warning = 0;
     user_movex = 0;
@@ -331,8 +480,22 @@ SWFOutputDev::SWFOutputDev()
     user_clipy1 = 0;
     user_clipx2 = 0;
     user_clipy2 = 0;
-    memset(&output, 0, sizeof(output));
-//    printf("SWFOutputDev::SWFOutputDev() \n");
+    current_text_stroke = 0;
+    current_text_clip = 0;
+    fontlist = 0;
+    result = 0;
+    outer_clip_box = 0;
+    pages = 0;
+    pagebuflen = 0;
+    pagepos = 0;
+    output = (gfxdevice_t*)malloc(sizeof(gfxdevice_t));
+    gfxdevice_swf_init(output);
+    /* configure device */
+    parameter_t*p = device_config;
+    while(p) {
+       output->setparameter(output, p->name, p->value);
+       p = p->next;
+    }
 };
   
 void SWFOutputDev::setMove(int x,int y)
@@ -351,13 +514,6 @@ void SWFOutputDev::setClip(int x1,int y1,int x2,int y2)
     this->user_clipx2 = x2;
     this->user_clipy2 = y2;
 }
-void SWFOutputDev::getDimensions(int*x1,int*y1,int*x2,int*y2)
-{
-    if(x1) *x1 = output.swf.movieSize.xmin/20;
-    if(y1) *y1 = output.swf.movieSize.ymin/20;
-    if(x2) *x2 = output.swf.movieSize.xmax/20;
-    if(y2) *y2 = output.swf.movieSize.ymax/20;
-}
 
 static char*getFontID(GfxFont*font)
 {
@@ -522,13 +678,14 @@ static void showFontError(GfxFont*font, int nr)
 
 static void dumpFontInfo(char*loglevel, GfxFont*font)
 {
-  char* name = getFontID(font);
+  char* id = getFontID(font);
+  char* name = getFontName(font);
   Ref* r=font->getID();
-  msg("%s=========== %s (ID:%d,%d) ==========\n", loglevel, getFontName(font), r->num,r->gen);
+  msg("%s=========== %s (ID:%d,%d) ==========\n", loglevel, name, r->num,r->gen);
 
   GString*gstr  = font->getTag();
    
-  msg("%s| Tag: %s\n", loglevel, name);
+  msg("%s| Tag: %s\n", loglevel, id);
   
   if(font->isCIDFont()) msg("%s| is CID font\n", loglevel);
 
@@ -562,10 +719,12 @@ static void dumpFontInfo(char*loglevel, GfxFont*font)
   
   Ref embRef;
   GBool embedded = font->getEmbeddedFontID(&embRef);
-  if(font->getEmbeddedFontName())
-    name = font->getEmbeddedFontName()->getCString();
+  char*embeddedName=0;
+  if(font->getEmbeddedFontName()) {
+    embeddedName = font->getEmbeddedFontName()->getCString();
+  }
   if(embedded)
-   msg("%s| Embedded name: %s id: %d\n",loglevel, FIXNULL(name), embRef.num);
+   msg("%s| Embedded id: %s id: %d\n",loglevel, FIXNULL(embeddedName), embRef.num);
 
   gstr = font->getExtFontFile();
   if(gstr)
@@ -577,111 +736,93 @@ static void dumpFontInfo(char*loglevel, GfxFont*font)
   if(font->isSymbolic()) msg("%s| is symbolic\n", loglevel);
   if(font->isItalic()) msg("%s| is italic\n", loglevel);
   if(font->isBold()) msg("%s| is bold\n", loglevel);
+
+  free(id);
+  free(name);
 }
 
 //void SWFOutputDev::drawImageMask(GfxState *state, Object *ref, Stream *str, int width, int height, GBool invert, GBool inlineImg) {printf("void SWFOutputDev::drawImageMask(GfxState *state, Object *ref, Stream *str, int width, int height, GBool invert, GBool inlineImg) \n");}
 //void SWFOutputDev::drawImage(GfxState *state, Object *ref, Stream *str, int width, int height, GfxImageColorMap *colorMap, GBool inlineImg) {printf("void SWFOutputDev::drawImage(GfxState *state, Object *ref, Stream *str, int width, int height, GfxImageColorMap *colorMap, GBool inlineImg) \n");}
 
-static void free_outline(SWF_OUTLINE*outline)
-{
-    while(outline) {
-        SWF_OUTLINE*next = outline->link;
-        free(outline);
-        outline = next;
-    }
-}
 
-static void dump_outline(SWF_OUTLINE*outline)
+void dump_outline(gfxline_t*line)
 {
-    double x=0,y=0;
-    while(outline) {
-        double lastx=x,lasty=y;
-        x += (outline->dest.x/(float)0xffff);
-        y += (outline->dest.y/(float)0xffff);
-        if(outline->type == SWF_PATHTYPE_MOVE) {
-            msg("<trace> | moveto %f,%f", x,y);
-        } else if(outline->type == SWF_PATHTYPE_LINE) {
-            msg("<trace> | lineto: %f,%f\n",x,y);
-        } else if(outline->type == SWF_PATHTYPE_BEZIER) {
-            SWF_BEZIERSEGMENT*o2 = (SWF_BEZIERSEGMENT*)outline;
-            float x1 = o2->C.x/(float)0xffff+lastx;
-            float y1 = o2->C.y/(float)0xffff+lasty;
-            float x2 = o2->B.x/(float)0xffff+lastx;
-            float y2 = o2->B.y/(float)0xffff+lasty;
-            msg("<trace> | spline: %f,%f -> %f,%f -> %f,%f\n",x1,y1,x2,y2,x,y);
-        } 
-        outline = outline->link;
+    while(line) {
+       if(line->type == gfx_moveTo) {
+           msg("<debug> |     moveTo %.2f %.2f", line->x,line->y);
+       } else if(line->type == gfx_lineTo) {
+           msg("<debug> |     lineTo %.2f %.2f", line->x,line->y);
+       } else if(line->type == gfx_splineTo) {
+           msg("<debug> |     splineTo (%.2f %.2f) %.2f %.2f", line->sx,line->sy, line->x, line->y);
+       }
+       line = line->next;
     }
 }
 
-SWF_OUTLINE* gfxPath_to_SWF_OUTLINE(GfxState*state, GfxPath*path)
+gfxline_t* gfxPath_to_gfxline(GfxState*state, GfxPath*path, int closed, int user_movex, int user_movey)
 {
     int num = path->getNumSubpaths();
     int s,t;
-    bezierpathsegment*start,*last=0;
-    bezierpathsegment*outline = start = (bezierpathsegment*)malloc(sizeof(bezierpathsegment));
     int cpos = 0;
-    double lastx=0,lasty=0;
+    double lastx=0,lasty=0,posx=0,posy=0;
+    int needsfix=0;
     if(!num) {
        msg("<warning> empty path");
-       outline->type = SWF_PATHTYPE_MOVE;
-       outline->dest.x = 0;
-       outline->dest.y = 0;
-       outline->link = 0;
-       return (SWF_OUTLINE*)outline;
+       return 0;
     }
+    gfxdrawer_t draw;
+    gfxdrawer_target_gfxline(&draw);
+
     for(t = 0; t < num; t++) {
        GfxSubpath *subpath = path->getSubpath(t);
        int subnum = subpath->getNumPoints();
+       double bx=0,by=0,cx=0,cy=0;
 
        for(s=0;s<subnum;s++) {
-          double nx,ny;
-          state->transform(subpath->getX(s),subpath->getY(s),&nx,&ny);
-          int x = (int)((nx-lastx)*0xffff);
-          int y = (int)((ny-lasty)*0xffff);
-          if(s==0) 
-          {
-               last = outline;
-               outline->type = SWF_PATHTYPE_MOVE;
-               outline->dest.x = x;
-               outline->dest.y = y;
-               outline->link = (SWF_OUTLINE*)malloc(sizeof(bezierpathsegment));
-               outline = (bezierpathsegment*)outline->link;
+          double x,y;
+          
+          state->transform(subpath->getX(s),subpath->getY(s),&x,&y);
+          x += user_movex;
+          y += user_movey;
+
+          if(s==0) {
+               if(closed && needsfix && (fabs(posx-lastx)+fabs(posy-lasty))>0.001) {
+                   draw.lineTo(&draw, lastx, lasty);
+               }
+               draw.moveTo(&draw, x,y);
+               posx = lastx = x; 
+               posy = lasty = y;
                cpos = 0;
-               lastx = nx;
-               lasty = ny;
-          }
-          else if(subpath->getCurve(s) && !cpos)
-          {
-               outline->B.x = x;
-               outline->B.y = y;
+               needsfix = 0;
+          } else if(subpath->getCurve(s) && cpos==0) {
+               bx = x;
+               by = y;
                cpos = 1;
-          } 
-          else if(subpath->getCurve(s) && cpos)
-          {
-               outline->C.x = x;
-               outline->C.y = y;
+          } else if(subpath->getCurve(s) && cpos==1) {
+               cx = x;
+               cy = y;
                cpos = 2;
-          }
-          else
-          {
-               last = outline;
-               outline->dest.x = x;
-               outline->dest.y = y;
-               outline->type = cpos?SWF_PATHTYPE_BEZIER:SWF_PATHTYPE_LINE;
-               outline->link = (SWF_OUTLINE*)malloc(sizeof(bezierpathsegment));
-               outline = (bezierpathsegment*)outline->link;
+          } else {
+               posx = x;
+               posy = y;
+               if(cpos==0) {
+                   draw.lineTo(&draw, x,y);
+               } else {
+                   gfxdraw_cubicTo(&draw, bx,by, cx,cy, x,y, 0.05);
+               }
+               needsfix = 1;
                cpos = 0;
-               lastx = nx;
-               lasty = ny;
           }
        }
     }
-    if(last->link) {free(last->link);}
-    last->link = 0;
-
-    return (SWF_OUTLINE*)start;
+    /* fix non-closed lines */
+    if(closed && needsfix && (fabs(posx-lastx)+fabs(posy-lasty))>0.001) {
+       draw.lineTo(&draw, lastx, lasty);
+    }
+    gfxline_t*result = (gfxline_t*)draw.result(&draw);
+    return result;
 }
+
 /*----------------------------------------------------------------------------
  * Primitive Graphic routines
  *----------------------------------------------------------------------------*/
@@ -689,129 +830,231 @@ SWF_OUTLINE* gfxPath_to_SWF_OUTLINE(GfxState*state, GfxPath*path)
 void SWFOutputDev::stroke(GfxState *state) 
 {
     GfxPath * path = state->getPath();
+    gfxline_t*line= gfxPath_to_gfxline(state, path, 0, user_movex, user_movey);
+    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();
     double width = state->getTransformedLineWidth();
-    struct swfmatrix m;
+
     GfxRGB rgb;
     double opaq = state->getStrokeOpacity();
-    state->getStrokeRGB(&rgb);
+    if(type3active)
+       state->getFillRGB(&rgb);
+    else
+       state->getStrokeRGB(&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);
+   
+    gfx_capType capType = gfx_capRound;
+    if(lineCap == 0) capType = gfx_capButt;
+    else if(lineCap == 1) capType = gfx_capRound;
+    else if(lineCap == 2) capType = gfx_capSquare;
+
+    gfx_joinType joinType = gfx_joinRound;
+    if(lineJoin == 0) joinType = gfx_joinMiter;
+    else if(lineJoin == 1) joinType = gfx_joinRound;
+    else if(lineJoin == 2) joinType = gfx_joinBevel;
+
+    int dashnum = 0;
+    double dashphase = 0;
+    double * ldash = 0;
+    state->getLineDash(&ldash, &dashnum, &dashphase);
+
+    gfxline_t*line2 = 0;
+
+    if(dashnum && ldash) {
+       float * dash = (float*)malloc(sizeof(float)*(dashnum+1));
+       int t;
+       double cut = 0;
+       int fixzero = 0;
+       msg("<trace> %d dashes", dashnum);
+       msg("<trace> |  phase: %f", dashphase);
+       for(t=0;t<dashnum;t++) {
+           dash[t] = ldash[t];
+           msg("<trace> |  d%-3d: %f", t, ldash[t]);
+       }
+       dash[dashnum] = -1;
+       if(getLogLevel() >= LOGLEVEL_TRACE) {
+           dump_outline(line);
+       }
 
-    m.m11 = 1; m.m21 = 0; m.m22 = 1;
-    m.m12 = 0; m.m13 = 0; m.m23 = 0;
-    SWF_OUTLINE*outline = gfxPath_to_SWF_OUTLINE(state, path);
+       line2 = gfxtool_dash_line(line, dash, dashphase);
+       line = line2;
+       free(dash);
+       msg("<trace> After dashing:");
+    }
     
     if(getLogLevel() >= LOGLEVEL_TRACE)  {
-        msg("<trace> stroke\n");
-        dump_outline(outline);
+       double gray;
+       state->getStrokeGray(&gray);
+        msg("<trace> stroke width=%f join=%s cap=%s dashes=%d color=%02x%02x%02x%02x gray=%f\n",
+               width,
+               lineJoin==0?"miter": (lineJoin==1?"round":"bevel"),
+               lineCap==0?"butt": (lineJoin==1?"round":"square"),
+               dashnum,
+               col.r,col.g,col.b,col.a,
+               gray
+               );
+        dump_outline(line);
     }
+   
+    //swfoutput_drawgfxline(output, line, width, &col, capType, joinType, miterLimit);
+    output->stroke(output, line, width, &col, capType, joinType, miterLimit);
+    
+    if(line2)
+       gfxline_free(line2);
+}
 
-    lineJoin = 1; // other line joins are not yet supported by the swf encoder
-                  // TODO: support bevel joints
-
-    if(((lineCap==1) && (lineJoin==1)) || width<=caplinewidth) {
-       /* FIXME- if the path is smaller than 2 segments, we could ignore
-          lineJoin */
-       swfoutput_setdrawmode(&output, DRAWMODE_STROKE);
-       swfoutput_drawpath(&output, outline, &m);
-    } else {
-       swfoutput_setfillcolor(&output, (char)(rgb.r*255), (char)(rgb.g*255), 
-                                       (char)(rgb.b*255), (char)(opaq*255));
+gfxcolor_t getFillColor(GfxState * state)
+{
+    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;
+}
 
-       //swfoutput_setlinewidth(&output, 1.0); //only for debugging
-       //swfoutput_setstrokecolor(&output, 0, 255, 0, 255); //likewise, see below
-       //swfoutput_setfillcolor(&output, 255, 0, 0, 255); //likewise, see below
+void SWFOutputDev::fillGfxLine(GfxState *state, gfxline_t*line) 
+{
+    gfxcolor_t col = getFillColor(state);
 
-       swfoutput_drawpath2poly(&output, outline, &m, lineJoin, lineCap, width, miterLimit);
-       updateLineWidth(state);  //reset
-       updateStrokeColor(state); //reset
-       updateFillColor(state);  //reset
+    if(getLogLevel() >= LOGLEVEL_TRACE)  {
+        msg("<trace> fill %02x%02x%02x%02x\n", col.r, col.g, col.b, col.a);
+        dump_outline(line);
     }
-    free_outline(outline);
+    output->fill(output, line, &col);
 }
 void SWFOutputDev::fill(GfxState *state) 
 {
     GfxPath * path = state->getPath();
-    struct swfmatrix m;
-    m.m11 = 1; m.m21 = 0; m.m22 = 1;
-    m.m12 = 0; m.m13 = 0; m.m23 = 0;
-    SWF_OUTLINE*outline = gfxPath_to_SWF_OUTLINE(state, path);
-
-    if(getLogLevel() >= LOGLEVEL_TRACE)  {
-        msg("<trace> fill\n");
-        dump_outline(outline);
-    }
-
-    swfoutput_setdrawmode(&output, DRAWMODE_FILL);
-    swfoutput_drawpath(&output, outline, &m);
-    free_outline(outline);
+    gfxline_t*line= gfxPath_to_gfxline(state, path, 1, user_movex, user_movey);
+    fillGfxLine(state, line);
+    gfxline_free(line);
 }
 void SWFOutputDev::eoFill(GfxState *state) 
 {
     GfxPath * path = state->getPath();
-    struct swfmatrix m;
-    m.m11 = 1; m.m21 = 0; m.m22 = 1;
-    m.m12 = 0; m.m13 = 0; m.m23 = 0;
-    SWF_OUTLINE*outline = gfxPath_to_SWF_OUTLINE(state, path);
+    gfxcolor_t col = getFillColor(state);
+
+    gfxline_t*line= gfxPath_to_gfxline(state, path, 1, user_movex, user_movey);
 
     if(getLogLevel() >= LOGLEVEL_TRACE)  {
         msg("<trace> eofill\n");
-        dump_outline(outline);
+        dump_outline(line);
     }
 
-    swfoutput_setdrawmode(&output, DRAWMODE_EOFILL);
-    swfoutput_drawpath(&output, outline, &m);
-    free_outline(outline);
+    output->fill(output, line, &col);
+    gfxline_free(line);
 }
+
 void SWFOutputDev::clip(GfxState *state) 
 {
     GfxPath * path = state->getPath();
-    struct swfmatrix m;
-    m.m11 = 1; m.m22 = 1;
-    m.m12 = 0; m.m21 = 0; 
-    m.m13 = 0; m.m23 = 0;
-    SWF_OUTLINE*outline = gfxPath_to_SWF_OUTLINE(state, path);
+    gfxline_t*line = gfxPath_to_gfxline(state, path, 1, user_movex, user_movey);
+    clipToGfxLine(state, line);
+    gfxline_free(line);
+}
 
+void SWFOutputDev::clipToGfxLine(GfxState *state, gfxline_t*line) 
+{
     if(getLogLevel() >= LOGLEVEL_TRACE)  {
         msg("<trace> clip\n");
-        dump_outline(outline);
+        dump_outline(line);
     }
 
-    swfoutput_startclip(&output, outline, &m);
-    clipping[clippos] ++;
-    free_outline(outline);
+    output->startclip(output, line);
+    states[statepos].clipping++;
 }
 void SWFOutputDev::eoClip(GfxState *state) 
 {
     GfxPath * path = state->getPath();
-    struct swfmatrix m;
-    m.m11 = 1; m.m21 = 0; m.m22 = 1;
-    m.m12 = 0; m.m13 = 0; m.m23 = 0;
-    SWF_OUTLINE*outline = gfxPath_to_SWF_OUTLINE(state, path);
+    gfxline_t*line = gfxPath_to_gfxline(state, path, 1, user_movex, user_movey);
 
     if(getLogLevel() >= LOGLEVEL_TRACE)  {
         msg("<trace> eoclip\n");
-        dump_outline(outline);
+        dump_outline(line);
+    }
+
+    output->startclip(output, line);
+    states[statepos].clipping++;
+    gfxline_free(line);
+}
+
+void SWFOutputDev::endframe()
+{
+    if(outer_clip_box) {
+       output->endclip(output);
+       outer_clip_box = 0;
     }
 
-    swfoutput_startclip(&output, outline, &m);
-    clipping[clippos] ++;
-    free_outline(outline);
+    output->endpage(output);
+}
+
+void SWFOutputDev::finish()
+{
+    if(outer_clip_box) {
+       if(output) {
+           output->endclip(output);
+       }
+       outer_clip_box = 0;
+    }
+    if(output) {
+       this->result = output->finish(output);
+       free(output);output=0;
+    }
 }
+
 int SWFOutputDev::save(char*filename)
 {
-    return swfoutput_save(&output, filename);
+    finish();
+    return result->save(result, filename);
+}
+void* SWFOutputDev::get(char*name)
+{
+    finish();
+    return result->get(result, name);
 }
 
 SWFOutputDev::~SWFOutputDev() 
 {
-    swfoutput_destroy(&output);
-    outputstarted = 0;
+    finish();
+
+    if(this->result) {
+       this->result->destroy(this->result);
+       this->result = 0;
+    }
+    
+    if(this->pages) {
+       free(this->pages); this->pages = 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;
+    }
+    this->fontlist = 0;
 };
 GBool SWFOutputDev::upsideDown() 
 {
-    msg("<debug> upsidedown? yes");
     return gTrue;
 };
 GBool SWFOutputDev::useDrawChar() 
@@ -828,14 +1071,108 @@ GBool SWFOutputDev::useGradients()
     return gTrue;
 }
 
+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)
+{
+    int len = strlen(str);
+    int dots = 0;
+    if(len>=80) {
+       len = 80;
+       dots = 1;
+    }
+    int t;
+    for(t=0;t<len;t++) {
+       char c = str[t];
+       if(c<32 || c>124) {
+           c = '.';
+       }
+       tmp_printstr[t] = c;
+    }
+    if(dots) {
+       tmp_printstr[len++] = '.';
+       tmp_printstr[len++] = '.';
+       tmp_printstr[len++] = '.';
+    }
+    tmp_printstr[len] = 0;
+    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];
+    }
+
+    /* we don't need to "draw" space characters, so don't overdo the search
+       for a matching glyph */
+    if(charname && !strcasecmp(charname, "space"))
+       return -1;
+
+    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();
+    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);
+
+    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;
 }
 
 void SWFOutputDev::drawChar(GfxState *state, double x, double y,
@@ -843,82 +1180,212 @@ void SWFOutputDev::drawChar(GfxState *state, double x, double y,
                        double originX, double originY,
                        CharCode c, Unicode *_u, int uLen)
 {
+    int render = state->getRender();
     // check for invisible text -- this is used by Acrobat Capture
-    if ((state->getRender() & 3) == 3)
+    if (render == 3) {
+       msg("<debug> Ignoring invisible text: char %d at %f,%f", c, x, y);
        return;
+    }
 
+    if(states[statepos].textRender != render)
+       msg("<error> Internal error: drawChar.render!=beginString.render");
+
+    gfxcolor_t col = getFillColor(state);
+
+    Gushort *CIDToGIDMap = 0;
     GfxFont*font = state->getFont();
 
     if(font->getType() == fontType3) {
        /* type 3 chars are passed as graphics */
+       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;
-    if(_u && uLen) 
-       u = *_u;
-
-    /* find out the character name */
     char*name=0;
-    if(font->isCIDFont() && u) {
-       GfxCIDFont*cfont = (GfxCIDFont*)font;
-       int t;
-       for(t=0;t<sizeof(nameToUnicodeTab)/sizeof(nameToUnicodeTab[0]);t++) {
-           /* todo: should be precomputed */
-           if(nameToUnicodeTab[t].u == u) {
-               name = nameToUnicodeTab[t].name;
-               break;
+
+    if(_u && uLen) {
+       u = *_u;
+       if (u) {
+           int t;
+           /* find out char name from unicode index 
+              TODO: should be precomputed
+            */
+           for(t=0;t<sizeof(nameToUnicodeTab)/sizeof(nameToUnicodeTab[0]);t++) {
+               if(nameToUnicodeTab[t].u == u) {
+                   name = nameToUnicodeTab[t].name;
+                   break;
+               }
            }
        }
+    }
+
+    if(font->isCIDFont()) {
+       GfxCIDFont*cfont = (GfxCIDFont*)font;
+
+       if(font->getType() == fontCIDType2)
+           CIDToGIDMap = cfont->getCIDToGID();
     } else {
        Gfx8BitFont*font8;
        font8 = (Gfx8BitFont*)font;
        char**enc=font8->getEncoding();
-       if(enc && enc[c])
+       if(enc && enc[c] && strcasecmp(enc[c], "space")) {
           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);
+       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);
+    }
+
+    int charid = -1;
+   
+    if(uLen<=1) {
+       charid = getGfxCharID(current_gfxfont, c, name, u);
+    } else {
+       charid = getGfxCharID(current_gfxfont, c, 0, -1);
+       if(charid < 0) {
+           /* multiple unicodes- should usually map to a ligature.
+              if the ligature doesn't exist, we need to draw
+              the characters one-by-one. */
+           int t;
+           msg("<warning> ligature %d missing in font %s\n", c, current_font_id);
+           for(t=0;t<uLen;t++) {
+               drawChar(state, x, y, dx, dy, originX, originY, c, _u+t, 1);
+           }
+           return;
+       }
+    }
+
+    if(charid<0 && name) {
+       if(strcasecmp(name, "space")) {
+           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);
+    m.tx += user_movex;
+    m.ty += user_movey;
+
+    if(render == RENDER_FILL) {
+       output->drawchar(output, current_font_id, charid, &col, &m);
+    } else {
+       msg("<debug> Drawing glyph %d as shape", charid);
+       if(!textmodeinfo) {
+           msg("<notice> Some texts will be rendered as shape");
+           textmodeinfo = 1;
+       }
+       gfxline_t*glyph = current_gfxfont->glyphs[charid].line;
+       gfxline_t*tglyph = gfxline_clone(glyph);
+       gfxline_transform(tglyph, &m);
+       if((render&3) != RENDER_INVISIBLE) {
+           gfxline_t*add = gfxline_clone(tglyph);
+           current_text_stroke = gfxline_append(current_text_stroke, add);
+       }
+       if(render&RENDER_CLIP) {
+           gfxline_t*add = gfxline_clone(tglyph);
+           current_text_clip = gfxline_append(current_text_clip, add);
+       }
+       gfxline_free(tglyph);
+    }
+}
+
+void SWFOutputDev::endString(GfxState *state) 
+{ 
+    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");
     
-    msg("<debug> drawChar(%f,%f,c='%c' (%d),u=%d <%d>) CID=%d name=\"%s\"\n",x,y,(c&127)>=32?c:'?',c,u, uLen, font->isCIDFont(), FIXNULL(name));
+    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&3) == RENDER_FILL) {
+           fillGfxLine(state, current_text_stroke);
+           gfxline_free(current_text_stroke);
+           current_text_stroke = 0;
+       } else if((render&3) == RENDER_FILLSTROKE) {
+           fillGfxLine(state, current_text_stroke);
+           strokeGfxline(state, current_text_stroke);
+           gfxline_free(current_text_stroke);
+           current_text_stroke = 0;
+       } else if((render&3) == RENDER_STROKE) {
+           strokeGfxline(state, current_text_stroke);
+           gfxline_free(current_text_stroke);
+           current_text_stroke = 0;
+       }
+    }
+}    
 
-    /*x1 = (int)(x1+0.5);
-    y1 = (int)(y1+0.5);*/
+void SWFOutputDev::endTextObject(GfxState *state)
+{
+    int render = state->getRender();
+    msg("<trace> endTextObject() render=%d textstroke=%08x clipstroke=%08x", render, current_text_stroke, current_text_clip);
+    if(states[statepos].textRender != render)
+       msg("<error> Internal error: drawChar.render!=beginString.render");
     
-    int ret = swfoutput_drawchar(&output, x1, y1, name, c, u);
+    if(current_text_clip) {
+       clipToGfxLine(state, current_text_clip);
+       gfxline_free(current_text_clip);
+       current_text_clip = 0;
+    }
 }
 
-void SWFOutputDev::endString(GfxState *state) { 
-}    
+/* the logic seems to be as following:
+   first, beginType3Char is called, with the charcode and the coordinates.
+   if this function returns true, it already knew about the char and has now drawn it.
+   if the function returns false, it's a new char, and type3D1 is called with some parameters-
+   the all draw operations until endType3Char are part of the char (which in this moment is
+   at the position first passed to beginType3Char). the char ends with endType3Char.
 
-GBool SWFOutputDev::beginType3Char(GfxState *state,
-                              CharCode code, Unicode *u, int uLen)
+   The drawing operations between beginType3Char and endType3Char are somewhat different to
+   the normal ones. For example, the fillcolor equals the stroke color.
+*/
+
+GBool SWFOutputDev::beginType3Char(GfxState *state, double x, double y, double dx, double dy, CharCode code, Unicode *u, int uLen)
 {
     msg("<debug> beginType3Char %d, %08x, %d", code, *u, uLen);
     type3active = 1;
-    /* the character itself is going to be passed using
-       drawImageMask() */
+    /* the character itself is going to be passed using the draw functions */
     return gFalse; /* gTrue= is_in_cache? */
 }
 
+void SWFOutputDev::type3D0(GfxState *state, double wx, double wy) {
+    msg("<debug> type3D0 width=%f height=%f", wx, wy);
+}
+void SWFOutputDev::type3D1(GfxState *state, double wx, double wy, double llx, double lly, double urx, double ury) {
+    msg("<debug> type3D1 width=%f height=%f bbox=(%f,%f,%f,%f)", wx, wy,
+           llx,lly,urx,ury);
+}
+
 void SWFOutputDev::endType3Char(GfxState *state)
 {
     type3active = 0;
     msg("<debug> endType3Char");
 }
 
+void SWFOutputDev::startFrame(int width, int height) 
+{
+    output->startpage(output, width, height);
+}
+
 void SWFOutputDev::startPage(int pageNum, GfxState *state, double crop_x1, double crop_y1, double crop_x2, double crop_y2) 
 {
     this->currentpage = pageNum;
     double x1,y1,x2,y2;
     int rot = doc->getPageRotate(1);
+    gfxcolor_t white;
     laststate = state;
-    msg("<verbose> startPage %d (%f,%f,%f,%f)\n", pageNum, crop_x1, crop_y1, crop_x2, crop_y2);
-    if(rot!=0)
-        msg("<verbose> page is rotated %d degrees\n", rot);
+    gfxline_t clippath[5];
+
+    white.r = white.g = white.b = white.a = 255;
 
     /* state->transform(state->getX1(),state->getY1(),&x1,&y1);
     state->transform(state->getX2(),state->getY2(),&x2,&y2);
@@ -929,27 +1396,39 @@ void SWFOutputDev::startPage(int pageNum, GfxState *state, double crop_x1, doubl
     y1 = crop_y1;
     x2 = crop_x2;
     y2 = crop_y2;*/
-    state->transform(crop_x1,crop_y1,&x1,&y1);
-    state->transform(crop_x2,crop_y2,&x2,&y2);
+    state->transform(crop_x1,crop_y1,&x1,&y1); //x1 += user_movex; y1 += user_movey;
+    state->transform(crop_x2,crop_y2,&x2,&y2); //x2 += user_movex; y2 += user_movey;
 
     if(x2<x1) {double x3=x1;x1=x2;x2=x3;}
     if(y2<y1) {double y3=y1;y1=y2;y2=y3;}
 
+
     /* apply user clip box */
     if(user_clipx1|user_clipy1|user_clipx2|user_clipy2) {
-        if(user_clipx1 > x1) x1 = user_clipx1;
-        if(user_clipx2 < x2) x2 = user_clipx2;
-        if(user_clipy1 > y1) y1 = user_clipy1;
-        if(user_clipy2 < y2) y2 = user_clipy2;
+        /*if(user_clipx1 > x1)*/ x1 = user_clipx1;
+        /*if(user_clipx2 < x2)*/ x2 = user_clipx2;
+        /*if(user_clipy1 > y1)*/ y1 = user_clipy1;
+        /*if(user_clipy2 < y2)*/ y2 = user_clipy2;
     }
 
-    if(!outputstarted) {
-        msg("<verbose> Bounding box is (%f,%f)-(%f,%f)", x1,y1,x2,y2);
-        swfoutput_init(&output);
-        outputstarted = 1;
+    //msg("<verbose> Bounding box is (%f,%f)-(%f,%f) [shifted by %d/%d]", x1,y1,x2,y2, user_movex, user_movey);
+    
+    if(outer_clip_box) {
+       output->endclip(output);
+       outer_clip_box = 0;
     }
-      
-    swfoutput_newpage(&output, pageNum, user_movex, user_movey, (int)x1, (int)y1, (int)x2, (int)y2);
+
+    msg("<notice> processing PDF page %d (%dx%d:%d:%d) (move:%d:%d)", pageNum, (int)x2-(int)x1,(int)y2-(int)y1, (int)x1, (int)y1, user_movex, user_movey);
+    if(rot!=0)
+        msg("<verbose> page is rotated %d degrees\n", rot);
+
+    clippath[0].type = gfx_moveTo;clippath[0].x = x1; clippath[0].y = y1; clippath[0].next = &clippath[1];
+    clippath[1].type = gfx_lineTo;clippath[1].x = x2; clippath[1].y = y1; clippath[1].next = &clippath[2];
+    clippath[2].type = gfx_lineTo;clippath[2].x = x2; clippath[2].y = y2; clippath[2].next = &clippath[3];
+    clippath[3].type = gfx_lineTo;clippath[3].x = x1; clippath[3].y = y2; clippath[3].next = &clippath[4];
+    clippath[4].type = gfx_lineTo;clippath[4].x = x1; clippath[4].y = y1; clippath[4].next = 0;
+    output->startclip(output, clippath); outer_clip_box = 1;
+    output->fill(output, clippath, &white);
 }
 
 void SWFOutputDev::drawLink(Link *link, Catalog *catalog) 
@@ -957,7 +1436,7 @@ void SWFOutputDev::drawLink(Link *link, Catalog *catalog)
     msg("<debug> drawlink\n");
     double x1, y1, x2, y2, w;
     GfxRGB rgb;
-    swfcoord points[5];
+    gfxline_t points[5];
     int x, y;
 
 #ifdef XPDF_101
@@ -969,17 +1448,30 @@ void SWFOutputDev::drawLink(Link *link, Catalog *catalog)
     rgb.g = 0;
     rgb.b = 1;
     cvtUserToDev(x1, y1, &x, &y);
-    points[0].x = points[4].x = (int)x;
-    points[0].y = points[4].y = (int)y;
+    points[0].type = gfx_moveTo;
+    points[0].x = points[4].x = x + user_movex;
+    points[0].y = points[4].y = y + user_movey;
+    points[0].next = &points[1];
     cvtUserToDev(x2, y1, &x, &y);
-    points[1].x = (int)x;
-    points[1].y = (int)y;
+    points[1].type = gfx_lineTo;
+    points[1].x = x + user_movex;
+    points[1].y = y + user_movey;
+    points[1].next = &points[2];
     cvtUserToDev(x2, y2, &x, &y);
-    points[2].x = (int)x;
-    points[2].y = (int)y;
+    points[2].type = gfx_lineTo;
+    points[2].x = x + user_movex;
+    points[2].y = y + user_movey;
+    points[2].next = &points[3];
     cvtUserToDev(x1, y2, &x, &y);
-    points[3].x = (int)x;
-    points[3].y = (int)y;
+    points[3].type = gfx_lineTo;
+    points[3].x = x + user_movex;
+    points[3].y = y + user_movey;
+    points[3].next = &points[4];
+    cvtUserToDev(x1, y1, &x, &y);
+    points[4].type = gfx_lineTo;
+    points[4].x = x + user_movex;
+    points[4].y = y + user_movey;
+    points[4].next = 0;
 
     LinkAction*action=link->getAction();
     char buf[128];
@@ -1035,7 +1527,8 @@ void SWFOutputDev::drawLink(Link *link, Catalog *catalog)
                     }
                     else if(strstr(s, "last") || strstr(s, "end"))
                     {
-                        page = pagepos>0?pages[pagepos-1]:0;
+                       if(pages && pagepos>0)
+                           page = pages[pagepos-1];
                     }
                     else if(strstr(s, "first") || strstr(s, "top"))
                     {
@@ -1082,45 +1575,54 @@ void SWFOutputDev::drawLink(Link *link, Catalog *catalog)
         msg("<notice> File contains links");
         linkinfo = 1;
     }
+    
     if(page>0)
     {
         int t;
-        for(t=0;t<pagepos;t++)
-            if(pages[t]==page)
+       int lpage = -1;
+        for(t=1;t<=pagepos;t++) {
+            if(pages[t]==page) {
+               lpage = t;
                 break;
-        if(t!=pagepos)
-            swfoutput_linktopage(&output, t, points);
+           }
+       }
+        if(lpage>=0) {
+           char buf[80];
+           sprintf(buf, "page%d", t);
+            output->drawlink(output, points, buf);
+       } else {
+           msg("<warning> Invalid link to page %d", page);
+       }
     }
     else if(url)
     {
-        swfoutput_linktourl(&output, url, points);
-    }
-    else if(named)
-    {
-        swfoutput_namedlink(&output, named, points);
+        output->drawlink(output, points, url);
     }
+
     msg("<verbose> \"%s\" link to \"%s\" (%d)\n", type, FIXNULL(s), page);
     free(s);s=0;
 }
 
 void SWFOutputDev::saveState(GfxState *state) {
-  msg("<debug> saveState\n");
+  msg("<trace> saveState\n");
   updateAll(state);
-  if(clippos<64)
-    clippos ++;
-  else
+  if(statepos>=64) {
     msg("<error> Too many nested states in pdf.");
-  clipping[clippos] = 0;
+    return;
+  }
+  statepos ++;
+  states[statepos].clipping = 0; //? shouldn't this be the current value?
+  states[statepos].textRender = states[statepos-1].textRender;
 };
 
 void SWFOutputDev::restoreState(GfxState *state) {
-  msg("<debug> restoreState\n");
+  msg("<trace> restoreState\n");
   updateAll(state);
-  while(clipping[clippos]) {
-      swfoutput_endclip(&output);
-      clipping[clippos]--;
+  while(states[statepos].clipping) {
+      output->endclip(output);
+      states[statepos].clipping--;
   }
-  clippos--;
+  statepos--;
 }
 
 char* SWFOutputDev::searchFont(char*name) 
@@ -1161,7 +1663,7 @@ char* SWFOutputDev::searchFont(char*name)
 void SWFOutputDev::updateLineWidth(GfxState *state)
 {
     double width = state->getTransformedLineWidth();
-    swfoutput_setlinewidth(&output, width);
+    //swfoutput_setlinewidth(&output, width);
 }
 
 void SWFOutputDev::updateLineCap(GfxState *state)
@@ -1180,8 +1682,7 @@ void SWFOutputDev::updateFillColor(GfxState *state)
     double opaq = state->getFillOpacity();
     state->getFillRGB(&rgb);
 
-    swfoutput_setfillcolor(&output, (char)(rgb.r*255), (char)(rgb.g*255), 
-                                   (char)(rgb.b*255), (char)(opaq*255));
+    //swfoutput_setfillcolor(&output, (char)(rgb.r*255), (char)(rgb.g*255), (char)(rgb.b*255), (char)(opaq*255));
 }
 
 void SWFOutputDev::updateStrokeColor(GfxState *state) 
@@ -1189,9 +1690,7 @@ void SWFOutputDev::updateStrokeColor(GfxState *state)
     GfxRGB rgb;
     double opaq = state->getStrokeOpacity();
     state->getStrokeRGB(&rgb);
-
-    swfoutput_setstrokecolor(&output, (char)(rgb.r*255), (char)(rgb.g*255), 
-                                     (char)(rgb.b*255), (char)(opaq*255));
+    //swfoutput_setstrokecolor(&output, (char)(rgb.r*255), (char)(rgb.g*255), (char)(rgb.b*255), (char)(opaq*255));
 }
 
 void FoFiWrite(void *stream, char *data, int len)
@@ -1232,8 +1731,8 @@ char*SWFOutputDev::writeEmbeddedFontToFile(XRef*ref, GfxFont*font)
        msg("<notice> Collection: %s", c.getCString());
     }*/
 
-    if (font->getType() == fontType1C ||
-       font->getType() == fontCIDType0C) {
+    //if (font->getType() == fontType1C) {
+    if (0) { //font->getType() == fontType1C) {
       if (!(fontBuf = font->readEmbFontFile(xref, &fontLen))) {
        fclose(f);
        msg("<error> Couldn't read embedded font file");
@@ -1241,9 +1740,11 @@ char*SWFOutputDev::writeEmbeddedFontToFile(XRef*ref, GfxFont*font)
       }
 #ifdef XPDF_101
       Type1CFontFile *cvt = new Type1CFontFile(fontBuf, fontLen);
+      if(!cvt) return 0;
       cvt->convertToType1(f);
 #else
       FoFiType1C *cvt = FoFiType1C::make(fontBuf, fontLen);
+      if(!cvt) return 0;
       cvt->convertToType1(NULL, gTrue, FoFiWrite, f);
 #endif
       //cvt->convertToCIDType0("test", f);
@@ -1327,7 +1828,7 @@ char* searchForSuitableFont(GfxFont*gfxFont)
     if (!fcinitcalled) {
         msg("<debug> Initializing FontConfig...");
         fcinitcalled = true;
-       if(FcInit()) {
+       if(!FcInit()) {
             msg("<debug> FontConfig Initialization failed. Disabling.");
             config_use_fontconfig = 0;
             return 0;
@@ -1355,7 +1856,7 @@ char* searchForSuitableFont(GfxFont*gfxFont)
         // if we get an exact match
         if (strcmp((char *)v, name) == 0) {
            if (FcPatternGetString(match, "file", 0, &v) == FcResultMatch) {
-               filename = strdup((char*)v);
+               filename = strdup((char*)v); // mem leak
                char *nfn = strrchr(filename, '/');
                if(nfn) fontname = strdup(nfn+1);
                else    fontname = filename;
@@ -1396,7 +1897,7 @@ char* searchForSuitableFont(GfxFont*gfxFont)
             match = FcFontMatch (0, pattern, &result);
             
             if (FcPatternGetString(match, "file", 0, &v) == FcResultMatch) {
-               filename = strdup((char*)v);
+               filename = strdup((char*)v); // mem leak
                char *nfn = strrchr(filename, '/');
                if(nfn) fontname = strdup(nfn+1);
                else    fontname = filename;
@@ -1423,24 +1924,28 @@ char* searchForSuitableFont(GfxFont*gfxFont)
 char* SWFOutputDev::substituteFont(GfxFont*gfxFont, char* oldname)
 {
     char*fontname = 0, *filename = 0;
-    msg("<notice> subsituteFont(%s)", oldname);
+    msg("<notice> substituteFont(%s)", oldname);
 
     if(!(fontname = searchForSuitableFont(gfxFont))) {
        fontname = "Times-Roman";
     }
     filename = searchFont(fontname);
+    if(!filename) {
+       msg("<error> Couldn't find font %s- did you install the default fonts?", fontname);
+       return 0;
+    }
 
     if(substitutepos>=sizeof(substitutesource)/sizeof(char*)) {
        msg("<fatal> Too many fonts in file.");
        exit(1);
     }
     if(oldname) {
-       substitutesource[substitutepos] = oldname;
+       substitutesource[substitutepos] = strdup(oldname); //mem leak
        substitutetarget[substitutepos] = fontname;
        msg("<notice> substituting %s -> %s", FIXNULL(oldname), FIXNULL(fontname));
        substitutepos ++;
     }
-    return strdup(filename);
+    return strdup(filename); //mem leak
 }
 
 void unlinkfont(char* filename)
@@ -1478,6 +1983,47 @@ void SWFOutputDev::setXRef(PDFDoc*doc, XRef *xref)
     this->xref = xref;
 }
 
+int SWFOutputDev::setGfxFont(char*id, char*filename, double maxSize)
+{
+    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;
+           output->addfont(output, id, current_gfxfont);
+           return 1;
+       }
+       l = l->next;
+    }
+    if(!filename) return 0;
+
+    /* A font size of e.g. 9 means the font will be scaled down by
+       1024 and scaled up by 9. So to have a maximum error of 1/20px,
+       we have to divide 0.05 by (fontsize/1024)
+     */
+    double quality = (1024 * 0.05) / maxSize;
+    
+    font = gfxfont_load(filename, quality);
+    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;
+    }
+    output->addfont(output, id, current_gfxfont);
+    return 1;
+}
 
 void SWFOutputDev::updateFont(GfxState *state) 
 {
@@ -1487,6 +2033,11 @@ void SWFOutputDev::updateFont(GfxState *state)
        return;
     }  
     char * fontid = getFontID(gfxFont);
+    double maxSize = 1.0;
+
+    if(this->info) {
+       maxSize = this->info->getMaximumFontSize(fontid);
+    }
     
     int t;
     /* first, look if we substituted this font before-
@@ -1494,20 +2045,24 @@ void SWFOutputDev::updateFont(GfxState *state)
        too often */
     for(t=0;t<substitutepos;t++) {
        if(!strcmp(fontid, substitutesource[t])) {
-           fontid = substitutetarget[t];
+           free(fontid);fontid=0;
+           fontid = strdup(substitutetarget[t]);
            break;
        }
     }
 
-    /* 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, 0)) {
+       free(fontid);
+       return;
+    }
+/*    if(swfoutput_queryfont(&output, fontid))
        swfoutput_setfont(&output, fontid, 0);
        
        msg("<debug> updateFont(%s) [cached]", fontid);
        return;
-    }
+    }*/
 
     // look for Type 3 font
     if (gfxFont->getType() == fontType3) {
@@ -1515,6 +2070,7 @@ void SWFOutputDev::updateFont(GfxState *state)
            type3Warning = gTrue;
            showFontError(gfxFont, 2);
        }
+       free(fontid);
        return;
     }
 
@@ -1528,7 +2084,7 @@ void SWFOutputDev::updateFont(GfxState *state)
     if(embedded &&
        (gfxFont->getType() == fontType1 ||
        gfxFont->getType() == fontType1C ||
-       //gfxFont->getType() == fontCIDType0C ||
+       (gfxFont->getType() == fontCIDType0C && forceType0Fonts) ||
        gfxFont->getType() == fontTrueType ||
        gfxFont->getType() == fontCIDType2
        ))
@@ -1540,30 +2096,42 @@ void SWFOutputDev::updateFont(GfxState *state)
       char * fontname = getFontName(gfxFont);
       fileName = searchFont(fontname);
       if(!fileName) showFontError(gfxFont,0);
+      free(fontname);
     }
     if(!fileName) {
        char * fontname = getFontName(gfxFont);
        msg("<warning> Font %s %scould not be loaded.", fontname, embedded?"":"(not embedded) ");
-       msg("<warning> Try putting a TTF version of that font (named \"%s.ttf\") into /swftools/fonts", fontname);
+       
+       if(lastfontdir)
+           msg("<warning> Try putting a TTF version of that font (named \"%s.ttf\") into %s/swftools/fonts", fontname, lastfontdir);
+       else
+           msg("<warning> Try specifying one or more font directories");
+
        fileName = substituteFont(gfxFont, fontid);
-       if(fontid) { fontid = substitutetarget[substitutepos-1]; /*ugly hack*/};
+       if(fontid) { free(fontid);fontid = strdup(substitutetarget[substitutepos-1]); /*ugly hack*/};
        msg("<notice> Font is now %s (%s)", fontid, fileName);
     }
 
     if(!fileName) {
        msg("<error> Couldn't set font %s\n", fontid);
+       free(fontid);
        return;
     }
        
-    msg("<verbose> updateFont(%s) -> %s", fontid, fileName);
+    msg("<verbose> updateFont(%s) -> %s (max size: %f)", fontid, fileName, maxSize);
     dumpFontInfo("<verbose>", gfxFont);
 
-    swfoutput_setfont(&output, fontid, fileName);
+    //swfoutput_setfont(&output, fontid, fileName);
+    
+    if(!setGfxFont(fontid, 0, 0)) {
+       setGfxFont(fontid, fileName, maxSize);
+    }
    
     if(fileName && del)
        unlinkfont(fileName);
     if(fileName)
         free(fileName);
+    free(fontid);
 }
 
 #define SQR(x) ((x)*(x))
@@ -1616,9 +2184,76 @@ unsigned char* antialize(unsigned char*data, int width, int height, int newwidth
     return newdata;
 }
 
+#define IMAGE_TYPE_JPEG 0
+#define IMAGE_TYPE_LOSSLESS 1
+
+static void drawimage(gfxdevice_t*dev, RGBA* data, int sizex,int sizey, 
+        double x1,double y1,
+        double x2,double y2,
+        double x3,double y3,
+        double x4,double y4, int type)
+{
+    RGBA*newpic=0;
+    
+    double l1 = sqrt((x4-x1)*(x4-x1) + (y4-y1)*(y4-y1));
+    double l2 = sqrt((x2-x1)*(x2-x1) + (y2-y1)*(y2-y1));
+   
+    gfxline_t p1,p2,p3,p4,p5;
+    p1.type=gfx_moveTo;p1.x=x1; p1.y=y1;p1.next=&p2;
+    p2.type=gfx_lineTo;p2.x=x2; p2.y=y2;p2.next=&p3;
+    p3.type=gfx_lineTo;p3.x=x3; p3.y=y3;p3.next=&p4;
+    p4.type=gfx_lineTo;p4.x=x4; p4.y=y4;p4.next=&p5;
+    p5.type=gfx_lineTo;p5.x=x1; p5.y=y1;p5.next=0;
+
+    {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;
+     p5.x = (int)(p5.x*20)/20.0;
+     p5.y = (int)(p5.y*20)/20.0;
+    }
+    
+    float m00,m10,tx;
+    float m01,m11,ty;
+    
+    gfxmatrix_t m;
+    m.m00 = (p4.x-p1.x)/sizex; m.m10 = (p2.x-p1.x)/sizey;
+    m.m01 = (p4.y-p1.y)/sizex; m.m11 = (p2.y-p1.y)/sizey;
+    m.tx = p1.x - 0.5;
+    m.ty = p1.y - 0.5;
+
+    gfximage_t img;
+    img.data = (gfxcolor_t*)data;
+    img.width = sizex;
+    img.height = sizey;
+  
+    if(type == IMAGE_TYPE_JPEG)
+       /* TODO: pass image_dpi to device instead */
+       dev->setparameter(dev, "next_bitmap_is_jpeg", "1");
+
+    dev->fillbitmap(dev, &p1, &img, &m, 0);
+}
+
+void drawimagejpeg(gfxdevice_t*dev, RGBA*mem, int sizex,int sizey, 
+        double x1,double y1, double x2,double y2, double x3,double y3, double x4,double y4)
+{
+    drawimage(dev,mem,sizex,sizey,x1,y1,x2,y2,x3,y3,x4,y4, IMAGE_TYPE_JPEG);
+}
+
+void drawimagelossless(gfxdevice_t*dev, RGBA*mem, int sizex,int sizey, 
+        double x1,double y1, double x2,double y2, double x3,double y3, double x4,double y4)
+{
+    drawimage(dev,mem,sizex,sizey,x1,y1,x2,y2,x3,y3,x4,y4, IMAGE_TYPE_LOSSLESS);
+}
+
+
 void SWFOutputDev::drawGeneralImage(GfxState *state, Object *ref, Stream *str,
                                   int width, int height, GfxImageColorMap*colorMap, GBool invert,
-                                  GBool inlineImg, int mask)
+                                  GBool inlineImg, int mask, int*maskColors)
 {
   FILE *fi;
   int c;
@@ -1650,10 +2285,10 @@ void SWFOutputDev::drawGeneralImage(GfxState *state, Object *ref, Stream *str,
       return;
   }
   
-  state->transform(0, 1, &x1, &y1);
-  state->transform(0, 0, &x2, &y2);
-  state->transform(1, 0, &x3, &y3);
-  state->transform(1, 1, &x4, &y4);
+  state->transform(0, 1, &x1, &y1); x1 += user_movex; y1+= user_movey;
+  state->transform(0, 0, &x2, &y2); x2 += user_movex; y2+= user_movey;
+  state->transform(1, 0, &x3, &y3); x3 += user_movex; y3+= user_movey;
+  state->transform(1, 1, &x4, &y4); x4 += user_movex; y4+= user_movey;
 
   if(!pbminfo && !(str->getKind()==strDCT)) {
       if(!type3active) {
@@ -1669,22 +2304,21 @@ void SWFOutputDev::drawGeneralImage(GfxState *state, Object *ref, Stream *str,
   }
 
   if(mask) {
-      int yes=0,i,j;
+      int i,j;
       unsigned char buf[8];
-      int xid = 0;
-      int yid = 0;
       int x,y;
       unsigned char*pic = new unsigned char[width*height];
       RGBA pal[256];
       GfxRGB rgb;
       state->getFillRGB(&rgb);
+
       memset(pal,255,sizeof(pal));
-      pal[0].r = (int)(rgb.r*255); pal[0].g = (int)(rgb.g*255); 
-      pal[0].b = (int)(rgb.b*255); pal[0].a = 255;
-      pal[1].r = 0; pal[1].g = 0; pal[1].b = 0; pal[1].a = 0;
+      pal[0].r = (int)(rgb.r*255); pal[1].r = 0;
+      pal[0].g = (int)(rgb.g*255); pal[1].g = 0;
+      pal[0].b = (int)(rgb.b*255); pal[1].b = 0;
+      pal[0].a = 255;              pal[1].a = 0;
+
       int numpalette = 2;
-      xid += pal[1].r*3 + pal[1].g*11 + pal[1].b*17;
-      yid += pal[1].r*7 + pal[1].g*5 + pal[1].b*23;
       int realwidth = (int)sqrt(SQR(x2-x3) + SQR(y2-y3));
       int realheight = (int)sqrt(SQR(x1-x2) + SQR(y1-y2));
       for (y = 0; y < height; ++y)
@@ -1694,173 +2328,120 @@ void SWFOutputDev::drawGeneralImage(GfxState *state, Object *ref, Stream *str,
            if(invert) 
                buf[0]=1-buf[0];
            pic[width*y+x] = buf[0];
-           xid+=x*buf[0]+1;
-           yid+=y*buf[0]*3+1;
       }
       
       /* the size of the drawn image is added to the identifier
         as the same image may require different bitmaps if displayed
         at different sizes (due to antialiasing): */
-      if(type3active) {
-         xid += realwidth;
-         yid += realheight;
-      }
       int t,found = -1;
-      for(t=0;t<picpos;t++)
-      {
-         if(pic_xids[t] == xid &&
-            pic_yids[t] == yid) {
-             /* if the image was antialiased, the size has changed: */
-             width = pic_width[t];
-             height = pic_height[t];
-             found = t;break;
+      if(type3active) {
+         unsigned char*pic2 = 0;
+         numpalette = 16;
+         
+         pic2 = antialize(pic,width,height,realwidth,realheight,numpalette);
+
+         if(!pic2) {
+           delete pic;
+           delete imgStr;
+           return;
          }
-      }
-      if(found<0) {
-         if(type3active) {
-             numpalette = 16;
-             unsigned char*pic2 = 0;
-             
-             pic2 = antialize(pic,width,height,realwidth,realheight, numpalette);
-
-             if(pic2) {
-                 width = realwidth;
-                 height = realheight;
-                 free(pic);
-                 pic = pic2;
-                 /* make a black/white palette */
-                 int t;
-                 GfxRGB rgb2;
-                 rgb2.r = 1 - rgb.r;
-                 rgb2.g = 1 - rgb.g;
-                 rgb2.b = 1 - rgb.b;
-
-                 float r = 255/(numpalette-1);
-                 for(t=0;t<numpalette;t++) {
-                     /*pal[t].r = (U8)(t*r*rgb.r+(numpalette-1-t)*r*rgb2.r);
-                     pal[t].g = (U8)(t*r*rgb.g+(numpalette-1-t)*r*rgb2.g);
-                     pal[t].b = (U8)(t*r*rgb.b+(numpalette-1-t)*r*rgb2.b);
-                     pal[t].a = 255; */
-                     pal[t].r = (U8)(255*rgb.r);
-                     pal[t].g = (U8)(255*rgb.g);
-                     pal[t].b = (U8)(255*rgb.b);
-                     pal[t].a = (U8)(t*r);
-                 }
-             }
+
+         width = realwidth;
+         height = realheight;
+         free(pic);
+         pic = pic2;
+         
+         /* make a black/white palette */
+         GfxRGB rgb2;
+         rgb2.r = 1 - rgb.r;
+         rgb2.g = 1 - rgb.g;
+         rgb2.b = 1 - rgb.b;
+         float r = 255/(numpalette-1);
+         int t;
+         for(t=0;t<numpalette;t++) {
+             pal[t].r = (U8)(255*rgb.r);
+             pal[t].g = (U8)(255*rgb.g);
+             pal[t].b = (U8)(255*rgb.b);
+             pal[t].a = (U8)(t*r);
          }
-         pic_ids[picpos] = swfoutput_drawimagelosslessN(&output, pic, pal, width, height, 
-                 x1,y1,x2,y2,x3,y3,x4,y4, numpalette);
-         pic_xids[picpos] = xid;
-         pic_yids[picpos] = yid;
-         pic_width[picpos] = width;
-         pic_height[picpos] = height;
-         if(picpos<1024)
-             picpos++;
-      } else {
-         swfoutput_drawimageagain(&output, pic_ids[found], width, height,
-                 x1,y1,x2,y2,x3,y3,x4,y4);
       }
+
+      RGBA*pic2 = new RGBA[width*height];
+      for (y = 0; y < height; ++y) {
+       for (x = 0; x < width; ++x) {
+         pic2[width*y+x] = pal[pic[y*width+x]];
+       }
+      }
+      drawimagelossless(output, pic2, width, height, x1,y1,x2,y2,x3,y3,x4,y4);
+      free(pic2);
       free(pic);
       delete imgStr;
       return;
   } 
 
   int x,y;
-  
-  if(colorMap->getNumPixelComps()!=1 || str->getKind()==strDCT)
-  {
+
+  if(colorMap->getNumPixelComps()!=1 || str->getKind()==strDCT) {
       RGBA*pic=new RGBA[width*height];
-      int xid = 0;
-      int yid = 0;
       for (y = 0; y < height; ++y) {
        for (x = 0; x < width; ++x) {
-         int r,g,b,a;
          imgStr->getPixel(pixBuf);
          colorMap->getRGB(pixBuf, &rgb);
-         pic[width*y+x].r = r = (U8)(rgb.r * 255 + 0.5);
-         pic[width*y+x].g = g = (U8)(rgb.g * 255 + 0.5);
-         pic[width*y+x].b = b = (U8)(rgb.b * 255 + 0.5);
-         pic[width*y+x].a = a = 255;//(U8)(rgb.a * 255 + 0.5);
-         xid += x*r+x*b*3+x*g*7+x*a*11;
-         yid += y*r*3+y*b*17+y*g*19+y*a*11;
+         pic[width*y+x].r = (U8)(rgb.r * 255 + 0.5);
+         pic[width*y+x].g = (U8)(rgb.g * 255 + 0.5);
+         pic[width*y+x].b = (U8)(rgb.b * 255 + 0.5);
+         pic[width*y+x].a = 255;//(U8)(rgb.a * 255 + 0.5);
        }
       }
-      int t,found = -1;
-      for(t=0;t<picpos;t++)
-      {
-         if(pic_xids[t] == xid &&
-            pic_yids[t] == yid) {
-             found = t;break;
-         }
-      }
-      if(found<0) {
-         if(str->getKind()==strDCT)
-             pic_ids[picpos] = swfoutput_drawimagejpeg(&output, pic, width, height, 
-                     x1,y1,x2,y2,x3,y3,x4,y4);
-         else
-             pic_ids[picpos] = swfoutput_drawimagelossless(&output, pic, width, height, 
-                     x1,y1,x2,y2,x3,y3,x4,y4);
-         pic_xids[picpos] = xid;
-         pic_yids[picpos] = yid;
-         pic_width[picpos] = width;
-         pic_height[picpos] = height;
-         if(picpos<1024)
-             picpos++;
-      } else {
-         swfoutput_drawimageagain(&output, pic_ids[found], width, height,
-                 x1,y1,x2,y2,x3,y3,x4,y4);
-      }
+      if(str->getKind()==strDCT)
+         drawimagejpeg(output, pic, width, height, x1,y1,x2,y2,x3,y3,x4,y4);
+      else
+         drawimagelossless(output, pic, width, height, x1,y1,x2,y2,x3,y3,x4,y4);
       delete pic;
       delete imgStr;
       return;
-  }
-  else
-  {
-      U8*pic = new U8[width*height];
+  } else {
+      RGBA*pic=new RGBA[width*height];
       RGBA pal[256];
       int t;
-      int xid=0,yid=0;
-      for(t=0;t<256;t++)
-      {
-         int r,g,b,a;
+      for(t=0;t<256;t++) {
          pixBuf[0] = t;
          colorMap->getRGB(pixBuf, &rgb);
-         pal[t].r = r = (U8)(rgb.r * 255 + 0.5);
-         pal[t].g = g = (U8)(rgb.g * 255 + 0.5);
-         pal[t].b = b = (U8)(rgb.b * 255 + 0.5);
-         pal[t].a = a = 255;//(U8)(rgb.b * 255 + 0.5);
-         xid += t*r+t*b*3+t*g*7+t*a*11;
-         xid += (~t)*r+t*b*3+t*g*7+t*a*11;
+         /*if(maskColors && *maskColors==t) {
+             msg("<notice> Color %d is transparent", t);
+             if (imgData->maskColors) {
+               *alpha = 0;
+               for (i = 0; i < imgData->colorMap->getNumPixelComps(); ++i) {
+                 if (pix[i] < imgData->maskColors[2*i] ||
+                     pix[i] > imgData->maskColors[2*i+1]) {
+                   *alpha = 1;
+                   break;
+                 }
+               }
+             } else {
+               *alpha = 1;
+             }
+             if(!*alpha) {
+                   pal[t].r = 0;
+                   pal[t].g = 0;
+                   pal[t].b = 0;
+                   pal[t].a = 0;
+             }
+         } else*/ {
+             pal[t].r = (U8)(rgb.r * 255 + 0.5);
+             pal[t].g = (U8)(rgb.g * 255 + 0.5);
+             pal[t].b = (U8)(rgb.b * 255 + 0.5);
+             pal[t].a = 255;//(U8)(rgb.b * 255 + 0.5);
+         }
       }
       for (y = 0; y < height; ++y) {
        for (x = 0; x < width; ++x) {
          imgStr->getPixel(pixBuf);
-         pic[width*y+x] = pixBuf[0];
-         xid += x*pixBuf[0]*7;
-         yid += y*pixBuf[0]*3;
+         pic[width*y+x] = pal[pixBuf[0]];
        }
       }
-      int found = -1;
-      for(t=0;t<picpos;t++)
-      {
-         if(pic_xids[t] == xid &&
-            pic_yids[t] == yid) {
-             found = t;break;
-         }
-      }
-      if(found<0) {
-         pic_ids[picpos] = swfoutput_drawimagelosslessN(&output, pic, pal, width, height, 
-                 x1,y1,x2,y2,x3,y3,x4,y4,256);
-         pic_xids[picpos] = xid;
-         pic_yids[picpos] = yid;
-         pic_width[picpos] = width;
-         pic_height[picpos] = height;
-         if(picpos<1024)
-             picpos++;
-      } else {
-         swfoutput_drawimageagain(&output, pic_ids[found], width, height,
-                 x1,y1,x2,y2,x3,y3,x4,y4);
-      }
+      drawimagelossless(output, pic, width, height, x1,y1,x2,y2,x3,y3,x4,y4);
+
       delete pic;
       delete imgStr;
       return;
@@ -1871,14 +2452,19 @@ void SWFOutputDev::drawImageMask(GfxState *state, Object *ref, Stream *str,
                                   int width, int height, GBool invert,
                                   GBool inlineImg) 
 {
+  if(states[statepos].textRender & 4) //clipped
+      return;
   msg("<verbose> drawImageMask %dx%d, invert=%d inline=%d", width, height, invert, inlineImg);
-  drawGeneralImage(state,ref,str,width,height,0,invert,inlineImg,1);
+  drawGeneralImage(state,ref,str,width,height,0,invert,inlineImg,1, 0);
 }
 
 void SWFOutputDev::drawImage(GfxState *state, Object *ref, Stream *str,
                         int width, int height, GfxImageColorMap *colorMap,
                         int *maskColors, GBool inlineImg)
 {
+  if(states[statepos].textRender & 4) //clipped
+      return;
+
   msg("<verbose> drawImage %dx%d, %s %s, inline=%d", width, height, 
          colorMap?"colorMap":"no colorMap", 
          maskColors?"maskColors":"no maskColors",
@@ -1886,10 +2472,10 @@ void SWFOutputDev::drawImage(GfxState *state, Object *ref, Stream *str,
   if(colorMap)
       msg("<verbose> colorMap pixcomps:%d bits:%d mode:%d\n", colorMap->getNumPixelComps(),
              colorMap->getBits(),colorMap->getColorSpace()->getMode());
-  drawGeneralImage(state,ref,str,width,height,colorMap,0,inlineImg,0);
+  drawGeneralImage(state,ref,str,width,height,colorMap,0,inlineImg,0,maskColors);
 }
 
-SWFOutputDev*output = 0; 
+//SWFOutputDev*output = 0; 
 
 static void printInfoString(Dict *infoDict, char *key, char *fmt) {
   Object obj;
@@ -1933,13 +2519,48 @@ static void printInfoDate(Dict *infoDict, char *key, char *fmt) {
   obj.free();
 }
 
+int jpeg_dpi = 0;
+int ppm_dpi = 0;
+
+void storeDeviceParameter(char*name, char*value)
+{
+    parameter_t*p = new parameter_t();
+    p->name = strdup(name);
+    p->value = strdup(value);
+    p->next = 0;
+    if(device_config_next) {
+       device_config_next->next = p;
+       device_config_next = p;
+    } else {
+       device_config = p;
+       device_config_next = p;
+    }
+}
+
 void pdfswf_setparameter(char*name, char*value)
 {
     msg("<verbose> setting parameter %s to \"%s\"", name, value);
     if(!strcmp(name, "caplinewidth")) {
        caplinewidth = atof(value);
     } else if(!strcmp(name, "zoom")) {
-       zoom = atoi(value);
+       char buf[80];
+       zoom = atof(value);
+       sprintf(buf, "%f", (double)jpeg_dpi/(double)zoom);
+       storeDeviceParameter("jpegsubpixels", buf);
+       sprintf(buf, "%f", (double)ppm_dpi/(double)zoom);
+       storeDeviceParameter("ppmsubpixels", buf);
+    } else if(!strcmp(name, "jpegdpi")) {
+       char buf[80];
+       jpeg_dpi = atoi(value);
+       sprintf(buf, "%f", (double)jpeg_dpi/(double)zoom);
+       storeDeviceParameter("jpegsubpixels", buf);
+    } else if(!strcmp(name, "ppmdpi")) {
+       char buf[80];
+       ppm_dpi = atoi(value);
+       sprintf(buf, "%f", (double)ppm_dpi/(double)zoom);
+       storeDeviceParameter("ppmsubpixels", buf);
+    } else if(!strcmp(name, "forceType0Fonts")) {
+       forceType0Fonts = atoi(value);
     } else if(!strcmp(name, "fontdir")) {
         pdfswf_addfontdir(value);
     } else if(!strcmp(name, "languagedir")) {
@@ -1947,7 +2568,7 @@ void pdfswf_setparameter(char*name, char*value)
     } else if(!strcmp(name, "fontconfig")) {
         config_use_fontconfig = atoi(value);
     } else {
-       swfoutput_setparameter(name, value);
+       storeDeviceParameter(name,value);
     }
 }
 void pdfswf_addfont(char*filename)
@@ -1998,6 +2619,7 @@ void pdfswf_addfontdir(char*dirname)
 {
 #ifdef HAVE_DIRENT_H
     msg("<notice> Adding %s to font directories", dirname);
+    lastfontdir = strdup(dirname);
     DIR*dir = opendir(dirname);
     if(!dir) {
        msg("<warning> Couldn't open directory %s\n", dirname);
@@ -2042,6 +2664,7 @@ typedef struct _pdf_doc_internal
 {
     int protect;
     PDFDoc*doc;
+    InfoOutputDev*info;
 } pdf_doc_internal_t;
 typedef struct _pdf_page_internal
 {
@@ -2118,24 +2741,19 @@ pdf_doc_t* pdf_init(char*filename, char*userPassword)
           if(!i->doc->okToChange() || !i->doc->okToAddNotes())
               i->protect = 1;
     }
-   
-    return pdf_doc;
-}
 
-void pdfswf_preparepage(int page)
-{
-    /*FIXME*/
-    if(!pages) {
-       pages = (int*)malloc(1024*sizeof(int));
-       pagebuflen = 1024;
-    } else {
-       if(pagepos == pagebuflen)
-       {
-           pagebuflen+=1024;
-           pages = (int*)realloc(pages, pagebuflen);
-       }
+    InfoOutputDev*io = new InfoOutputDev();
+    int t;
+    for(t=1;t<=pdf_doc->num_pages;t++) {
+#ifdef XPDF_101
+       i->doc->displayPage((OutputDev*)io, t, /*zoom*/zoom, /*rotate*/0, /*doLinks*/(int)1);
+#else
+       i->doc->displayPage((OutputDev*)io, t, zoom, zoom, /*rotate*/0, true, /*doLinks*/(int)1);
+#endif
     }
-    pages[pagepos++] = page;
+    i->info = io;
+
+    return pdf_doc;
 }
 
 class MemCheck
@@ -2152,10 +2770,11 @@ void pdf_destroy(pdf_doc_t*pdf_doc)
 {
     pdf_doc_internal_t*i= (pdf_doc_internal_t*)pdf_doc->internal;
 
-    msg("<debug> pdfswf.cc: pdfswf_close()");
     delete i->doc; i->doc=0;
     
-    free(pages); pages = 0; //FIXME
+    if(i->info) {
+       delete i->info;i->info=0;
+    }
 
     free(pdf_doc->internal);pdf_doc->internal=0;
     free(pdf_doc);pdf_doc=0;
@@ -2197,17 +2816,60 @@ swf_output_t* swf_output_init()
     return swf_output;
 }
 
-void swf_output_setparameter(swf_output_t*swf_output, char*name, char*value)
+void swf_output_setparameter(swf_output_t*swf, char*name, char*value)
 {
-    /* FIXME */
     pdfswf_setparameter(name, value);
 }
 
+void swf_output_startframe(swf_output_t*swf, int width, int height)
+{
+    swf_output_internal_t*i= (swf_output_internal_t*)swf->internal;
+    i->outputDev->startFrame(width, height);
+}
+
+void swf_output_endframe(swf_output_t*swf)
+{
+    swf_output_internal_t*i= (swf_output_internal_t*)swf->internal;
+    i->outputDev->endframe();
+}
+
+void swf_output_preparepage(swf_output_t*swf, int pdfpage, int outputpage)
+{
+    swf_output_internal_t*i= (swf_output_internal_t*)swf->internal;
+    SWFOutputDev*o = i->outputDev;
+
+    if(pdfpage < 0)
+       return;
+
+    if(!o->pages) {
+       o->pagebuflen = 1024;
+       o->pages = (int*)malloc(o->pagebuflen*sizeof(int));
+       memset(o->pages, -1, o->pagebuflen*sizeof(int));
+    } else {
+       while(pdfpage >= o->pagebuflen)
+       {
+           int oldlen = o->pagebuflen;
+           o->pagebuflen+=1024;
+           o->pages = (int*)realloc(o->pages, o->pagebuflen*sizeof(int));
+           memset(&o->pages[oldlen], -1, (o->pagebuflen-oldlen)*sizeof(int));
+       }
+    }
+    o->pages[pdfpage] = outputpage;
+    if(pdfpage>o->pagepos)
+       o->pagepos = pdfpage;
+}
+
 int swf_output_save(swf_output_t*swf, char*filename)
 {
     swf_output_internal_t*i= (swf_output_internal_t*)swf->internal;
     int ret = i->outputDev->save(filename);
-    i->outputDev->getDimensions(&swf->x1, &swf->y1, &swf->x2, &swf->y2);
+    return ret;
+}
+
+void* swf_output_get(swf_output_t*swf,char*name)
+{
+    swf_output_internal_t*i= (swf_output_internal_t*)swf->internal;
+    void* ret = i->outputDev->get(name);
     return ret;
 }
 
@@ -2224,16 +2886,22 @@ void pdf_page_render2(pdf_page_t*page, swf_output_t*swf)
     pdf_doc_internal_t*pi = (pdf_doc_internal_t*)page->parent->internal;
     swf_output_internal_t*si = (swf_output_internal_t*)swf->internal;
 
+    if(!pi) {
+       msg("<fatal> pdf_page_render: Parent PDF this page belongs to doesn't exist yet/anymore");
+       return;
+    }
+
     if(pi->protect) {
-        swfoutput_setparameter("protect", "1");
+       gfxdevice_t*dev = si->outputDev->output;
+        dev->setparameter(dev, "protect", "1");
     }
+    si->outputDev->setInfo(pi->info);
     si->outputDev->setXRef(pi->doc, pi->doc->getXRef());
 #ifdef XPDF_101
     pi->doc->displayPage((OutputDev*)si->outputDev, page->nr, /*zoom*/zoom, /*rotate*/0, /*doLinks*/(int)1);
 #else
     pi->doc->displayPage((OutputDev*)si->outputDev, page->nr, zoom, zoom, /*rotate*/0, true, /*doLinks*/(int)1);
 #endif
-    si->outputDev->getDimensions(&swf->x1, &swf->y1, &swf->x2, &swf->y2);
 }
 
 void pdf_page_rendersection(pdf_page_t*page, swf_output_t*output, int x, int y, int x1, int y1, int x2, int y2)
@@ -2290,4 +2958,5 @@ pdf_page_info_t* pdf_page_getinfo(pdf_page_t*page)
 void pdf_page_info_destroy(pdf_page_info_t*info)
 {
     free(info);
+
 }