fixed unicode bug
[swftools.git] / pdf2swf / SWFOutputDev.cc
index e246867..b0bc202 100644 (file)
 #include "Page.h"
 #include "PDFDoc.h"
 #include "Error.h"
+#include "Link.h"
 #include "OutputDev.h"
 #include "GfxState.h"
 #include "GfxFont.h"
 #include "CharCodeToUnicode.h"
 #include "NameToUnicodeTable.h"
 #include "GlobalParams.h"
-//#define XPDF_101
-#ifdef XPDF_101
-#include "FontFile.h"
-#else
 #include "FoFiType1C.h"
 #include "FoFiTrueType.h"
-#endif
+#include "GHash.h"
 #include "SWFOutputDev.h"
 
 //swftools header files
-#include "swfoutput.h"
+#include "../lib/devices/swf.h"
 #include "../lib/log.h"
 #include "../lib/gfxdevice.h"
 #include "../lib/gfxtools.h"
@@ -92,12 +89,6 @@ 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 double zoom = 72; /* xpdf: 86 */
@@ -106,6 +97,8 @@ 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;
@@ -143,25 +136,29 @@ typedef struct _fontlist
     _fontlist*next;
 } fontlist_t;
 
+class InfoOutputDev;
+
 class SWFOutputDev:  public OutputDev {
-  int outputstarted;
 public:
   gfxdevice_t* output;
 
   // Constructor.
-  SWFOutputDev();
+  SWFOutputDev(gfxdevice_t*output);
 
   // Destructor.
   virtual ~SWFOutputDev() ;
 
   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);
-  void  pagefeed();
-  void* getSWF();
+  // Start a page.
+  void startFrame(int width, int height);
 
-  void getDimensions(int*x1,int*y1,int*x2,int*y2);
+  virtual void startPage(int pageNum, GfxState *state, double x1, double y1, double x2, double y2) ;
+
+  void endframe();
 
   //----- get info about output device
 
@@ -181,9 +178,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) ;
 
@@ -226,7 +220,7 @@ public:
   virtual void drawChar(GfxState *state, double x, double y,
                        double dx, double dy,
                        double originX, double originY,
-                       CharCode code, Unicode *u, int uLen);
+                       CharCode code, int nBytes, Unicode *u, int uLen);
 
   //----- image drawing
   virtual void drawImageMask(GfxState *state, Object *ref, Stream *str,
@@ -235,6 +229,17 @@ public:
   virtual void drawImage(GfxState *state, Object *ref, Stream *str,
                         int width, int height, GfxImageColorMap *colorMap,
                         int *maskColors, GBool inlineImg);
+  virtual void drawMaskedImage(GfxState *state, Object *ref, Stream *str,
+                              int width, int height,
+                              GfxImageColorMap *colorMap,
+                              Stream *maskStr, int maskWidth, int maskHeight,
+                              GBool maskInvert);
+  virtual void drawSoftMaskedImage(GfxState *state, Object *ref, Stream *str,
+                                  int width, int height,
+                                  GfxImageColorMap *colorMap,
+                                  Stream *maskStr,
+                                  int maskWidth, int maskHeight,
+                                  GfxImageColorMap *maskColorMap);
   
   virtual GBool beginType3Char(GfxState *state, double x, double y, double dx, double dy, CharCode code, Unicode *u, int uLen);
   virtual void endType3Char(GfxState *state);
@@ -242,19 +247,21 @@ public:
   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);
 
+  void finish();
+
   private:
   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);
+                                  GBool inlineImg, int mask, int *maskColors,
+                                  Stream *maskStr, int maskWidth, int maskHeight, GBool maskInvert, GfxImageColorMap*maskColorMap);
+  int SWFOutputDev::setGfxFont(char*id, char*name, 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();
-
-  SWF*swf; //filled upon completion
+  char outer_clip_box; //whether the page clip box is still on
 
+  InfoOutputDev*info;
   SWFOutputState states[64];
   int statepos;
 
@@ -267,6 +274,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?
@@ -293,12 +301,71 @@ public:
   gfxmatrix_t current_font_matrix;
 
   fontlist_t* fontlist;
+
+  int*pages;
+  int pagebuflen;
+  int pagepos;
+
+  friend void dev_output_preparepage(dev_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;
@@ -310,9 +377,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;}
@@ -334,14 +403,54 @@ 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++;
+      char*id = getFontID(font);
+      
+      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, int nBytes, 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,
@@ -357,30 +466,39 @@ class InfoOutputDev:  public OutputDev
   }
 };
 
-SWFOutputDev::SWFOutputDev()
-{
-    jpeginfo = 0;
-    ttfinfo = 0;
-    linkinfo = 0;
-    pbminfo = 0;
-    type3active = 0;
-    statepos = 0;
-    outputstarted = 0;
-    xref = 0;
-    substitutepos = 0;
-    type3Warning = 0;
-    user_movex = 0;
-    user_movey = 0;
-    user_clipx1 = 0;
-    user_clipy1 = 0;
-    user_clipx2 = 0;
-    user_clipy2 = 0;
-    current_text_stroke = 0;
-    current_text_clip = 0;
-    fontlist = 0;
-    swf = 0;
-    output = (gfxdevice_t*)malloc(sizeof(gfxdevice_t));
-    memset(output, 0, sizeof(output));
+SWFOutputDev::SWFOutputDev(gfxdevice_t*output)
+{
+    this->output = output;
+    this->jpeginfo = 0;
+    this->textmodeinfo = 0;
+    this->ttfinfo = 0;
+    this->linkinfo = 0;
+    this->pbminfo = 0;
+    this->type3active = 0;
+    this->statepos = 0;
+    this->xref = 0;
+    this->substitutepos = 0;
+    this->type3Warning = 0;
+    this->user_movex = 0;
+    this->user_movey = 0;
+    this->user_clipx1 = 0;
+    this->user_clipy1 = 0;
+    this->user_clipx2 = 0;
+    this->user_clipy2 = 0;
+    this->current_text_stroke = 0;
+    this->current_text_clip = 0;
+    this->fontlist = 0;
+    this->outer_clip_box = 0;
+    this->pages = 0;
+    this->pagebuflen = 0;
+    this->pagepos = 0;
+
+    /* 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)
@@ -399,27 +517,34 @@ 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)
+
+static char*getFontID(GfxFont*font)
 {
-    return gfxdevice_swf_getdimensions(output, x1,y1,x2,y2);
+    Ref*ref = font->getID();
+    GString*gstr = font->getName();
+    char* fname = gstr==0?0:gstr->getCString();
+    char buf[128];
+    if(fname==0) {
+       sprintf(buf, "font-%d-%d", ref->num, ref->gen);
+    } else {
+       sprintf(buf, "%s-%d-%d", fname, ref->num, ref->gen);
+    }
+    return strdup(buf);
 }
 
-static char*getFontID(GfxFont*font)
+static char*getFontName(GfxFont*font)
 {
+    char*fontid;
     GString*gstr = font->getName();
-    char* fontname = gstr==0?0:gstr->getCString();
-    if(fontname==0) {
+    char* fname = gstr==0?0:gstr->getCString();
+    if(fname==0) {
        char buf[32];
        Ref*r=font->getID();
        sprintf(buf, "UFONT%d", r->num);
-       return strdup(buf);
+       fontid = strdup(buf);
     }
-    return strdup(fontname);
-}
+    fontid = strdup(fname);
 
-static char*getFontName(GfxFont*font)
-{
-    char*fontid = getFontID(font);
     char*fontname= 0;
     char* plus = strchr(fontid, '+');
     if(plus && plus < &fontid[strlen(fontid)-1]) {
@@ -648,7 +773,7 @@ void dump_outline(gfxline_t*line)
     }
 }
 
-gfxline_t* gfxPath_to_gfxline(GfxState*state, GfxPath*path, int closed)
+gfxline_t* gfxPath_to_gfxline(GfxState*state, GfxPath*path, int closed, int user_movex, int user_movey)
 {
     int num = path->getNumSubpaths();
     int s,t;
@@ -669,7 +794,11 @@ gfxline_t* gfxPath_to_gfxline(GfxState*state, GfxPath*path, int closed)
 
        for(s=0;s<subnum;s++) {
           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);
@@ -693,7 +822,7 @@ gfxline_t* gfxPath_to_gfxline(GfxState*state, GfxPath*path, int closed)
                if(cpos==0) {
                    draw.lineTo(&draw, x,y);
                } else {
-                   gfxdraw_cubicTo(&draw, bx,by, cx,cy, x,y);
+                   gfxdraw_cubicTo(&draw, bx,by, cx,cy, x,y, 0.05);
                }
                needsfix = 1;
                cpos = 0;
@@ -715,7 +844,7 @@ 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);
+    gfxline_t*line= gfxPath_to_gfxline(state, path, 0, user_movex, user_movey);
     strokeGfxline(state, line);
     gfxline_free(line);
 }
@@ -734,9 +863,9 @@ void SWFOutputDev::strokeGfxline(GfxState *state, gfxline_t*line)
     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.r = colToByte(rgb.r);
+    col.g = colToByte(rgb.g);
+    col.b = colToByte(rgb.b);
     col.a = (unsigned char)(opaq*255);
    
     gfx_capType capType = gfx_capRound;
@@ -774,19 +903,17 @@ void SWFOutputDev::strokeGfxline(GfxState *state, gfxline_t*line)
 
        line2 = gfxtool_dash_line(line, dash, dashphase);
        line = line2;
+       free(dash);
        msg("<trace> After dashing:");
     }
     
     if(getLogLevel() >= LOGLEVEL_TRACE)  {
-       double gray;
-       state->getStrokeGray(&gray);
-        msg("<trace> stroke width=%f join=%s cap=%s dashes=%d color=%02x%02x%02x%02x gray=%f\n",
+        msg("<trace> stroke width=%f join=%s cap=%s dashes=%d color=%02x%02x%02x%02x\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
+               col.r,col.g,col.b,col.a
                );
         dump_outline(line);
     }
@@ -798,15 +925,19 @@ void SWFOutputDev::strokeGfxline(GfxState *state, gfxline_t*line)
        gfxline_free(line2);
 }
 
+void convertRGB()
+{
+}
+
 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.r = colToByte(rgb.r);
+    col.g = colToByte(rgb.g);
+    col.b = colToByte(rgb.b);
     col.a = (unsigned char)(opaq*255);
     return col;
 }
@@ -824,7 +955,7 @@ void SWFOutputDev::fillGfxLine(GfxState *state, gfxline_t*line)
 void SWFOutputDev::fill(GfxState *state) 
 {
     GfxPath * path = state->getPath();
-    gfxline_t*line= gfxPath_to_gfxline(state, path, 1);
+    gfxline_t*line= gfxPath_to_gfxline(state, path, 1, user_movex, user_movey);
     fillGfxLine(state, line);
     gfxline_free(line);
 }
@@ -833,7 +964,7 @@ void SWFOutputDev::eoFill(GfxState *state)
     GfxPath * path = state->getPath();
     gfxcolor_t col = getFillColor(state);
 
-    gfxline_t*line= gfxPath_to_gfxline(state, path, 1);
+    gfxline_t*line= gfxPath_to_gfxline(state, path, 1, user_movex, user_movey);
 
     if(getLogLevel() >= LOGLEVEL_TRACE)  {
         msg("<trace> eofill\n");
@@ -847,7 +978,7 @@ void SWFOutputDev::eoFill(GfxState *state)
 void SWFOutputDev::clip(GfxState *state) 
 {
     GfxPath * path = state->getPath();
-    gfxline_t*line = gfxPath_to_gfxline(state, path, 1);
+    gfxline_t*line = gfxPath_to_gfxline(state, path, 1, user_movex, user_movey);
     clipToGfxLine(state, line);
     gfxline_free(line);
 }
@@ -865,7 +996,7 @@ void SWFOutputDev::clipToGfxLine(GfxState *state, gfxline_t*line)
 void SWFOutputDev::eoClip(GfxState *state) 
 {
     GfxPath * path = state->getPath();
-    gfxline_t*line = gfxPath_to_gfxline(state, path, 1);
+    gfxline_t*line = gfxPath_to_gfxline(state, path, 1, user_movex, user_movey);
 
     if(getLogLevel() >= LOGLEVEL_TRACE)  {
         msg("<trace> eoclip\n");
@@ -877,37 +1008,32 @@ void SWFOutputDev::eoClip(GfxState *state)
     gfxline_free(line);
 }
 
-/* pass through functions for swf_output */
-int SWFOutputDev::save(char*filename)
+void SWFOutputDev::endframe()
 {
-    return gfxdevice_swf_save(output, filename);
-}
-void SWFOutputDev::pagefeed()
-{
-    swfoutput_pagefeed(output);
-}
-void* SWFOutputDev::getSWF()
-{
-    return (void*)gfxdevice_swf_get(output);
+    if(outer_clip_box) {
+       output->endclip(output);
+       outer_clip_box = 0;
+    }
+
+    output->endpage(output);
 }
 
 void SWFOutputDev::finish()
 {
-    if(output) {
-       this->swf = (SWF*)output->finish(output);
-       free(output);output=0;
+    if(outer_clip_box) {
+       if(output) {
+           output->endclip(output);
+       }
+       outer_clip_box = 0;
     }
 }
 
 SWFOutputDev::~SWFOutputDev() 
 {
     finish();
-    outputstarted = 0;
 
-    if(this->swf) {
-       swf_FreeTags(this->swf);
-       free(this->swf);
-       this->swf = 0;
+    if(this->pages) {
+       free(this->pages); this->pages = 0;
     }
 
     fontlist_t*l = this->fontlist;
@@ -915,15 +1041,15 @@ SWFOutputDev::~SWFOutputDev()
        fontlist_t*next = l->next;
        l->next = 0;
        gfxfont_free(l->font);
-       free(l->id);
-       free(l->filename);
+       free(l->id);l->id=0;
+       free(l->filename);l->filename=0;
        free(l);
        l = next;
     }
+    this->fontlist = 0;
 };
 GBool SWFOutputDev::upsideDown() 
 {
-    msg("<debug> upsidedown? yes");
     return gTrue;
 };
 GBool SWFOutputDev::useDrawChar() 
@@ -978,8 +1104,22 @@ char* makeStringPrintable(char*str)
 
 int getGfxCharID(gfxfont_t*font, int charnr, char *charname, int u)
 {
-    int t;
+    char*uniname = 0;
+    if(u>0) {
+       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) {
+               uniname = nameToUnicodeTab[t].name;
+               break;
+           }
+       }
+    }
+
     if(charname) {
+       int t;
        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);
@@ -996,6 +1136,24 @@ int getGfxCharID(gfxfont_t*font, int charnr, char *charname, int u)
        }
     }
 
+    if(uniname) {
+       int t;
+       for(t=0;t<font->num_glyphs;t++) {
+           if(font->glyphs[t].name && !strcmp(font->glyphs[t].name,uniname)) {
+               msg("<debug> Char [%d,%s,>%d(%s)<] maps to %d\n", charnr, charname, u, uniname, 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,uniname)) {
+               msg("<debug> Char [%d,%s,>>%d(%s)<<] maps to %d\n", charnr, charname, u, uniname, 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]);
@@ -1042,12 +1200,14 @@ void SWFOutputDev::beginString(GfxState *state, GString *s)
 void SWFOutputDev::drawChar(GfxState *state, double x, double y,
                        double dx, double dy,
                        double originX, double originY,
-                       CharCode c, Unicode *_u, int uLen)
+                       CharCode c, int nBytes, Unicode *_u, int uLen)
 {
     int render = state->getRender();
     // check for invisible text -- this is used by Acrobat Capture
-    if (render == 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");
@@ -1066,21 +1226,8 @@ void SWFOutputDev::drawChar(GfxState *state, double x, double y,
     Unicode u=0;
     char*name=0;
 
-    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(uLen)
+       u = _u[0];
 
     if(font->isCIDFont()) {
        GfxCIDFont*cfont = (GfxCIDFont*)font;
@@ -1091,8 +1238,7 @@ void SWFOutputDev::drawChar(GfxState *state, double x, double y,
        Gfx8BitFont*font8;
        font8 = (Gfx8BitFont*)font;
        char**enc=font8->getEncoding();
-       if(enc && enc[c])
-          name = 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);
@@ -1101,7 +1247,25 @@ void SWFOutputDev::drawChar(GfxState *state, double x, double y,
        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 = getGfxCharID(current_gfxfont, c, name, u);
+    int charid = -1;
+   
+    if(uLen<=1) {
+       charid = getGfxCharID(current_gfxfont, c, name, u);
+    } else {
+       charid = getGfxCharID(current_gfxfont, c, name, -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, nBytes, _u+t, 1);
+           }
+           return;
+       }
+    }
     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);
@@ -1110,11 +1274,17 @@ void SWFOutputDev::drawChar(GfxState *state, double x, double y,
 
     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);
@@ -1142,6 +1312,7 @@ void SWFOutputDev::endString(GfxState *state)
           now (as there may be texts of other rendering modes in this
           text object)- clipping objects have to wait until endTextObject,
           however */
+       output->setparameter(output, "mark","TXT");
        if((render&3) == RENDER_FILL) {
            fillGfxLine(state, current_text_stroke);
            gfxline_free(current_text_stroke);
@@ -1156,6 +1327,7 @@ void SWFOutputDev::endString(GfxState *state)
            gfxline_free(current_text_stroke);
            current_text_stroke = 0;
        }
+       output->setparameter(output, "mark","");
     }
 }    
 
@@ -1167,7 +1339,9 @@ void SWFOutputDev::endTextObject(GfxState *state)
        msg("<error> Internal error: drawChar.render!=beginString.render");
     
     if(current_text_clip) {
+       output->setparameter(output, "mark","TXT");
        clipToGfxLine(state, current_text_clip);
+       output->setparameter(output, "mark","");
        gfxline_free(current_text_clip);
        current_text_clip = 0;
     }
@@ -1206,15 +1380,21 @@ void SWFOutputDev::endType3Char(GfxState *state)
     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);
@@ -1225,12 +1405,13 @@ 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;
@@ -1239,64 +1420,59 @@ void SWFOutputDev::startPage(int pageNum, GfxState *state, double crop_x1, doubl
         /*if(user_clipy2 < y2)*/ y2 = user_clipy2;
     }
 
-    if(!outputstarted) {
-        msg("<verbose> Bounding box is (%f,%f)-(%f,%f)", x1,y1,x2,y2);
-
-        gfxdevice_swf_init(output);
-       
-       /* configure device */
-       parameter_t*p = device_config;
-       while(p) {
-           output->setparameter(output, p->name, p->value);
-           p = p->next;
-       }
-        
-       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) 
 {
-    msg("<debug> drawlink\n");
     double x1, y1, x2, y2, w;
-    GfxRGB rgb;
     gfxline_t points[5];
     int x, y;
+    
+    msg("<debug> drawlink\n");
 
-#ifdef XPDF_101
-    link->getBorder(&x1, &y1, &x2, &y2, &w);
-#else
     link->getRect(&x1, &y1, &x2, &y2);
-#endif
-    rgb.r = 0;
-    rgb.g = 0;
-    rgb.b = 1;
     cvtUserToDev(x1, y1, &x, &y);
     points[0].type = gfx_moveTo;
-    points[0].x = points[4].x = (int)x;
-    points[0].y = points[4].y = (int)y;
+    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].type = gfx_lineTo;
-    points[1].x = (int)x;
-    points[1].y = (int)y;
+    points[1].x = x + user_movex;
+    points[1].y = y + user_movey;
     points[1].next = &points[2];
     cvtUserToDev(x2, y2, &x, &y);
     points[2].type = gfx_lineTo;
-    points[2].x = (int)x;
-    points[2].y = (int)y;
+    points[2].x = x + user_movex;
+    points[2].y = y + user_movey;
     points[2].next = &points[3];
     cvtUserToDev(x1, y2, &x, &y);
     points[3].type = gfx_lineTo;
-    points[3].x = (int)x;
-    points[3].y = (int)y;
+    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 = (int)x;
-    points[4].y = (int)y;
+    points[4].x = x + user_movex;
+    points[4].y = y + user_movey;
     points[4].next = 0;
 
     LinkAction*action=link->getAction();
@@ -1353,7 +1529,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"))
                     {
@@ -1367,7 +1544,9 @@ void SWFOutputDev::drawLink(Link *link, Catalog *catalog)
             type = "Launch";
             LinkLaunch*l = (LinkLaunch*)action;
             GString * str = new GString(l->getFileName());
-            str->append(l->getParams());
+           GString * params = l->getParams();
+           if(params)
+               str->append(params);
             s = strdup(str->getCString());
             delete str;
         }
@@ -1393,6 +1572,7 @@ void SWFOutputDev::drawLink(Link *link, Catalog *catalog)
             break;
         }
     }
+
     if(!s) s = strdup("-?-");
 
     if(!linkinfo && (page || url))
@@ -1404,14 +1584,19 @@ void SWFOutputDev::drawLink(Link *link, Catalog *catalog)
     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) {
-           char buf[80];
-           sprintf(buf, "page%d", t);
-            output->drawlink(output, points, buf);
+           }
+       }
+        if(lpage<0) {
+           lpage = page;
        }
+       char buf[80];
+       sprintf(buf, "page%d", lpage);
+       output->drawlink(output, points, buf);
     }
     else if(url)
     {
@@ -1557,15 +1742,9 @@ char*SWFOutputDev::writeEmbeddedFontToFile(XRef*ref, GfxFont*font)
        msg("<error> Couldn't read embedded font file");
        return 0;
       }
-#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);
       //cvt->convertToType0("test", f);
       delete cvt;
@@ -1577,13 +1756,8 @@ char*SWFOutputDev::writeEmbeddedFontToFile(XRef*ref, GfxFont*font)
        msg("<error> Couldn't read embedded font file");
        return 0;
       }
-#ifdef XPDF_101
-      TrueTypeFontFile *cvt = new TrueTypeFontFile(fontBuf, fontLen);
-      cvt->writeTTF(f);
-#else
       FoFiTrueType *cvt = FoFiTrueType::make(fontBuf, fontLen);
       cvt->writeTTF(FoFiWrite, f);
-#endif
       delete cvt;
       gfree(fontBuf);
     } else {
@@ -1743,7 +1917,7 @@ 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";
@@ -1802,7 +1976,7 @@ void SWFOutputDev::setXRef(PDFDoc*doc, XRef *xref)
     this->xref = xref;
 }
 
-int SWFOutputDev::setGfxFont(char*id, char*filename)
+int SWFOutputDev::setGfxFont(char*id, char*name, char*filename, double maxSize)
 {
     gfxfont_t*font = 0;
     fontlist_t*last=0,*l = this->fontlist;
@@ -1820,7 +1994,17 @@ int SWFOutputDev::setGfxFont(char*id, char*filename)
        l = l->next;
     }
     if(!filename) return 0;
-    font = gfxfont_load(filename);
+
+    /* 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;
+   
+    msg("<verbose> Loading %s...", filename);
+    font = gfxfont_load(filename, quality);
+    msg("<verbose> Font %s loaded successfully", filename);
+
     l = new fontlist_t;
     l->font = font;
     l->filename = strdup(filename);
@@ -1844,7 +2028,15 @@ void SWFOutputDev::updateFont(GfxState *state)
     if (!gfxFont) {
        return;
     }  
+    
     char * fontid = getFontID(gfxFont);
+    char * fontname = getFontName(gfxFont);
+
+    double maxSize = 1.0;
+
+    if(this->info) {
+       maxSize = this->info->getMaximumFontSize(fontid);
+    }
     
     int t;
     /* first, look if we substituted this font before-
@@ -1860,8 +2052,9 @@ void SWFOutputDev::updateFont(GfxState *state)
 
     /* second, see if this is a font which was used before-
        if so, we are done */
-    if(setGfxFont(fontid, 0)) {
+    if(setGfxFont(fontid, fontname, 0, 0)) {
        free(fontid);
+       free(fontname);
        return;
     }
 /*    if(swfoutput_queryfont(&output, fontid))
@@ -1878,6 +2071,7 @@ void SWFOutputDev::updateFont(GfxState *state)
            showFontError(gfxFont, 2);
        }
        free(fontid);
+       free(fontname);
        return;
     }
 
@@ -1900,15 +2094,21 @@ void SWFOutputDev::updateFont(GfxState *state)
       if(!fileName) showFontError(gfxFont,0);
       else del = 1;
     } else {
-      char * fontname = getFontName(gfxFont);
       fileName = searchFont(fontname);
       if(!fileName) showFontError(gfxFont,0);
     }
     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", fontname, lastfontdir);
+       else
+           msg("<warning> Try specifying one or more font directories");
+
        fileName = substituteFont(gfxFont, fontid);
+       if(!fileName)
+           exit(1);
        if(fontid) { free(fontid);fontid = strdup(substitutetarget[substitutepos-1]); /*ugly hack*/};
        msg("<notice> Font is now %s (%s)", fontid, fileName);
     }
@@ -1916,23 +2116,28 @@ void SWFOutputDev::updateFont(GfxState *state)
     if(!fileName) {
        msg("<error> Couldn't set font %s\n", fontid);
        free(fontid);
+       free(fontname);
        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);
     
-    if(!setGfxFont(fontid, 0)) {
-       setGfxFont(fontid, fileName);
+    if(!setGfxFont(fontid, fontname, 0, 0)) {
+       setGfxFont(fontid, fontname, fileName, maxSize);
     }
    
     if(fileName && del)
        unlinkfont(fileName);
+
     if(fileName)
         free(fileName);
     free(fontid);
+    free(fontname);
+
+    msg("<verbose> |");
 }
 
 #define SQR(x) ((x)*(x))
@@ -1988,13 +2193,13 @@ unsigned char* antialize(unsigned char*data, int width, int height, int newwidth
 #define IMAGE_TYPE_JPEG 0
 #define IMAGE_TYPE_LOSSLESS 1
 
-static void drawimage(gfxdevice_t*dev, RGBA* data, int sizex,int sizey, 
+static void drawimage(gfxdevice_t*dev, gfxcolor_t* 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;
+    gfxcolor_t*newpic=0;
     
     double l1 = sqrt((x4-x1)*(x4-x1) + (y4-y1)*(y4-y1));
     double l2 = sqrt((x2-x1)*(x2-x1) + (y2-y1)*(y2-y1));
@@ -2039,13 +2244,13 @@ static void drawimage(gfxdevice_t*dev, RGBA* data, int sizex,int sizey,
     dev->fillbitmap(dev, &p1, &img, &m, 0);
 }
 
-void drawimagejpeg(gfxdevice_t*dev, RGBA*mem, int sizex,int sizey, 
+void drawimagejpeg(gfxdevice_t*dev, gfxcolor_t*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, 
+void drawimagelossless(gfxdevice_t*dev, gfxcolor_t*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);
@@ -2054,22 +2259,59 @@ void drawimagelossless(gfxdevice_t*dev, RGBA*mem, int sizex,int sizey,
 
 void SWFOutputDev::drawGeneralImage(GfxState *state, Object *ref, Stream *str,
                                   int width, int height, GfxImageColorMap*colorMap, GBool invert,
-                                  GBool inlineImg, int mask, int*maskColors)
+                                  GBool inlineImg, int mask, int*maskColors,
+                                  Stream *maskStr, int maskWidth, int maskHeight, GBool maskInvert, GfxImageColorMap*maskColorMap)
 {
-  FILE *fi;
-  int c;
-  char fileName[128];
   double x1,y1,x2,y2,x3,y3,x4,y4;
   ImageStream *imgStr;
   Guchar pixBuf[4];
   GfxRGB rgb;
   int ncomps = 1;
   int bits = 1;
+  unsigned char* maskbitmap = 0;
                                 
   if(colorMap) {
     ncomps = colorMap->getNumPixelComps();
     bits = colorMap->getBits();
   }
+  
+  if(maskStr) {
+      int x,y;
+      unsigned char buf[8];
+      maskbitmap = (unsigned char*)malloc(maskHeight*maskWidth);
+      if(maskColorMap) {
+         ImageStream*imgMaskStr = new ImageStream(maskStr, maskWidth, maskColorMap->getNumPixelComps(), maskColorMap->getBits());
+         imgMaskStr->reset();
+         unsigned char pal[256];
+         int n = 1 << colorMap->getBits();
+         int t;
+         for(t=0;t<n;t++) {
+             GfxGray gray;
+             pixBuf[0] = t;
+             maskColorMap->getGray(pixBuf, &gray);
+             pal[t] = colToByte(gray);
+         }
+         for (y = 0; y < maskHeight; y++) {
+             for (x = 0; x < maskWidth; x++) {
+                 imgMaskStr->getPixel(buf);
+                 maskbitmap[y*maskWidth+x] = pal[buf[0]];
+             }
+         }
+         delete imgMaskStr;
+      } else {
+         ImageStream*imgMaskStr = new ImageStream(maskStr, maskWidth, 1, 1);
+         imgMaskStr->reset();
+         for (y = 0; y < maskHeight; y++) {
+             for (x = 0; x < maskWidth; x++) {
+                 imgMaskStr->getPixel(buf);
+                 buf[0]^=maskInvert;
+                 maskbitmap[y*maskWidth+x] = (buf[0]^1)*255;
+             }
+         }
+         delete imgMaskStr;
+      }
+  }
+      
   imgStr = new ImageStream(str, width, ncomps,bits);
   imgStr->reset();
 
@@ -2083,13 +2325,15 @@ void SWFOutputDev::drawGeneralImage(GfxState *state, Object *ref, Stream *str,
          imgStr->getPixel(buf);
       }
       delete imgStr;
+      if(maskbitmap)
+         free(maskbitmap);
       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) {
@@ -2109,16 +2353,16 @@ void SWFOutputDev::drawGeneralImage(GfxState *state, Object *ref, Stream *str,
       unsigned char buf[8];
       int x,y;
       unsigned char*pic = new unsigned char[width*height];
-      RGBA pal[256];
+      gfxcolor_t pal[256];
       GfxRGB rgb;
       state->getFillRGB(&rgb);
 
       memset(pal,255,sizeof(pal));
-      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].r = (int)(colToByte(rgb.r)); pal[1].r = 0;
+      pal[0].g = (int)(colToByte(rgb.g)); pal[1].g = 0;
+      pal[0].b = (int)(colToByte(rgb.b)); pal[1].b = 0;
       pal[0].a = 255;              pal[1].a = 0;
-
+    
       int numpalette = 2;
       int realwidth = (int)sqrt(SQR(x2-x3) + SQR(y2-y3));
       int realheight = (int)sqrt(SQR(x1-x2) + SQR(y1-y2));
@@ -2153,21 +2397,18 @@ void SWFOutputDev::drawGeneralImage(GfxState *state, Object *ref, Stream *str,
          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);
+             pal[t].r = colToByte(rgb.r);
+             pal[t].g = colToByte(rgb.g);
+             pal[t].b = colToByte(rgb.b);
+             pal[t].a = (unsigned char)(t*r);
          }
       }
 
-      RGBA*pic2 = new RGBA[width*height];
+      gfxcolor_t*pic2 = new gfxcolor_t[width*height];
       for (y = 0; y < height; ++y) {
        for (x = 0; x < width; ++x) {
          pic2[width*y+x] = pal[pic[y*width+x]];
@@ -2177,21 +2418,25 @@ void SWFOutputDev::drawGeneralImage(GfxState *state, Object *ref, Stream *str,
       free(pic2);
       free(pic);
       delete imgStr;
+      if(maskbitmap) free(maskbitmap);
       return;
-  } 
+  }
 
   int x,y;
 
   if(colorMap->getNumPixelComps()!=1 || str->getKind()==strDCT) {
-      RGBA*pic=new RGBA[width*height];
+      gfxcolor_t*pic=new gfxcolor_t[width*height];
       for (y = 0; y < height; ++y) {
        for (x = 0; x < width; ++x) {
          imgStr->getPixel(pixBuf);
          colorMap->getRGB(pixBuf, &rgb);
-         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].r = (unsigned char)(colToByte(rgb.r));
+         pic[width*y+x].g = (unsigned char)(colToByte(rgb.g));
+         pic[width*y+x].b = (unsigned char)(colToByte(rgb.b));
          pic[width*y+x].a = 255;//(U8)(rgb.a * 255 + 0.5);
+         if(maskbitmap) {
+             pic[width*y+x].a = maskbitmap[(y*maskHeight/height)*maskWidth+(x*maskWidth/width)];
+         }
        }
       }
       if(str->getKind()==strDCT)
@@ -2200,15 +2445,18 @@ void SWFOutputDev::drawGeneralImage(GfxState *state, Object *ref, Stream *str,
          drawimagelossless(output, pic, width, height, x1,y1,x2,y2,x3,y3,x4,y4);
       delete pic;
       delete imgStr;
+      if(maskbitmap) free(maskbitmap);
       return;
   } else {
-      RGBA*pic=new RGBA[width*height];
-      RGBA pal[256];
+      gfxcolor_t*pic=new gfxcolor_t[width*height];
+      gfxcolor_t pal[256];
+      int n = 1 << colorMap->getBits();
       int t;
       for(t=0;t<256;t++) {
          pixBuf[0] = t;
          colorMap->getRGB(pixBuf, &rgb);
-         /*if(maskColors && *maskColors==t) {
+
+         {/*if(maskColors && *maskColors==t) {
              msg("<notice> Color %d is transparent", t);
              if (imgData->maskColors) {
                *alpha = 0;
@@ -2228,10 +2476,10 @@ void SWFOutputDev::drawGeneralImage(GfxState *state, Object *ref, Stream *str,
                    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);
+         } else {*/
+             pal[t].r = (unsigned char)(colToByte(rgb.r));
+             pal[t].g = (unsigned char)(colToByte(rgb.g));
+             pal[t].b = (unsigned char)(colToByte(rgb.b));
              pal[t].a = 255;//(U8)(rgb.b * 255 + 0.5);
          }
       }
@@ -2239,12 +2487,16 @@ void SWFOutputDev::drawGeneralImage(GfxState *state, Object *ref, Stream *str,
        for (x = 0; x < width; ++x) {
          imgStr->getPixel(pixBuf);
          pic[width*y+x] = pal[pixBuf[0]];
+         if(maskbitmap) {
+             pic[width*y+x].a = maskbitmap[(y*maskHeight/height)*maskWidth+(x*maskWidth/width)];
+         }
        }
       }
       drawimagelossless(output, pic, width, height, x1,y1,x2,y2,x3,y3,x4,y4);
 
       delete pic;
       delete imgStr;
+      if(maskbitmap) free(maskbitmap);
       return;
   }
 }
@@ -2256,7 +2508,7 @@ void SWFOutputDev::drawImageMask(GfxState *state, Object *ref, Stream *str,
   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, 0);
+  drawGeneralImage(state,ref,str,width,height,0,invert,inlineImg,1, 0, 0,0,0,0, 0);
 }
 
 void SWFOutputDev::drawImage(GfxState *state, Object *ref, Stream *str,
@@ -2266,14 +2518,51 @@ void SWFOutputDev::drawImage(GfxState *state, Object *ref, Stream *str,
   if(states[statepos].textRender & 4) //clipped
       return;
 
-  msg("<verbose> drawImage %dx%d, %s %s, inline=%d", width, height, 
+  msg("<verbose> drawImage %dx%d, %s, %s, inline=%d", width, height, 
          colorMap?"colorMap":"no colorMap", 
          maskColors?"maskColors":"no maskColors",
          inlineImg);
   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,maskColors);
+  drawGeneralImage(state,ref,str,width,height,colorMap,0,inlineImg,0,maskColors, 0,0,0,0, 0);
+}
+  
+void SWFOutputDev::drawMaskedImage(GfxState *state, Object *ref, Stream *str,
+                              int width, int height,
+                              GfxImageColorMap *colorMap,
+                              Stream *maskStr, int maskWidth, int maskHeight,
+                              GBool maskInvert)
+{
+  if(states[statepos].textRender & 4) //clipped
+      return;
+
+  msg("<verbose> drawMaskedImage %dx%d, %s, %dx%d mask", width, height, 
+         colorMap?"colorMap":"no colorMap", 
+         maskWidth, maskHeight);
+  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,0,0,0, maskStr, maskWidth, maskHeight, maskInvert, 0);
+}
+
+void SWFOutputDev::drawSoftMaskedImage(GfxState *state, Object *ref, Stream *str,
+                                  int width, int height,
+                                  GfxImageColorMap *colorMap,
+                                  Stream *maskStr,
+                                  int maskWidth, int maskHeight,
+                                  GfxImageColorMap *maskColorMap)
+{
+  if(states[statepos].textRender & 4) //clipped
+      return;
+
+  msg("<verbose> drawSoftMaskedImage %dx%d, %s, %dx%d mask", width, height, 
+         colorMap?"colorMap":"no colorMap", 
+         maskWidth, maskHeight);
+  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,0,0,0, maskStr, maskWidth, maskHeight, 0, maskColorMap);
 }
 
 //SWFOutputDev*output = 0; 
@@ -2362,9 +2651,9 @@ void pdfswf_setparameter(char*name, char*value)
        storeDeviceParameter("ppmsubpixels", buf);
     } else if(!strcmp(name, "forceType0Fonts")) {
        forceType0Fonts = atoi(value);
-    } else if(!strcmp(name, "fontdir")) {
+    } else if(!strncmp(name, "fontdir", strlen("fontdir"))) {
         pdfswf_addfontdir(value);
-    } else if(!strcmp(name, "languagedir")) {
+    } else if(!strncmp(name, "languagedir", strlen("languagedir"))) {
         pdfswf_addlanguagedir(value);
     } else if(!strcmp(name, "fontconfig")) {
         config_use_fontconfig = atoi(value);
@@ -2402,7 +2691,7 @@ void pdfswf_addlanguagedir(char*dir)
 
     int l;
     FILE*fi = 0;
-    char* config_file = (char*)malloc(strlen(dir) + 1 + sizeof("add-to-xpdfrc"));
+    char* config_file = (char*)malloc(strlen(dir) + 1 + sizeof("add-to-xpdfrc") + 1);
     strcpy(config_file, dir);
     strcat(config_file, dirseparator());
     strcat(config_file, "add-to-xpdfrc");
@@ -2420,6 +2709,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);
@@ -2464,14 +2754,15 @@ typedef struct _pdf_doc_internal
 {
     int protect;
     PDFDoc*doc;
+    InfoOutputDev*info;
 } pdf_doc_internal_t;
 typedef struct _pdf_page_internal
 {
 } pdf_page_internal_t;
-typedef struct _swf_output_internal
+typedef struct _dev_output_internal
 {
     SWFOutputDev*outputDev;
-} swf_output_internal_t;
+} dev_output_internal_t;
 
 pdf_doc_t* pdf_init(char*filename, char*userPassword)
 {
@@ -2540,24 +2831,15 @@ 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++) {
+       i->doc->displayPage((OutputDev*)io, t, zoom, zoom, /*rotate*/0, /*usemediabox*/true, /*crop*/true, /*doLinks*/(int)1);
     }
-    pages[pagepos++] = page;
+    i->info = io;
+
+    return pdf_doc;
 }
 
 class MemCheck
@@ -2574,10 +2856,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;
@@ -2607,77 +2890,98 @@ void pdf_page_destroy(pdf_page_t*pdf_page)
     free(pdf_page);pdf_page=0;
 }
 
-swf_output_t* swf_output_init() 
+dev_output_t* dev_output_init(gfxdevice_t*dev) 
 {
-    swf_output_t*swf_output = (swf_output_t*)malloc(sizeof(swf_output_t));
-    memset(swf_output, 0, sizeof(swf_output_t));
-    swf_output_internal_t*i= (swf_output_internal_t*)malloc(sizeof(swf_output_internal_t));
-    memset(i, 0, sizeof(swf_output_internal_t));
-    swf_output->internal = i;
+    dev_output_t*dev_output = (dev_output_t*)malloc(sizeof(dev_output_t));
+    memset(dev_output, 0, sizeof(dev_output_t));
+    dev_output_internal_t*i= (dev_output_internal_t*)malloc(sizeof(dev_output_internal_t));
+    memset(i, 0, sizeof(dev_output_internal_t));
+    dev_output->internal = i;
 
-    i->outputDev = new SWFOutputDev();
-    return swf_output;
+    i->outputDev = new SWFOutputDev(dev);
+    return dev_output;
 }
 
-void swf_output_setparameter(swf_output_t*swf_output, char*name, char*value)
+void dev_output_setparameter(dev_output_t*swf, char*name, char*value)
 {
-    /* FIXME */
     pdfswf_setparameter(name, value);
 }
 
-void swf_output_pagefeed(swf_output_t*swf)
+void dev_output_startframe(dev_output_t*swf, int width, int height)
 {
-    swf_output_internal_t*i= (swf_output_internal_t*)swf->internal;
-    i->outputDev->pagefeed();
-    i->outputDev->getDimensions(&swf->x1, &swf->y1, &swf->x2, &swf->y2);
+    dev_output_internal_t*i= (dev_output_internal_t*)swf->internal;
+    i->outputDev->startFrame(width, height);
 }
 
-int swf_output_save(swf_output_t*swf, char*filename)
+void dev_output_endframe(dev_output_t*swf)
 {
-    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;
+    dev_output_internal_t*i= (dev_output_internal_t*)swf->internal;
+    i->outputDev->endframe();
 }
 
-void* swf_output_get(swf_output_t*swf)
+void dev_output_finish(dev_output_t*swf)
 {
-    swf_output_internal_t*i= (swf_output_internal_t*)swf->internal;
-    void* ret = i->outputDev->getSWF();
-    i->outputDev->getDimensions(&swf->x1, &swf->y1, &swf->x2, &swf->y2);
-    return ret;
+    dev_output_internal_t*i= (dev_output_internal_t*)swf->internal;
+    i->outputDev->finish();
+}
+
+void dev_output_preparepage(dev_output_t*swf, int pdfpage, int outputpage)
+{
+    dev_output_internal_t*i= (dev_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;
 }
 
-void swf_output_destroy(swf_output_t*output)
+void dev_output_destroy(dev_output_t*output)
 {
-    swf_output_internal_t*i = (swf_output_internal_t*)output->internal;
+    dev_output_internal_t*i = (dev_output_internal_t*)output->internal;
     delete i->outputDev; i->outputDev=0;
     free(output->internal);output->internal=0;
     free(output);
 }
 
-void pdf_page_render2(pdf_page_t*page, swf_output_t*swf)
+void pdf_page_render2(pdf_page_t*page, dev_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;
+    dev_output_internal_t*si = (dev_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) {
        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);
+    pi->doc->displayPage((OutputDev*)si->outputDev, page->nr, zoom, zoom, /*rotate*/0, true, true, /*doLinks*/(int)1);
 }
 
-void pdf_page_rendersection(pdf_page_t*page, swf_output_t*output, int x, int y, int x1, int y1, int x2, int y2)
+void pdf_page_rendersection(pdf_page_t*page, dev_output_t*output, int x, int y, int x1, int y1, int x2, int y2)
 {
     pdf_doc_internal_t*pi = (pdf_doc_internal_t*)page->parent->internal;
-    swf_output_internal_t*si = (swf_output_internal_t*)output->internal;
+    dev_output_internal_t*si = (dev_output_internal_t*)output->internal;
 
     si->outputDev->setMove(x,y);
     if((x1|y1|x2|y2)==0) x2++;
@@ -2685,10 +2989,10 @@ void pdf_page_rendersection(pdf_page_t*page, swf_output_t*output, int x, int y,
 
     pdf_page_render2(page, output);
 }
-void pdf_page_render(pdf_page_t*page, swf_output_t*output)
+void pdf_page_render(pdf_page_t*page, dev_output_t*output)
 {
     pdf_doc_internal_t*pi = (pdf_doc_internal_t*)page->parent->internal;
-    swf_output_internal_t*si = (swf_output_internal_t*)output->internal;
+    dev_output_internal_t*si = (dev_output_internal_t*)output->internal;
     
     si->outputDev->setMove(0,0);
     si->outputDev->setClip(0,0,0,0);
@@ -2706,11 +3010,7 @@ pdf_page_info_t* pdf_page_getinfo(pdf_page_t*page)
 
     InfoOutputDev*output = new InfoOutputDev;
     
-#ifdef XPDF_101
-    pi->doc->displayPage((OutputDev*)output, page->nr, /*zoom*/zoom, /*rotate*/0, /*doLinks*/(int)1);
-#else
-    pi->doc->displayPage((OutputDev*)output, page->nr, zoom, zoom, /*rotate*/0, true, /*doLinks*/(int)1);
-#endif
+    pi->doc->displayPage((OutputDev*)output, page->nr, zoom, zoom, /*rotate*/0, true, true, /*doLinks*/(int)1);
 
     info->xMin = output->x1;
     info->yMin = output->y1;
@@ -2728,4 +3028,5 @@ pdf_page_info_t* pdf_page_getinfo(pdf_page_t*page)
 void pdf_page_info_destroy(pdf_page_info_t*info)
 {
     free(info);
+
 }