replaced libart with new polygon code
[swftools.git] / lib / pdf / GFXOutputDev.cc
index 84d02ab..e963148 100644 (file)
@@ -1,4 +1,4 @@
-/* pdfswf.cc
+/* GFXOutputDev.cc
    implements a pdf output device (OutputDev).
 
    This file is part of swftools.
@@ -22,7 +22,9 @@
 #include <stdarg.h>
 #include <stddef.h>
 #include <string.h>
+#ifdef HAVE_UNISTD_H
 #include <unistd.h>
+#endif
 #include "../../config.h"
 #include "../os.h"
 #ifdef HAVE_DIRENT_H
 #endif
 //xpdf header files
 #include "config.h"
+#ifdef HAVE_POPPLER
+#include <goo/GooString.h>
+#include <goo/gfile.h>
+#else
 #include "gfile.h"
 #include "GString.h"
-#include "gmem.h"
+#endif
 #include "Object.h"
 #include "Stream.h"
 #include "Array.h"
 #include "OutputDev.h"
 #include "GfxFont.h"
 #include "GfxState.h"
-#include "CharCodeToUnicode.h"
-#include "NameToUnicodeTable.h"
+//#include "NameToUnicodeTable.h"
 #include "GlobalParams.h"
-#include "FoFiType1C.h"
-#include "FoFiTrueType.h"
-#include "GHash.h"
 #include "GFXOutputDev.h"
 
-//swftools header files
+//  swftools header files
 #include "../log.h"
 #include "../gfxdevice.h"
 #include "../gfxtools.h"
 #include "../gfxfont.h"
+#include "../gfxpoly.h"
 #include "../devices/record.h"
 #include "../devices/ops.h"
-#include "../devices/arts.h"
+#include "../devices/polyops.h"
 #include "../devices/render.h"
 
-#include "../art/libart.h"
-#include "../devices/artsutils.c"
-
 #include "../png.h"
 #include "fonts.h"
 
 #include <math.h>
 
+#define SQRT2 1.41421356237309504880
+
 typedef struct _fontfile
 {
     const char*filename;
+    int len; // basename length
     int used;
+    struct _fontfile*next;
 } fontfile_t;
 
 // for pdfswf_addfont
-static fontfile_t fonts[2048];
+
+static fontfile_t* global_fonts = 0;
+static fontfile_t* global_fonts_next = 0;
+
 static int fontnum = 0;
 
 /* config */
 
-static char* lastfontdir = 0;
-
 struct fontentry {
     const char*pdffont;
     const char*filename;
@@ -118,7 +123,7 @@ struct fontentry {
 
 
 static int verbose = 0;
-static int dbgindent = 0;
+static int dbgindent = 1;
 static void dbg(const char*format, ...)
 {
     char buf[1024];
@@ -127,7 +132,7 @@ static void dbg(const char*format, ...)
     if(!verbose)
        return;
     va_start(arglist, format);
-    vsprintf(buf, format, arglist);
+    vsnprintf(buf, sizeof(buf)-1, format, arglist);
     va_end(arglist);
     l = strlen(buf);
     while(l && buf[l-1]=='\n') {
@@ -144,17 +149,34 @@ static void dbg(const char*format, ...)
     fflush(stdout);
 }
 
+GFXOutputGlobals*gfxglobals=0;
 
-typedef struct _feature
+GFXOutputGlobals::GFXOutputGlobals()
 {
-    char*string;
-    struct _feature*next;
-} feature_t;
-feature_t*featurewarnings = 0;
+    this->featurewarnings = 0;
+    this->jpeginfo = 0;
+    this->textmodeinfo = 0;
+    this->linkinfo = 0;
+    this->pbminfo = 0;
+}
+GFXOutputGlobals::~GFXOutputGlobals()
+{
+    feature_t*f = this->featurewarnings;
+    while(f) {
+       feature_t*next = f->next;
+       if(f->string) {
+           free(f->string);f->string =0;
+       }
+       f->next = 0;
+       free(f);
+       f = next;
+    }
+    this->featurewarnings = 0;
+}
 
-void GFXOutputDev::showfeature(const char*feature,char fully, char warn)
+void GFXOutputDev::showfeature(const char*feature, char fully, char warn)
 {
-    feature_t*f = featurewarnings;
+    feature_t*f = gfxglobals->featurewarnings;
     while(f) {
        if(!strcmp(feature, f->string))
            return;
@@ -162,8 +184,8 @@ void GFXOutputDev::showfeature(const char*feature,char fully, char warn)
     }
     f = (feature_t*)malloc(sizeof(feature_t));
     f->string = strdup(feature);
-    f->next = featurewarnings;
-    featurewarnings = f;
+    f->next = gfxglobals->featurewarnings;
+    gfxglobals->featurewarnings = f;
     if(warn) {
        msg("<warning> %s not yet %ssupported!",feature,fully?"fully ":"");
        if(this->config_break_on_warning) {
@@ -194,7 +216,7 @@ GFXOutputState::GFXOutputState() {
 
 GBool GFXOutputDev::interpretType3Chars() 
 {
-    return this->do_interpretType3Chars;
+    return gTrue;
 }
 
 typedef struct _drawnchar
@@ -290,9 +312,11 @@ void unlinkfont(char* filename)
     }
 }
 
+static int config_use_fontconfig = 1;
+static int fcinitcalled = 0; 
 
 GFXGlobalParams::GFXGlobalParams()
-: GlobalParams("")
+: GlobalParams((char*)"")
 {
     //setupBaseFonts(char *dir); //not tested yet
 }
@@ -305,12 +329,187 @@ GFXGlobalParams::~GFXGlobalParams()
            unlinkfont(pdf2t1map[t].fullfilename);
        }
     }
+#ifdef HAVE_FONTCONFIG
+    if(config_use_fontconfig && fcinitcalled)
+       FcFini();
+#endif
 }
+#ifdef HAVE_FONTCONFIG
+static char fc_ismatch(FcPattern*match, char*family, char*style)
+{
+    char*fcfamily=0,*fcstyle=0,*fcfullname=0,*filename=0;
+    FcBool scalable=FcFalse, outline=FcFalse;
+    FcPatternGetString(match, "family", 0, (FcChar8**)&fcfamily);
+    FcPatternGetString(match, "style", 0, (FcChar8**)&fcstyle);
+    FcPatternGetString(match, "file", 0, (FcChar8**)&filename);
+    FcPatternGetBool(match, "outline", 0, &outline);
+    FcPatternGetBool(match, "scalable", 0, &scalable);
+
+    if(scalable!=FcTrue || outline!=FcTrue)
+       return 0;
+
+    if (!strcasecmp(fcfamily, family)) {
+       msg("<debug> Font %s-%s (%s) is a match for %s%s%s", fcfamily, fcstyle, filename, family, style?"-":"", style?style:"");
+       return 1;
+    } else {
+       //msg("<debug> Font %s-%s (%s) is NOT a match for %s%s%s", fcfamily, fcstyle, filename, family, style?"-":"", style?style:"");
+       return 0;
+    }
+}
+#endif
+
+char* fontconfig_searchForFont(char*name)
+{
+#ifdef HAVE_FONTCONFIG
+    if(!config_use_fontconfig)
+       return 0;
+    
+    // call init ony once
+    if (!fcinitcalled) {
+        fcinitcalled = 1;
+
+       // check whether we have a config file
+       char* configfile = (char*)FcConfigFilename(0);
+       int configexists = 0;
+       FILE*fi = fopen(configfile, "rb");
+       if(fi) {
+           configexists = 1;fclose(fi);
+           msg("<debug> Initializing FontConfig (configfile=%s)", configfile);
+       } else {
+           msg("<debug> Initializing FontConfig (no configfile)");
+       }
+
+       if(!configexists) {
+           /* A fontconfig instance which didn't find a configfile is unbelievably
+              cranky, so let's just write out a small xml file and make fontconfig
+              happy */
+           FcConfig*c = FcConfigCreate();
+           char namebuf[512];
+           char* tmpFileName = mktmpname(namebuf);
+           FILE*fi = fopen(tmpFileName, "wb");
+           fprintf(fi, "<?xml version=\"1.0\"?>\n<fontconfig>\n");//<!DOCTYPE fontconfig SYSTEM "fonts.dtd">
+#ifdef WIN32
+           fprintf(fi, "<dir>WINDOWSFONTDIR</dir>\n");
+#endif
+           fprintf(fi, "<dir>~/.fonts</dir>\n");
+#ifdef WIN32
+           fprintf(fi, "<cachedir>WINDOWSTEMPDIR_FONTCONFIG_CACHE</cachedir>\n");
+#endif
+           fprintf(fi, "<cachedir>~/.fontconfig</cachedir>\n");
+           fprintf(fi, "</fontconfig>\n");
+           fclose(fi);
+           FcConfigParseAndLoad(c, (FcChar8*)tmpFileName, 1);
+           FcConfigBuildFonts(c);
+           FcConfigSetCurrent(c);
+       }
+
+       if(!FcInit()) {
+            msg("<debug> FontConfig Initialization failed. Disabling.");
+            config_use_fontconfig = 0;
+            return 0;
+        }
+       FcConfig * config = FcConfigGetCurrent();
+       if(!config) {
+            msg("<debug> FontConfig Config Initialization failed. Disabling.");
+            config_use_fontconfig = 0;
+            return 0;
+        }
+
+        /* add external fonts to fontconfig's config, too. */
+        fontfile_t*fd = global_fonts;
+        while(fd) {
+            FcConfigAppFontAddFile(config, (FcChar8*)fd->filename);
+            fd = fd->next;
+        }
+
+       FcFontSet * set =  FcConfigGetFonts(config, FcSetSystem);
+        msg("<verbose> FontConfig initialized. Found %d fonts", set?set->nfont:0);
+       if(!set || !set->nfont) {
+            msg("<debug> FontConfig has zero fonts. Disabling.");
+            config_use_fontconfig = 0;
+            return 0;
+        }
+
+       if(getLogLevel() >= LOGLEVEL_TRACE) {
+           int t;
+           for(t=0;t<set->nfont;t++) {
+               char*fcfamily=0,*fcstyle=0,*filename=0;
+               FcBool scalable=FcFalse, outline=FcFalse;
+               FcPatternGetString(set->fonts[t], "family", 0, (FcChar8**)&fcfamily);
+               FcPatternGetString(set->fonts[t], "style", 0, (FcChar8**)&fcstyle);
+               FcPatternGetString(set->fonts[t], "file", 0, (FcChar8**)&filename);
+               FcPatternGetBool(set->fonts[t], "outline", 0, &outline);
+               FcPatternGetBool(set->fonts[t], "scalable", 0, &scalable);
+               if(scalable && outline) {
+                   msg("<trace> %s-%s -> %s", fcfamily, fcstyle, filename);
+               }
+           }
+       }
+    }
+
+    char*family = strdup(name);
+    char*style = 0;
+    char*dash = strchr(family, '-');
+    FcPattern*pattern = 0;
+    if(dash) {
+       *dash = 0;
+       style = dash+1;
+       msg("<debug> FontConfig: Looking for font %s (family=%s style=%s)", name, family, style);
+       pattern = FcPatternBuild(NULL, FC_OUTLINE, FcTypeBool, FcTrue, FC_SCALABLE, FcTypeBool, FcTrue, FC_FAMILY, FcTypeString, family, FC_STYLE, FcTypeString, style, NULL);
+    } else {
+       msg("<debug> FontConfig: Looking for font %s (family=%s)", name, family);
+       pattern = FcPatternBuild(NULL, FC_OUTLINE, FcTypeBool, FcTrue, FC_SCALABLE, FcTypeBool, FcTrue, FC_FAMILY, FcTypeString, family, NULL);
+    }
+
+    FcResult result;
+    FcConfigSubstitute(0, pattern, FcMatchPattern); 
+    FcDefaultSubstitute(pattern);
+
+    FcFontSet*set = FcFontSort(0, pattern, 1, 0, &result);
+    if(set) {
+       int t;
+       for(t=0;t<set->nfont;t++) {
+           FcPattern*match = set->fonts[t];
+           //FcPattern*match = FcFontMatch(0, pattern, &result);
+           if(fc_ismatch(match, family, style)) {
+               char*filename=0;
+               if(FcPatternGetString(match, "file", 0, (FcChar8**)&filename) != FcResultMatch) {
+                   msg("<debug> FontConfig: Couldn't get fontconfig's filename for font %s", name);
+                   filename=0;
+               }
+               //FcPatternDestroy(match);
+               msg("<debug> fontconfig: returning filename %s", filename);
+               free(family);
+               FcPatternDestroy(pattern);
+               FcFontSetDestroy(set);
+               return filename?strdup(filename):0;
+           }
+       }
+    }
+    free(family);
+    FcPatternDestroy(pattern);
+    FcFontSetDestroy(set);
+    return 0;
+#else
+    return 0;
+#endif
+}
+
+static DisplayFontParamKind detectFontType(const char*filename)
+{
+    if(strstr(filename, ".ttf") || strstr(filename, ".TTF"))
+       return displayFontTT;
+    if(strstr(filename, ".pfa") || strstr(filename, ".PFA") || strstr(filename, ".pfb"))
+       return displayFontT1;
+    return displayFontTT;
+}
+
 DisplayFontParam *GFXGlobalParams::getDisplayFont(GString *fontName)
 {
-    msg("<verbose> looking for font %s in global params\n", fontName->getCString());
+    msg("<verbose> looking for font %s in global params", fontName->getCString());
 
     char*name = fontName->getCString();
+    
     /* see if it is a pdf standard font */
     int t;
     for(t=0;t<sizeof(pdf2t1map)/sizeof(fontentry);t++) {
@@ -328,20 +527,60 @@ DisplayFontParam *GFXGlobalParams::getDisplayFont(GString *fontName)
            return dfp;
        }
     }
+    
+    int bestlen = 0x7fffffff;
+    const char*bestfilename = 0;
+   
+#ifndef HAVE_FONTCONFIG
+    /* if we don't have fontconfig, try a simple filename-comparison approach */
+    fontfile_t*f = global_fonts;
+    while(f) {
+       if(strstr(f->filename, name)) {
+            if(f->len < bestlen) {
+                bestlen = f->len;
+                bestfilename = f->filename;
+            }
+        }
+        f = f->next;
+    }
+#endif
+
+    /* if we didn't find anything up to now, try looking for the
+       font via fontconfig */
+    char*filename = 0;
+    if(!bestfilename) {
+       filename = fontconfig_searchForFont(name);
+    } else {
+       filename = strdup(bestfilename);
+    }
+
+    if(filename) {
+        msg("<verbose> Font %s maps to %s\n", name, filename);
+       DisplayFontParamKind kind = detectFontType(filename);
+        DisplayFontParam *dfp = new DisplayFontParam(new GString(fontName), kind);
+       if(kind == displayFontTT) {
+           dfp->tt.fileName = new GString(filename);
+       } else {
+           dfp->t1.fileName = new GString(filename);
+       }
+       free(filename);
+        return dfp;
+    }
     return GlobalParams::getDisplayFont(fontName);
 }
 
-GFXOutputDev::GFXOutputDev(parameter_t*p)
+GFXOutputDev::GFXOutputDev(InfoOutputDev*info, PDFDoc*doc)
 {
-    this->jpeginfo = 0;
-    this->textmodeinfo = 0;
-    this->linkinfo = 0;
-    this->pbminfo = 0;
+    if(!gfxglobals)
+        gfxglobals = new GFXOutputGlobals();
+
+    this->info = info;
+    this->doc = doc;
+    this->xref = doc->getXRef();
+    
     this->type3active = 0;
     this->statepos = 0;
     this->xref = 0;
-    this->substitutepos = 0;
-    this->type3Warning = 0;
     this->user_movex = 0;
     this->user_movey = 0;
     this->clipmovex = 0;
@@ -350,58 +589,48 @@ GFXOutputDev::GFXOutputDev(parameter_t*p)
     this->user_clipy1 = 0;
     this->user_clipx2 = 0;
     this->user_clipy2 = 0;
+    this->current_fontinfo = 0;
     this->current_text_stroke = 0;
     this->current_text_clip = 0;
     this->outer_clip_box = 0;
-    this->pages = 0;
-    this->pagebuflen = 0;
-    this->pagepos = 0;
-    this->config_use_fontconfig=1;
+    this->config_bigchar=0;
+    this->config_convertgradients=1;
     this->config_break_on_warning=0;
     this->config_remapunicode=0;
     this->config_transparent=0;
-    this->do_interpretType3Chars = gTrue;
-
-    this->parameters = p;
-    
-    this->gfxfontlist = gfxfontlist_create();
+    this->config_extrafontdata = 0;
+    this->config_optimize_polygons = 0;
+    this->config_multiply = 1;
+    this->page2page = 0;
+    this->num_pages = 0;
   
     memset(states, 0, sizeof(states));
-
-    /* configure device */
-    while(p) {
-        setParameter(p->name, p->value);
-       p = p->next;
-    }
 };
 
 void GFXOutputDev::setParameter(const char*key, const char*value)
 {
-    if(!strcmp(key,"rawtext")) {
-        this->do_interpretType3Chars = atoi(value)^1;
-    } else if(!strcmp(key,"breakonwarning")) {
+    if(!strcmp(key,"breakonwarning")) {
        this->config_break_on_warning = atoi(value);
-    } else if(!strcmp(key,"fontconfig")) {
-        this->config_use_fontconfig = atoi(value);
     } else if(!strcmp(key,"remapunicode")) {
         this->config_remapunicode = atoi(value);
     } else if(!strcmp(key,"transparent")) {
         this->config_transparent = atoi(value);
+    } else if(!strcmp(key,"extrafontdata")) {
+        this->config_extrafontdata = atoi(value);
+    } else if(!strcmp(key,"convertgradients")) {
+        this->config_convertgradients = atoi(value);
+    } else if(!strcmp(key,"multiply")) {
+        this->config_multiply = atoi(value);
+        if(this->config_multiply<1) 
+            this->config_multiply=1;
+    } else if(!strcmp(key,"optimize_polygons")) {
+        this->config_optimize_polygons = atoi(value);
     }
 }
   
 void GFXOutputDev::setDevice(gfxdevice_t*dev)
 {
-    parameter_t*p = this->parameters;
-
-    /* pass parameters to output device */
     this->device = dev;
-    if(this->device) {
-       while(p) {
-           this->device->setparameter(this->device, p->name, p->value);
-           p = p->next;
-       }
-    }
 }
   
 void GFXOutputDev::setMove(int x,int y)
@@ -478,39 +707,39 @@ static void dumpFontInfo(const char*loglevel, GfxFont*font)
   char* id = getFontID(font);
   char* name = getFontName(font);
   Ref* r=font->getID();
-  msg("%s=========== %s (ID:%d,%d) ==========\n", loglevel, name, r->num,r->gen);
+  msg("%s=========== %s (ID:%d,%d) ==========", loglevel, name, r->num,r->gen);
 
   GString*gstr  = font->getTag();
    
-  msg("%s| Tag: %s\n", loglevel, id);
+  msg("%s| Tag: %s", loglevel, id);
   
-  if(font->isCIDFont()) msg("%s| is CID font\n", loglevel);
+  if(font->isCIDFont()) msg("%s| is CID font", loglevel);
 
   GfxFontType type=font->getType();
   switch(type) {
     case fontUnknownType:
-     msg("%s| Type: unknown\n",loglevel);
+     msg("%s| Type: unknown",loglevel);
     break;
     case fontType1:
-     msg("%s| Type: 1\n",loglevel);
+     msg("%s| Type: 1",loglevel);
     break;
     case fontType1C:
-     msg("%s| Type: 1C\n",loglevel);
+     msg("%s| Type: 1C",loglevel);
     break;
     case fontType3:
-     msg("%s| Type: 3\n",loglevel);
+     msg("%s| Type: 3",loglevel);
     break;
     case fontTrueType:
-     msg("%s| Type: TrueType\n",loglevel);
+     msg("%s| Type: TrueType",loglevel);
     break;
     case fontCIDType0:
-     msg("%s| Type: CIDType0\n",loglevel);
+     msg("%s| Type: CIDType0",loglevel);
     break;
     case fontCIDType0C:
-     msg("%s| Type: CIDType0C\n",loglevel);
+     msg("%s| Type: CIDType0C",loglevel);
     break;
     case fontCIDType2:
-     msg("%s| Type: CIDType2\n",loglevel);
+     msg("%s| Type: CIDType2",loglevel);
     break;
   }
   
@@ -521,18 +750,18 @@ static void dumpFontInfo(const char*loglevel, GfxFont*font)
     embeddedName = font->getEmbeddedFontName()->getCString();
   }
   if(embedded)
-   msg("%s| Embedded id: %s id: %d\n",loglevel, FIXNULL(embeddedName), embRef.num);
+   msg("%s| Embedded id: %s id: %d",loglevel, FIXNULL(embeddedName), embRef.num);
 
   gstr = font->getExtFontFile();
   if(gstr)
-   msg("%s| External Font file: %s\n", loglevel, FIXNULL(gstr->getCString()));
+   msg("%s| External Font file: %s", loglevel, FIXNULL(gstr->getCString()));
 
   // Get font descriptor flags.
-  if(font->isFixedWidth()) msg("%s| is fixed width\n", loglevel);
-  if(font->isSerif()) msg("%s| is serif\n", loglevel);
-  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);
+  if(font->isFixedWidth()) msg("%s| is fixed width", loglevel);
+  if(font->isSerif()) msg("%s| is serif", loglevel);
+  if(font->isSymbolic()) msg("%s| is symbolic", loglevel);
+  if(font->isItalic()) msg("%s| is italic", loglevel);
+  if(font->isBold()) msg("%s| is bold", loglevel);
 
   free(id);
   free(name);
@@ -543,6 +772,13 @@ static void dumpFontInfo(const char*loglevel, GfxFont*font)
 
 void dump_outline(gfxline_t*line)
 {
+    /*gfxbbox_t*r = gfxline_isrectangle(line);
+    if(!r)
+        printf("is not a rectangle\n");
+    else
+        printf("is a rectangle: (%f,%f)-(%f-%f)\n", r->xmin, r->ymin, r->xmax, r->ymax);
+    */
+
     while(line) {
        if(line->type == gfx_moveTo) {
            msg("<debug> |     moveTo %.2f %.2f", line->x,line->y);
@@ -555,7 +791,32 @@ void dump_outline(gfxline_t*line)
     }
 }
 
-gfxline_t* gfxPath_to_gfxline(GfxState*state, GfxPath*path, int closed, int user_movex, int user_movey)
+void gfxPath_dump(GfxPath*path)
+{
+    int num = path->getNumSubpaths();
+    int t;
+    int cpos=0;
+    for(t = 0; t < num; t++) {
+       GfxSubpath *subpath = path->getSubpath(t);
+       int subnum = subpath->getNumPoints();
+        int s; 
+        for(s=0;s<subnum;s++) {
+          double x=subpath->getX(s);
+           double y=subpath->getY(s);
+          if(s==0 && !subpath->getCurve(s)) {
+                printf("M %f %f\n", x, y);
+           } else if(s==0 && subpath->getCurve(s)) {
+                printf("E %f %f\n", x, y);
+          } else if(subpath->getCurve(s)) {
+                printf("C %f %f\n", x, y);
+          } else {
+                printf("T %f %f\n", x, y);
+          }
+       }
+    }
+}
+
+gfxline_t* GFXOutputDev::gfxPath_to_gfxline(GfxState*state, GfxPath*path, int closed, int user_movex, int user_movey)
 {
     int num = path->getNumSubpaths();
     int s,t;
@@ -577,9 +838,7 @@ gfxline_t* gfxPath_to_gfxline(GfxState*state, GfxPath*path, int closed, int user
        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;
+          this->transformXY(state, subpath->getX(s),subpath->getY(s),&x,&y);
 
           if(s==0) {
                if(closed && needsfix && (fabs(posx-lastx)+fabs(posy-lasty))>0.001) {
@@ -625,14 +884,182 @@ gfxline_t* gfxPath_to_gfxline(GfxState*state, GfxPath*path, int closed, int user
 GBool GFXOutputDev::useTilingPatternFill()
 {
     infofeature("tiled patterns");
+//    if(config_convertgradients)
+//     return gTrue;
     return gFalse;
 }
-
 GBool GFXOutputDev::useShadedFills()
 {
     infofeature("shaded fills");
+    if(config_convertgradients)
+       return gTrue;
     return gFalse;
 }
+
+void GFXOutputDev::transformXY(GfxState*state, double x, double y, double*nx, double*ny)
+{
+    state->transform(x,y,nx,ny);
+    *nx += user_movex + clipmovex;
+    *ny += user_movey + clipmovey;
+}
+
+
+#if (xpdfMajorVersion*10000 + xpdfMinorVersion*100 + xpdfUpdateVersion) < 30207
+void GFXOutputDev::tilingPatternFill(GfxState *state, Object *str,
+                              int paintType, Dict *resDict,
+                              double *mat, double *bbox,
+                              int x0, int y0, int x1, int y1,
+                              double xStep, double yStep)
+#else
+void GFXOutputDev::tilingPatternFill(GfxState *state, Gfx *gfx, Object *str,
+                              int paintType, Dict *resDict,
+                              double *mat, double *bbox,
+                              int x0, int y0, int x1, int y1,
+                              double xStep, double yStep) 
+#endif
+{
+    msg("<debug> tilingPatternFill");
+    infofeature("tiling pattern fills");
+}
+
+GBool GFXOutputDev::functionShadedFill(GfxState *state, GfxFunctionShading *shading) 
+{
+    msg("<error> functionShadedFill not supported yet");
+    infofeature("function shaded fills");
+    return gFalse;
+}
+static gfxcolor_t col2col(GfxColorSpace*colspace, GfxColor* col)
+{
+    gfxcolor_t c;
+    GfxRGB rgb;
+    colspace->getRGB(col, &rgb);
+    c.r = colToByte(rgb.r);
+    c.g = colToByte(rgb.g);
+    c.b = colToByte(rgb.b);
+    c.a = 255;
+    return c;
+}
+
+GBool GFXOutputDev::radialShadedFill(GfxState *state, GfxRadialShading *shading)
+{
+    double x0,y0,r0,x1,y1,x2,y2,x9,y9,r1;
+    shading->getCoords(&x0,&y0,&r0,&x9,&y9,&r1);
+    x1=x0+r1;y1=y0;
+    x2=x0;   y2=y0+r1;
+    this->transformXY(state, x0,y0, &x0,&y0);
+    this->transformXY(state, x1,y1, &x1,&y1);
+    this->transformXY(state, x2,y2, &x2,&y2);
+    
+    GfxColor color0;
+    GfxColor color1;
+    GfxColor color2;
+    shading->getColor(0.0, &color0);
+    shading->getColor(0.5, &color1);
+    shading->getColor(1.0, &color2);
+  
+    GfxColorSpace* colspace = shading->getColorSpace();
+    
+    msg("<verbose> radialShadedFill %f %f %f %f %f %f %02x%02x%02x->%02x%02x%02x", x0, y0, x1, y1, x2, y2,
+           colToByte(color0.c[0]), colToByte(color0.c[1]), colToByte(color0.c[2]), 
+           colToByte(color1.c[0]), colToByte(color1.c[1]), colToByte(color1.c[2]),
+           colToByte(color2.c[0]), colToByte(color2.c[1]), colToByte(color2.c[2]));
+    infofeature("radial shaded fills");
+
+    gfxgradient_t*g = (gfxgradient_t*)malloc(sizeof(gfxgradient_t)*3);
+    g[0].next = &g[1];
+    g[1].next = &g[2];
+    g[2].next = 0;
+    g[0].color = col2col(colspace, &color0);
+    g[1].color = col2col(colspace, &color1);
+    g[2].color = col2col(colspace, &color2);
+    g[0].pos = 0.0;
+    g[1].pos = 0.5;
+    g[2].pos = 1.0;
+
+    gfxbbox_t b = states[statepos].clipbbox;
+    gfxline_t p1,p2,p3,p4,p5;
+    p1.type=gfx_moveTo;p1.x=b.xmin; p1.y=b.ymin; p1.next=&p2;
+    p2.type=gfx_lineTo;p2.x=b.xmin; p2.y=b.ymax; p2.next=&p3;
+    p3.type=gfx_lineTo;p3.x=b.xmax; p3.y=b.ymax; p3.next=&p4;
+    p4.type=gfx_lineTo;p4.x=b.xmax; p4.y=b.ymin; p4.next=&p5;
+    p5.type=gfx_lineTo;p5.x=b.xmin; p5.y=b.ymin; p5.next=0;
+    
+    gfxmatrix_t m;
+    //m.m00 = (x3-x0); m.m10 = (x1-x0);
+    //m.m01 = (y3-y0); m.m11 = (y1-y0);
+    //x3/y3 specifies another (the ending) circle, not the second radius of an ellipse
+    m.m00 = (x1-x0); m.m10 = (x2-x0);
+    m.m01 = (y1-y0); m.m11 = (y2-y0);
+    m.tx = x0 - 0.5;
+    m.ty = y0 - 0.5;
+
+    device->fillgradient(device, &p1, g, gfxgradient_radial, &m);
+    return gTrue;
+}
+
+GBool GFXOutputDev::axialShadedFill(GfxState *state, GfxAxialShading *shading)
+{
+    double x0,y0,x1,y1;
+    shading->getCoords(&x0,&y0,&x1,&y1);
+    this->transformXY(state, x0,y0,&x0,&y0);
+    this->transformXY(state, x1,y1,&x1,&y1);
+
+    GfxColor color0;
+    GfxColor color1;
+    GfxColor color2;
+    shading->getColor(0.0, &color0);
+    shading->getColor(0.5, &color1);
+    shading->getColor(1.0, &color2);
+    
+    GfxColorSpace* colspace = shading->getColorSpace();
+    
+    msg("<verbose> axialShadedFill %f %f %f %f %02x%02x%02x->%02x%02x%02x->%02x%02x%02x", x0, y0, x1, y1,
+           colToByte(color0.c[0]), colToByte(color0.c[1]), colToByte(color0.c[2]), 
+           colToByte(color1.c[0]), colToByte(color1.c[1]), colToByte(color1.c[2]),
+           colToByte(color2.c[0]), colToByte(color2.c[1]), colToByte(color2.c[2])
+           );
+    infofeature("axial shaded fills");
+
+    gfxgradient_t*g = (gfxgradient_t*)malloc(sizeof(gfxgradient_t)*3);
+    g[0].next = &g[1];
+    g[1].next = &g[2];
+    g[2].next = 0;
+    g[0].color = col2col(colspace, &color0);
+    g[1].color = col2col(colspace, &color1);
+    g[2].color = col2col(colspace, &color2);
+    g[0].pos = 0.0;
+    g[1].pos = 0.5;
+    g[2].pos = 1.0;
+    double xMin,yMin,xMax,yMax;
+    state->getUserClipBBox(&xMin, &yMin, &xMax, &yMax);
+    this->transformXY(state, xMin, yMin, &xMin, &yMin);
+    msg("<verbose> userClipBox %f %f %f %f", xMin, yMin, xMax, yMax);
+
+    xMin = 0; yMin = 0;
+    xMin = 1024; yMin = 1024;
+
+    gfxbbox_t b = states[statepos].clipbbox;
+    gfxline_t p1,p2,p3,p4,p5;
+    p1.type=gfx_moveTo;p1.x=b.xmin; p1.y=b.ymin; p1.next=&p2;
+    p2.type=gfx_lineTo;p2.x=b.xmin; p2.y=b.ymax; p2.next=&p3;
+    p3.type=gfx_lineTo;p3.x=b.xmax; p3.y=b.ymax; p3.next=&p4;
+    p4.type=gfx_lineTo;p4.x=b.xmax; p4.y=b.ymin; p4.next=&p5;
+    p5.type=gfx_lineTo;p5.x=b.xmin; p5.y=b.ymin; p5.next=0;
+   
+    /* the gradient starts at (-1.0,0.0), so move (0,0) to
+       the middle of the two control points */
+    gfxmatrix_t m;
+    m.m00 = (x1-x0)/2; m.m10 = -(y1-y0)/2;
+    m.m01 = (y1-y0)/2; m.m11 =  (x1-x0)/2;
+    m.tx = (x0 + x1)/2 - 0.5;
+    m.ty = (y0 + y1)/2 - 0.5;
+
+    device->fillgradient(device, &p1, g, gfxgradient_linear, &m);
+
+    free(g);
+    return gTrue;
+}
   
 GBool GFXOutputDev::useDrawForm() 
 { 
@@ -649,9 +1076,17 @@ GBool GFXOutputDev::needNonText()
 }
 void GFXOutputDev::endPage() 
 {
-    msg("<verbose> endPage");
+    msg("<verbose> endPage (GfxOutputDev)");
+    if(outer_clip_box) {
+       device->endclip(device);
+       outer_clip_box = 0;
+    }
+    /* notice: we're not fully done yet with this page- there might still be 
+       a few calls to drawLink() yet to come */
 }
 
+static inline double sqr(double x) {return x*x;}
+
 #define STROKE_FILL 1
 #define STROKE_CLIP 2
 void GFXOutputDev::strokeGfxline(GfxState *state, gfxline_t*line, int flags)
@@ -672,58 +1107,81 @@ void GFXOutputDev::strokeGfxline(GfxState *state, gfxline_t*line, int flags)
     col.g = colToByte(rgb.g);
     col.b = colToByte(rgb.b);
     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;
+    else msg("<error> Invalid line cap type");
 
     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);
+    else msg("<error> Invalid line join type");
 
     gfxline_t*line2 = 0;
 
-    if(dashnum && ldash) {
-       float * dash = (float*)malloc(sizeof(float)*(dashnum+1));
+    int dashLength = states[statepos].dashLength;
+    double*dashPattern = states[statepos].dashPattern;
+    double dashStart = states[statepos].dashStart;
+    if(dashLength && dashPattern) {
+       float * dash = (float*)malloc(sizeof(float)*(dashLength+1));
        int t;
-       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]);
+
+        /* try to find out how much the transformation matrix would
+           stretch the dashes, and factor that into the dash lengths.
+           This is not the entirely correct approach- it would be 
+           better to first convert the path to an unscaled version,
+           then apply dashing, and then transform the path using
+           the current transformation matrix. However there are few
+           PDFs which actually stretch a dashed path in a non-orthonormal
+           way */
+        double tx1, ty1, tx2, ty2, tx3, ty3;
+       this->transformXY(state, 0, 0, &tx1, &ty1);
+       this->transformXY(state, 0, 1, &tx2, &ty2);
+       this->transformXY(state, 1, 0, &tx3, &ty3);
+        double d1 = sqrt(sqr(tx2-tx1)+sqr(ty2-ty1));
+        double d2 = sqrt(sqr(tx3-tx1)+sqr(ty3-ty1));
+        if(fabs(d1-d2)>0.5)
+            warnfeature("non-ortogonally dashed strokes", 0);
+        double f = (d1+d2)/2;
+
+       msg("<trace> %d dashes", dashLength);
+       msg("<trace> |  phase: %f", dashStart);
+       for(t=0;t<dashLength;t++) {
+           dash[t] = (float)dashPattern[t] * f;
+            if(!dash[t]) {
+                dash[t] = 1e-37;
+            }
+           msg("<trace> |  d%-3d: %f", t, dashPattern[t]);
        }
-       dash[dashnum] = -1;
+       dash[dashLength] = -1;
        if(getLogLevel() >= LOGLEVEL_TRACE) {
            dump_outline(line);
        }
+        
+        line2 = gfxtool_dash_line(line, dash, (float)(dashStart*f));
+        line = line2;
 
-       line2 = gfxtool_dash_line(line, dash, dashphase);
-       line = line2;
        free(dash);
        msg("<trace> After dashing:");
     }
     
     if(getLogLevel() >= LOGLEVEL_TRACE)  {
-        msg("<trace> stroke width=%f join=%s cap=%s dashes=%d color=%02x%02x%02x%02x\n",
+        msg("<trace> stroke width=%f join=%s cap=%s dashes=%d color=%02x%02x%02x%02x",
                width,
                lineJoin==0?"miter": (lineJoin==1?"round":"bevel"),
-               lineCap==0?"butt": (lineJoin==1?"round":"square"),
-               dashnum,
+               lineCap==0?"butt": (lineCap==1?"round":"square"),
+               dashLength,
                col.r,col.g,col.b,col.a
                );
         dump_outline(line);
     }
 
     if(flags&STROKE_FILL) {
-        ArtSVP* svp = gfxstrokeToSVP(line, width, capType, joinType, miterLimit);
-        gfxline_t*gfxline = SVPtogfxline(svp);
+        gfxpoly_t* poly = gfxpoly_from_stroke(line, width, capType, joinType, miterLimit, DEFAULT_GRID);
+        gfxline_t*gfxline = gfxline_from_gfxpoly(poly);
        if(getLogLevel() >= LOGLEVEL_TRACE)  {
            dump_outline(gfxline);
        }
@@ -736,8 +1194,8 @@ void GFXOutputDev::strokeGfxline(GfxState *state, gfxline_t*line, int flags)
         } else {
             device->fill(device, gfxline, &col);
         }
-        free(gfxline);
-       art_svp_free(svp);
+        gfxline_free(gfxline);
+       gfxpoly_destroy(poly);
     } else {
         if(flags&STROKE_CLIP) 
             msg("<error> Stroke&clip not supported at the same time");
@@ -766,7 +1224,7 @@ void GFXOutputDev::fillGfxLine(GfxState *state, gfxline_t*line)
     gfxcolor_t col = getFillColor(state);
 
     if(getLogLevel() >= LOGLEVEL_TRACE)  {
-        msg("<trace> fill %02x%02x%02x%02x\n", col.r, col.g, col.b, col.a);
+        msg("<trace> fill %02x%02x%02x%02x", col.r, col.g, col.b, col.a);
         dump_outline(line);
     }
     device->fill(device, line, &col);
@@ -775,9 +1233,10 @@ void GFXOutputDev::fillGfxLine(GfxState *state, gfxline_t*line)
 void GFXOutputDev::clipToGfxLine(GfxState *state, gfxline_t*line) 
 {
     if(getLogLevel() >= LOGLEVEL_TRACE)  {
-        msg("<trace> clip\n");
         dump_outline(line);
     }
+    gfxbbox_t bbox = gfxline_getbbox(line);
+    gfxbbox_intersect(&states[statepos].clipbbox, &bbox);
 
     device->startclip(device, line);
     states[statepos].clipping++;
@@ -786,7 +1245,13 @@ void GFXOutputDev::clipToGfxLine(GfxState *state, gfxline_t*line)
 void GFXOutputDev::clip(GfxState *state) 
 {
     GfxPath * path = state->getPath();
+    msg("<trace> clip");
     gfxline_t*line = gfxPath_to_gfxline(state, path, 1, user_movex + clipmovex, user_movey + clipmovey);
+    if(config_optimize_polygons) {
+       gfxline_t*line2 = gfxpoly_circular_to_evenodd(line, DEFAULT_GRID);
+       gfxline_free(line);
+       line = line2;
+    }
     clipToGfxLine(state, line);
     gfxline_free(line);
 }
@@ -795,14 +1260,7 @@ void GFXOutputDev::eoClip(GfxState *state)
 {
     GfxPath * path = state->getPath();
     gfxline_t*line = gfxPath_to_gfxline(state, path, 1, user_movex + clipmovex, user_movey + clipmovey);
-
-    if(getLogLevel() >= LOGLEVEL_TRACE)  {
-        msg("<trace> eoclip\n");
-        dump_outline(line);
-    }
-
-    device->startclip(device, line);
-    states[statepos].clipping++;
+    clipToGfxLine(state, line);
     gfxline_free(line);
 }
 void GFXOutputDev::clipToStrokePath(GfxState *state)
@@ -811,7 +1269,8 @@ void GFXOutputDev::clipToStrokePath(GfxState *state)
     gfxline_t*line= gfxPath_to_gfxline(state, path, 0, user_movex + clipmovex, user_movey + clipmovey);
 
     if(getLogLevel() >= LOGLEVEL_TRACE)  {
-        msg("<trace> cliptostrokepath\n");
+        double width = state->getTransformedLineWidth();
+        msg("<trace> cliptostrokepath width=%f", width);
         dump_outline(line);
     }
 
@@ -819,14 +1278,6 @@ void GFXOutputDev::clipToStrokePath(GfxState *state)
     gfxline_free(line);
 }
 
-void GFXOutputDev::endframe()
-{
-    if(outer_clip_box) {
-       device->endclip(device);
-       outer_clip_box = 0;
-    }
-}
-
 void GFXOutputDev::finish()
 {
     if(outer_clip_box) {
@@ -840,12 +1291,6 @@ void GFXOutputDev::finish()
 GFXOutputDev::~GFXOutputDev() 
 {
     finish();
-
-    if(this->pages) {
-       free(this->pages); this->pages = 0;
-    }
-
-    gfxfontlist_free(this->gfxfontlist);
 };
 GBool GFXOutputDev::upsideDown() 
 {
@@ -859,12 +1304,6 @@ GBool GFXOutputDev::useDrawChar()
 const 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)
 {
@@ -890,6 +1329,30 @@ char* makeStringPrintable(char*str)
     tmp_printstr[len] = 0;
     return tmp_printstr;
 }
+void GFXOutputDev::updateFontMatrix(GfxState*state)
+{
+    double* ctm = state->getCTM();
+    double fontSize = state->getFontSize();
+    double*textMat = state->getTextMat();
+
+    /*  taking the absolute value of horizScaling seems to be required for
+       some italic fonts. FIXME: SplashOutputDev doesn't need this- why? */
+    double hscale = fabs(state->getHorizScaling());
+   
+    // from xpdf-3.02/SplashOutputDev:updateFont
+    double mm11 = textMat[0] * fontSize * hscale;
+    double mm12 = textMat[1] * fontSize * hscale;
+    double mm21 = textMat[2] * fontSize;
+    double mm22 = textMat[3] * fontSize;
+
+    // multiply with ctm, like state->getFontTransMat() does
+    this->current_font_matrix.m00 = (ctm[0]*mm11 + ctm[2]*mm12) / INTERNAL_FONT_SIZE;
+    this->current_font_matrix.m01 = (ctm[1]*mm11 + ctm[3]*mm12) / INTERNAL_FONT_SIZE;
+    this->current_font_matrix.m10 = (ctm[0]*mm21 + ctm[2]*mm22) / INTERNAL_FONT_SIZE;
+    this->current_font_matrix.m11 = (ctm[1]*mm21 + ctm[3]*mm22) / INTERNAL_FONT_SIZE;
+    this->current_font_matrix.tx = 0;
+    this->current_font_matrix.ty = 0;
+}
 
 void GFXOutputDev::beginString(GfxState *state, GString *s) 
 { 
@@ -897,19 +1360,7 @@ void GFXOutputDev::beginString(GfxState *state, GString *s)
     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;
-    state->getFontTransMat(&m11, &m12, &m21, &m22);
-    m11 *= state->getHorizScaling();
-    m21 *= state->getHorizScaling();
-
-    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;
 }
 
 static gfxline_t* mkEmptyGfxShape(double x, double y)
@@ -932,10 +1383,10 @@ void GFXOutputDev::drawChar(GfxState *state, double x, double y,
                        CharCode charid, int nBytes, Unicode *_u, int uLen)
 {
     if(!current_fontinfo || (unsigned)charid >= current_fontinfo->num_glyphs || !current_fontinfo->glyphs[charid]) {
-       msg("<error> Invalid charid %d for font %s", charid, current_font_id);
+       msg("<error> Invalid charid %d for font (%d characters)", charid, current_fontinfo?current_fontinfo->num_glyphs:0);
        return;
     }
-
+  
     CharCode glyphid = current_fontinfo->glyphs[charid]->glyphid;
 
     int render = state->getRender();
@@ -944,31 +1395,33 @@ void GFXOutputDev::drawChar(GfxState *state, double x, double y,
     // check for invisible text -- this is used by Acrobat Capture
     if (render == RENDER_INVISIBLE) {
        col.a = 0;
+       if(!config_extrafontdata)
+           return;
     }
 
     GfxFont*font = state->getFont();
 
-    if(font->getType() == fontType3 && do_interpretType3Chars) {
+    if(font->getType() == fontType3) {
        /* type 3 chars are passed as graphics */
        msg("<debug> type3 char at %f/%f", x, y);
        return;
     }
 
     Unicode u = uLen?(_u[0]):0;
-    msg("<debug> drawChar(%f,%f,c='%c' (%d), u=%d <%d>) CID=%d render=%d\n",x,y,(charid&127)>=32?charid:'?', charid, u, uLen, font->isCIDFont(), render);
 
     gfxmatrix_t m = this->current_font_matrix;
-    state->transform(x, y, &m.tx, &m.ty);
-    m.tx += user_movex + clipmovex;
-    m.ty += user_movey + clipmovey;
+    this->transformXY(state, x-originX, y-originY, &m.tx, &m.ty);
+    //m.tx += originX; m.ty += originY;
+    
+    msg("<debug> drawChar(%f,%f,c='%c' (%d), u=%d <%d>) CID=%d render=%d glyphid=%d font=%08x",m.tx,m.ty,(charid&127)>=32?charid:'?', charid, u, uLen, font->isCIDFont(), render, glyphid, current_gfxfont);
 
     if(render == RENDER_FILL || render == RENDER_INVISIBLE) {
        device->drawchar(device, current_gfxfont, glyphid, &col, &m);
     } else {
        msg("<debug> Drawing glyph %d as shape", charid);
-       if(!textmodeinfo) {
+       if(!gfxglobals->textmodeinfo) {
            msg("<notice> Some texts will be rendered as shape");
-           textmodeinfo = 1;
+           gfxglobals->textmodeinfo = 1;
        }
        gfxline_t*glyph = current_gfxfont->glyphs[glyphid].line;
        gfxline_t*tglyph = gfxline_clone(glyph);
@@ -1034,38 +1487,47 @@ void GFXOutputDev::endTextObject(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
+   if the function returns false, it's a new char, and type3D0 and/or type3D1 might be 
+   called with some parameters.
+   Afterwards, 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.
 
    The drawing operations between beginType3Char and endType3Char are somewhat different to
-   the normal ones. For example, the fillcolor equals the stroke color.
+   the normal ones. For example, the fillcolor equals the stroke color. (Because the stroke
+   color determines the color of a font)
 */
 
-GBool GFXOutputDev::beginType3Char(GfxState *state, double x, double y, double dx, double dy, CharCode code, Unicode *u, int uLen)
+GBool GFXOutputDev::beginType3Char(GfxState *state, double x, double y, double dx, double dy, CharCode charid, Unicode *u, int uLen)
 {
-    msg("<debug> beginType3Char %d, %08x, %d", code, *u, uLen);
+    msg("<debug> beginType3Char %d u=%d", charid, uLen?u[0]:0);
     type3active = 1;
+    
+    if(config_extrafontdata && current_fontinfo) {
+
+       gfxmatrix_t m = this->current_font_matrix;
+       this->transformXY(state, 0, 0, &m.tx, &m.ty);
+       m.m00*=INTERNAL_FONT_SIZE;
+       m.m01*=INTERNAL_FONT_SIZE;
+       m.m10*=INTERNAL_FONT_SIZE;
+       m.m11*=INTERNAL_FONT_SIZE;
+
+       if(!current_fontinfo || (unsigned)charid >= current_fontinfo->num_glyphs || !current_fontinfo->glyphs[charid]) {
+           msg("<error> Invalid charid %d for font", charid);
+           return gFalse;
+       }
+       gfxcolor_t col={0,0,0,0};
+       CharCode glyphid = current_fontinfo->glyphs[charid]->glyphid;
+       device->drawchar(device, current_gfxfont, glyphid, &col, &m);
+    }
 
-    /*int t;
-
-    gfxcolor_t col={255,0,0,0};
-    gfxmatrix_t m = {1,0,0, 0,1,0};
-
-    for(t=0;t<uLen;t++) {
-       device->drawchar(device, 0, u[t], &col, &m);
-    }*/
 
     /* the character itself is going to be passed using the draw functions */
     return gFalse; /* gTrue= is_in_cache? */
 }
 
 void GFXOutputDev::type3D0(GfxState *state, double wx, double wy) {
-    msg("<debug> type3D0 width=%f height=%f", wx, wy);
 }
 void GFXOutputDev::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 GFXOutputDev::endType3Char(GfxState *state)
@@ -1074,29 +1536,16 @@ void GFXOutputDev::endType3Char(GfxState *state)
     msg("<debug> endType3Char");
 }
 
-void GFXOutputDev::startFrame(int width, int height) 
-{
-    if(outer_clip_box) {
-       device->endclip(device);
-       outer_clip_box = 0;
-    }
-
-    device->startpage(device, width, height);
-    this->width = width;
-    this->height = height;
-}
-
 void GFXOutputDev::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;
+    gfxcolor_t white = {255,255,255,255};
+    gfxcolor_t black = {255,0,0,0};
     laststate = state;
     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);
     Use CropBox, not MediaBox, as page size
@@ -1122,13 +1571,18 @@ void GFXOutputDev::startPage(int pageNum, GfxState *state, double crop_x1, doubl
         /*if(user_clipy1 > y1)*/ y1 = user_clipy1;
         /*if(user_clipy2 < y2)*/ y2 = user_clipy2;
        msg("<verbose> Using user clip box %f/%f/%f/%f",x1,y1,x2,y2);
+    } else {
+        x1 += this->clipmovex;
+        y1 += this->clipmovey;
+        x2 += this->clipmovex;
+        y2 += this->clipmovey;
     }
 
     //msg("<verbose> Bounding box is (%f,%f)-(%f,%f) [shifted by %d/%d]", x1,y1,x2,y2, user_movex, user_movey);
     
     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 + clipmovex, user_movey + clipmovey);
     if(rot!=0)
-        msg("<verbose> page is rotated %d degrees\n", rot);
+        msg("<verbose> page is rotated %d degrees", 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];
@@ -1136,18 +1590,27 @@ void GFXOutputDev::startPage(int pageNum, GfxState *state, double crop_x1, doubl
     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;
     device->startclip(device, clippath); outer_clip_box = 1;
-    if(!config_transparent)
+    if(!config_transparent) {
         device->fill(device, clippath, &white);
+    }
+    states[statepos].clipbbox.xmin = x1;
+    states[statepos].clipbbox.ymin = x1;
+    states[statepos].clipbbox.xmax = x2;
+    states[statepos].clipbbox.ymax = y2;
+    
+    states[statepos].dashPattern = 0;
+    states[statepos].dashLength = 0;
+    states[statepos].dashStart = 0;
 }
 
 
 void GFXOutputDev::processLink(Link *link, Catalog *catalog)
 {
-    double x1, y1, x2, y2, w;
+    double x1, y1, x2, y2;
     gfxline_t points[5];
     int x, y;
     
-    msg("<debug> drawlink\n");
+    msg("<debug> drawlink");
 
     link->getRect(&x1, &y1, &x2, &y2);
     cvtUserToDev(x1, y1, &x, &y);
@@ -1176,11 +1639,15 @@ void GFXOutputDev::processLink(Link *link, Catalog *catalog)
     points[4].y = y + user_movey + clipmovey;
     points[4].next = 0;
     
-    msg("<trace> drawlink %.2f/%.2f %.2f/%.2f %.2f/%.2f %.2f/%.2f\n",
+    msg("<trace> drawlink %.2f/%.2f %.2f/%.2f %.2f/%.2f %.2f/%.2f",
            points[0].x, points[0].y,
            points[1].x, points[1].y,
            points[2].x, points[2].y,
            points[3].x, points[3].y); 
+    
+    if(getLogLevel() >= LOGLEVEL_TRACE)  {
+       dump_outline(points);
+    }
 
     LinkAction*action=link->getAction();
     char buf[128];
@@ -1188,7 +1655,7 @@ void GFXOutputDev::processLink(Link *link, Catalog *catalog)
     const char*type = "-?-";
     char*named = 0;
     int page = -1;
-    msg("<trace> drawlink action=%d\n", action->getKind());
+    msg("<trace> drawlink action=%d", action->getKind());
     switch(action->getKind())
     {
         case actionGoTo: {
@@ -1197,7 +1664,8 @@ void GFXOutputDev::processLink(Link *link, Catalog *catalog)
             LinkDest *dest=NULL;
             if (ha->getDest()==NULL) 
                 dest=catalog->findDest(ha->getNamedDest());
-            else dest=ha->getDest();
+            else 
+                dest=ha->getDest()->copy();
             if (dest){ 
               if (dest->isPageRef()){
                 Ref pageref=dest->getPageRef();
@@ -1206,6 +1674,7 @@ void GFXOutputDev::processLink(Link *link, Catalog *catalog)
               else  page=dest->getPageNum();
               sprintf(buf, "%d", page);
               s = strdup(buf);
+              delete dest;
             }
         }
         break;
@@ -1244,8 +1713,9 @@ void GFXOutputDev::processLink(Link *link, Catalog *catalog)
                     }
                     else if(strstr(s, "last") || strstr(s, "end"))
                     {
-                       if(pages && pagepos>0)
-                           page = pages[pagepos-1];
+                        if(this->page2page && this->num_pages) {
+                           page = this->page2page[this->num_pages-1];
+                        }
                     }
                     else if(strstr(s, "first") || strstr(s, "top"))
                     {
@@ -1284,34 +1754,34 @@ void GFXOutputDev::processLink(Link *link, Catalog *catalog)
         }
         break;
         default: {
-            msg("<error> Unknown link type!\n");
+            msg("<error> Unknown link type!");
             break;
         }
     }
 
     if(!s) s = strdup("-?-");
     
-    msg("<trace> drawlink s=%s\n", s);
+    msg("<trace> drawlink s=%s", s);
 
-    if(!linkinfo && (page || s))
+    if(!gfxglobals->linkinfo && (page || s))
     {
         msg("<notice> File contains links");
-        linkinfo = 1;
+        gfxglobals->linkinfo = 1;
     }
     
-    if(page>0)
-    {
+    if(page>0) {
         int t;
        int lpage = -1;
-        for(t=1;t<=pagepos;t++) {
-            if(pages[t]==page) {
+        for(t=1;t<=this->num_pages;t++) {
+            if(this->page2page[t]==page) {
                lpage = t;
                 break;
            }
        }
         if(lpage<0) {
            lpage = page;
-       }
+        }
+
        char buf[80];
        sprintf(buf, "page%d", lpage);
        device->drawlink(device, points, buf);
@@ -1321,49 +1791,100 @@ void GFXOutputDev::processLink(Link *link, Catalog *catalog)
         device->drawlink(device, points, s);
     }
 
-    msg("<verbose> \"%s\" link to \"%s\" (%d)\n", type, FIXNULL(s), page);
+    msg("<verbose> \"%s\" link to \"%s\" (%d)", type, FIXNULL(s), page);
     free(s);s=0;
 }
 
 void GFXOutputDev::saveState(GfxState *state) {
-    dbg("saveState");dbgindent+=2;
+    dbg("saveState %08x", state); dbgindent+=2;
 
-    msg("<trace> saveState\n");
+    msg("<trace> saveState %08x", state);
     updateAll(state);
     if(statepos>=64) {
-      msg("<error> Too many nested states in pdf.");
-      return;
+      msg("<fatal> Too many nested states in pdf.");
+      exit(1);
     }
     statepos ++;
+    states[statepos].state = state;
     states[statepos].createsoftmask = states[statepos-1].createsoftmask;
     states[statepos].transparencygroup = states[statepos-1].transparencygroup;
     states[statepos].clipping = 0;
+    states[statepos].olddevice = 0;
+    states[statepos].clipbbox = states[statepos-1].clipbbox;
+
+    states[statepos].dashPattern = states[statepos-1].dashPattern;
+    states[statepos].dashStart = states[statepos-1].dashStart;
+    states[statepos].dashLength = states[statepos-1].dashLength;
 };
 
 void GFXOutputDev::restoreState(GfxState *state) {
-  dbgindent-=2; dbg("restoreState");
+  dbgindent-=2; dbg("restoreState %08x", state);
 
   if(statepos==0) {
-      msg("<error> Invalid restoreState");
-      return;
+      msg("<fatal> Invalid restoreState");
+      exit(1);
   }
-  msg("<trace> restoreState%s%s", states[statepos].softmask?" (end softmask)":"",
+  msg("<trace> restoreState %08x%s%s", state,
+                                 states[statepos].softmask?" (end softmask)":"",
                                  states[statepos].clipping?" (end clipping)":"");
   if(states[statepos].softmask) {
       clearSoftMask(state);
   }
+
+  if(states[statepos].dashPattern) {
+      if(!statepos || states[statepos-1].dashPattern != states[statepos].dashPattern) {
+          free(states[statepos].dashPattern);
+          states[statepos].dashPattern = 0;
+      }
+  }
+
   updateAll(state);
+  
   while(states[statepos].clipping) {
       device->endclip(device);
       states[statepos].clipping--;
   }
+  if(states[statepos].state!=state) {
+      msg("<fatal> bad state nesting");
+      exit(1);
+  }
+  states[statepos].state=0;
   statepos--;
 }
+void GFXOutputDev::updateLineDash(GfxState *state) 
+{
+    if(states[statepos].dashPattern &&
+       (!statepos || states[statepos-1].dashPattern != states[statepos].dashPattern)) {
+        free(states[statepos].dashPattern);
+        states[statepos].dashPattern = 0;
+    }
+    double *pattern = 0;
+    int dashLength;
+    double dashStart;
+    state->getLineDash(&pattern, &dashLength, &dashStart);
+    msg("<debug> updateLineDash, %d dashes", dashLength);
+    if(!dashLength) {
+        states[statepos].dashPattern = 0;
+        states[statepos].dashLength = 0;
+    } else {
+        double*p = (double*)malloc(dashLength*sizeof(states[statepos].dashPattern[0]));
+        memcpy(p, pattern, dashLength*sizeof(states[statepos].dashPattern[0]));
+        states[statepos].dashPattern = p;
+        states[statepos].dashLength = dashLength;
+        states[statepos].dashStart = dashStart;
+    }
+}
+  
+void GFXOutputDev::setPageMap(int*page2page, int num_pages)
+{
+    this->page2page = page2page;
+    this->num_pages = num_pages;
+}
 
 void GFXOutputDev::updateLineWidth(GfxState *state)
 {
     double width = state->getTransformedLineWidth();
-    //swfoutput_setlinewidth(&device, width);
 }
 
 void GFXOutputDev::updateLineCap(GfxState *state)
@@ -1417,82 +1938,6 @@ void GFXOutputDev::updateStrokeColor(GfxState *state)
     state->getStrokeRGB(&rgb);
 }
 
-void GFXOutputDev::setXRef(PDFDoc*doc, XRef *xref) 
-{
-    this->doc = doc;
-    this->xref = xref;
-}
-
-gfxfont_t* createGfxFont(GfxFont*xpdffont, FontInfo*src)
-{
-    gfxfont_t*font = (gfxfont_t*)malloc(sizeof(gfxfont_t));
-    memset(font, 0, sizeof(gfxfont_t));
-
-    font->glyphs = (gfxglyph_t*)malloc(sizeof(gfxglyph_t)*src->num_glyphs);
-    memset(font->glyphs, 0, sizeof(gfxglyph_t)*src->num_glyphs);
-    font->id = strdup(getFontName(xpdffont));
-    int t;
-    double quality = (1024 * 0.05) / src->max_size;
-    double scale = 1;
-    //printf("%d glyphs\n", font->num_glyphs);
-    font->num_glyphs = 0;
-    for(t=0;t<src->num_glyphs;t++) {
-       if(src->glyphs[t]) {
-           SplashPath*path = src->glyphs[t]->path;
-           int len = path?path->getLength():0;
-           //printf("glyph %d) %08x (%d line segments)\n", t, path, len);
-           gfxglyph_t*glyph = &font->glyphs[font->num_glyphs];
-           src->glyphs[t]->glyphid = font->num_glyphs;
-           glyph->unicode = src->glyphs[t]->unicode;
-           if(glyph->unicode >= font->max_unicode)
-               font->max_unicode = glyph->unicode+1;
-           gfxdrawer_t drawer;
-           gfxdrawer_target_gfxline(&drawer);
-           int s;
-           int count = 0;
-           double xmax = 0;
-           for(s=0;s<len;s++) {
-               Guchar f;
-               double x, y;
-               path->getPoint(s, &x, &y, &f);
-               if(x > xmax)
-                   xmax = x;
-               if(f&splashPathFirst) {
-                   drawer.moveTo(&drawer, x*scale, y*scale);
-               }
-               if(f&splashPathCurve) {
-                   double x2,y2;
-                   path->getPoint(++s, &x2, &y2, &f);
-                   if(f&splashPathCurve) {
-                       double x3,y3;
-                       path->getPoint(++s, &x3, &y3, &f);
-                       gfxdraw_cubicTo(&drawer, x*scale, y*scale, x2*scale, y2*scale, x3*scale, y3*scale, quality);
-                   } else {
-                       drawer.splineTo(&drawer, x*scale, y*scale, x2*scale, y2*scale);
-                   }
-               } else {
-                   drawer.lineTo(&drawer, x*scale, y*scale);
-               }
-            //   printf("%f %f %s %s\n", x, y, (f&splashPathCurve)?"curve":"",
-            //                           (f&splashPathFirst)?"first":"",
-            //                           (f&splashPathLast)?"last":"");
-           }
-           glyph->line = (gfxline_t*)drawer.result(&drawer);
-           glyph->advance = xmax*scale; // we don't know the real advance value, so this'll have to do
-           font->num_glyphs++;
-       }
-    }
-    font->unicode2glyph = (int*)malloc(sizeof(int)*font->max_unicode);
-    memset(font->unicode2glyph, -1, sizeof(int)*font->max_unicode);
-    for(t=0;t<font->num_glyphs;t++) {
-       if(font->glyphs[t].unicode>0 && font->glyphs[t].unicode<font->max_unicode) {
-           font->unicode2glyph[font->glyphs[t].unicode] = t;
-       }
-
-    }
-    return font;
-}
-
 void GFXOutputDev::updateFont(GfxState *state) 
 {
     GfxFont* gfxFont = state->getFont();
@@ -1500,36 +1945,47 @@ void GFXOutputDev::updateFont(GfxState *state)
        return; 
     }  
     char*id = getFontID(gfxFont);
+    msg("<verbose> Updating font to %s", id);
+    if(gfxFont->getType() == fontType3) {
+       infofeature("Type3 fonts");
+       if(!config_extrafontdata) {
+           return;
+       }
+    }
     if(!id) {
        msg("<error> Internal Error: FontID is null");
        return; 
     }
 
     this->current_fontinfo = this->info->getFont(id);
-    
-    gfxfont_t*font = gfxfontlist_findfont(this->gfxfontlist,id);
-    if(!font) {
-       font = createGfxFont(gfxFont, current_fontinfo);
-       gfxfontlist_addfont(this->gfxfontlist, font);
-       device->addfont(device, font);
+
+    if(!this->current_fontinfo) {
+       msg("<error> Internal Error: no fontinfo for font %s", id);
+       return;
+    }
+    if(!this->current_fontinfo->seen) {
+       dumpFontInfo("<verbose>", gfxFont);
     }
-    current_gfxfont = font;
+
+    current_gfxfont = this->current_fontinfo->getGfxFont();
+    device->addfont(device, current_gfxfont);
     free(id);
+
+    updateFontMatrix(state);
 }
 
 #define SQR(x) ((x)*(x))
 
 unsigned char* antialize(unsigned char*data, int width, int height, int newwidth, int newheight, int palettesize)
 {
-    if((newwidth<2 || newheight<2) ||
+    if((newwidth<1 || newheight<1) ||
        (width<=newwidth || height<=newheight))
        return 0;
     unsigned char*newdata;
     int x,y;
     newdata= (unsigned char*)malloc(newwidth*newheight);
-    int t;
-    double fx = (double)(width)/newwidth;
-    double fy = (double)(height)/newheight;
+    double fx = ((double)width)/newwidth;
+    double fy = ((double)height)/newheight;
     double px = 0;
     int blocksize = (int)(8192/(fx*fy));
     int r = 8192*256/palettesize;
@@ -1537,17 +1993,21 @@ unsigned char* antialize(unsigned char*data, int width, int height, int newwidth
        double ex = px + fx;
        int fromx = (int)px;
        int tox = (int)ex;
-       int xweight1 = (int)(((fromx+1)-px)*256);
+       int xweight1 = (int)((1-(px-fromx))*256);
        int xweight2 = (int)((ex-tox)*256);
        double py =0;
        for(y=0;y<newheight;y++) {
            double ey = py + fy;
            int fromy = (int)py;
            int toy = (int)ey;
-           int yweight1 = (int)(((fromy+1)-py)*256);
+           int yweight1 = (int)((1-(py-fromy))*256);
            int yweight2 = (int)((ey-toy)*256);
            int a = 0;
            int xx,yy;
+            if(tox>=width) 
+                tox = width-1;
+            if(toy>=height) 
+                toy = height-1;
            for(xx=fromx;xx<=tox;xx++)
            for(yy=fromy;yy<=toy;yy++) {
                int b = 1-data[width*yy+xx];
@@ -1574,7 +2034,7 @@ 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)
+        double x4,double y4, int type, int multiply)
 {
     gfxcolor_t*newpic=0;
     
@@ -1599,15 +2059,13 @@ static void drawimage(gfxdevice_t*dev, gfxcolor_t* data, int sizex,int sizey,
      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;
+        
+    m.tx = p1.x - 0.5*multiply;
+    m.ty = p1.y - 0.5*multiply;
 
     gfximage_t img;
     img.data = (gfxcolor_t*)data;
@@ -1618,19 +2076,20 @@ static void drawimage(gfxdevice_t*dev, gfxcolor_t* data, int sizex,int sizey,
        /* TODO: pass image_dpi to device instead */
        dev->setparameter(dev, "next_bitmap_is_jpeg", "1");
 
+    dump_outline(&p1);
     dev->fillbitmap(dev, &p1, &img, &m, 0);
 }
 
 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)
+        double x1,double y1, double x2,double y2, double x3,double y3, double x4,double y4, int multiply)
 {
-    drawimage(dev,mem,sizex,sizey,x1,y1,x2,y2,x3,y3,x4,y4, IMAGE_TYPE_JPEG);
+    drawimage(dev,mem,sizex,sizey,x1,y1,x2,y2,x3,y3,x4,y4, IMAGE_TYPE_JPEG, multiply);
 }
 
 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)
+        double x1,double y1, double x2,double y2, double x3,double y3, double x4,double y4, int multiply)
 {
-    drawimage(dev,mem,sizex,sizey,x1,y1,x2,y2,x3,y3,x4,y4, IMAGE_TYPE_LOSSLESS);
+    drawimage(dev,mem,sizex,sizey,x1,y1,x2,y2,x3,y3,x4,y4, IMAGE_TYPE_LOSSLESS, multiply);
 }
 
 
@@ -1639,6 +2098,8 @@ void GFXOutputDev::drawGeneralImage(GfxState *state, Object *ref, Stream *str,
                                   GBool inlineImg, int mask, int*maskColors,
                                   Stream *maskStr, int maskWidth, int maskHeight, GBool maskInvert, GfxImageColorMap*maskColorMap)
 {
+  /* the code in this function is *old*. It's not pretty, but it works. */
+
   double x1,y1,x2,y2,x3,y3,x4,y4;
   ImageStream *imgStr;
   Guchar pixBuf[4];
@@ -1693,7 +2154,7 @@ void GFXOutputDev::drawGeneralImage(GfxState *state, Object *ref, Stream *str,
   imgStr = new ImageStream(str, width, ncomps,bits);
   imgStr->reset();
 
-  if(!width || !height || (height<=1 && width<=1))
+  if(!width || !height || ((height+width)<=1 && (maskWidth+maskHeight)<=1))
   {
       msg("<verbose> Ignoring %d by %d image", width, height);
       unsigned char buf[8];
@@ -1708,26 +2169,35 @@ void GFXOutputDev::drawGeneralImage(GfxState *state, Object *ref, Stream *str,
       return;
   }
 
-  state->transform(0, 1, &x1, &y1); x1 += user_movex + clipmovex; y1 += user_movey + clipmovey;
-  state->transform(0, 0, &x2, &y2); x2 += user_movex + clipmovex; y2 += user_movey + clipmovey;
-  state->transform(1, 0, &x3, &y3); x3 += user_movex + clipmovex; y3 += user_movey + clipmovey;
-  state->transform(1, 1, &x4, &y4); x4 += user_movex + clipmovex; y4 += user_movey + clipmovey;
+  this->transformXY(state, 0, 1, &x1, &y1);
+  this->transformXY(state, 0, 0, &x2, &y2);
+  this->transformXY(state, 1, 0, &x3, &y3);
+  this->transformXY(state, 1, 1, &x4, &y4);
+
+  if(type3active) {
+      /* as type 3 bitmaps are antialized, we need to place them
+        at integer coordinates, otherwise flash player's antializing
+        will kick in and make everything blurry */
+      x1 = (int)(x1);y1 = (int)(y1);
+      x2 = (int)(x2);y2 = (int)(y2);
+      x3 = (int)(x3);y3 = (int)(y3);
+      x4 = (int)(x4);y4 = (int)(y4);
+  }
 
-  if(!pbminfo && !(str->getKind()==strDCT)) {
+  if(!gfxglobals->pbminfo && !(str->getKind()==strDCT)) {
       if(!type3active) {
-         msg("<notice> file contains pbm pictures %s",mask?"(masked)":"");
-         pbminfo = 1;
+         msg("<notice> File contains pbm pictures %s",mask?"(masked)":"");
+         gfxglobals->pbminfo = 1;
       }
       if(mask)
-      msg("<verbose> drawing %d by %d masked picture\n", width, height);
+      msg("<verbose> drawing %d by %d masked picture", width, height);
   }
-  if(!jpeginfo && (str->getKind()==strDCT)) {
-      msg("<notice> file contains jpeg pictures");
-      jpeginfo = 1;
+  if(!gfxglobals->jpeginfo && (str->getKind()==strDCT)) {
+      msg("<notice> File contains jpeg pictures");
+      gfxglobals->jpeginfo = 1;
   }
 
   if(mask) {
-      int i,j;
       unsigned char buf[8];
       int x,y;
       unsigned char*pic = new unsigned char[width*height];
@@ -1752,11 +2222,7 @@ void GFXOutputDev::drawGeneralImage(GfxState *state, Object *ref, Stream *str,
                buf[0]=1-buf[0];
            pic[width*y+x] = buf[0];
       }
-      
-      /* 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): */
-      int t,found = -1;
+     
       if(type3active) {
          unsigned char*pic2 = 0;
          numpalette = 16;
@@ -1764,19 +2230,19 @@ void GFXOutputDev::drawGeneralImage(GfxState *state, Object *ref, Stream *str,
          pic2 = antialize(pic,width,height,realwidth,realheight,numpalette);
 
          if(!pic2) {
-           delete pic;
+           delete[] pic;
            delete imgStr;
            return;
          }
-
+          
          width = realwidth;
          height = realheight;
-         free(pic);
+         delete[] pic;
          pic = pic2;
          
          /* make a black/white palette */
 
-         float r = 255/(numpalette-1);
+         float r = 255./(float)(numpalette-1);
          int t;
          for(t=0;t<numpalette;t++) {
              pal[t].r = colToByte(rgb.r);
@@ -1784,6 +2250,7 @@ void GFXOutputDev::drawGeneralImage(GfxState *state, Object *ref, Stream *str,
              pal[t].b = colToByte(rgb.b);
              pal[t].a = (unsigned char)(t*r);
          }
+          
       }
 
       gfxcolor_t*pic2 = new gfxcolor_t[width*height];
@@ -1792,9 +2259,9 @@ void GFXOutputDev::drawGeneralImage(GfxState *state, Object *ref, Stream *str,
          pic2[width*y+x] = pal[pic[y*width+x]];
        }
       }
-      drawimagelossless(device, pic2, width, height, x1,y1,x2,y2,x3,y3,x4,y4);
-      free(pic2);
-      free(pic);
+      drawimagelossless(device, pic2, width, height, x1,y1,x2,y2,x3,y3,x4,y4, config_multiply);
+      delete[] pic2;
+      delete[] pic;
       delete imgStr;
       if(maskbitmap) free(maskbitmap);
       return;
@@ -1813,15 +2280,31 @@ void GFXOutputDev::drawGeneralImage(GfxState *state, Object *ref, Stream *str,
          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)];
+              int x1 = x*maskWidth/width;
+              int y1 = y*maskHeight/height;
+              int x2 = (x+1)*maskWidth/width;
+              int y2 = (y+1)*maskHeight/height;
+              int xx,yy;
+              unsigned int alpha=0;
+              unsigned int count=0;
+              for(xx=x1;xx<x2;xx++)
+              for(yy=y1;yy<y2;yy++) {
+                  alpha += maskbitmap[yy*maskWidth+xx];
+                  count ++;
+              }
+              if(count) {
+                pic[width*y+x].a = alpha / count;
+              } else {
+                pic[width*y+x].a = maskbitmap[y1*maskWidth+x1];
+              }
          }
        }
       }
       if(str->getKind()==strDCT)
-         drawimagejpeg(device, pic, width, height, x1,y1,x2,y2,x3,y3,x4,y4);
+         drawimagejpeg(device, pic, width, height, x1,y1,x2,y2,x3,y3,x4,y4, config_multiply);
       else
-         drawimagelossless(device, pic, width, height, x1,y1,x2,y2,x3,y3,x4,y4);
-      delete pic;
+         drawimagelossless(device, pic, width, height, x1,y1,x2,y2,x3,y3,x4,y4, config_multiply);
+      delete[] pic;
       delete imgStr;
       if(maskbitmap) free(maskbitmap);
       return;
@@ -1865,14 +2348,39 @@ void GFXOutputDev::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(device, pic, width, height, x1,y1,x2,y2,x3,y3,x4,y4);
+      if(maskbitmap) {
+         if(maskWidth < width && maskHeight < height) {
+             for(y = 0; y < height; y++) {
+                 for (x = 0; x < width; x++) {
+                     pic[width*y+x].a = maskbitmap[(y*maskHeight/height)*maskWidth+(x*maskWidth/width)];
+                 }
+             }
+         } else {
+             msg("<verbose> resampling %dx%d to mask size (%dx%d)", width, height, maskWidth, maskHeight);
+             gfxcolor_t*newpic=new gfxcolor_t[maskWidth*maskHeight];
+             double dx = width / maskWidth;
+             double dy = height / maskHeight;
+             double yy = 0;
+             for(y = 0; y < maskHeight; y++) {
+                 double xx = 0;
+                 for (x = 0; x < maskWidth; x++) {
+                     newpic[maskWidth*y+x] = pic[int(yy)*width+int(xx)];
+                     newpic[maskWidth*y+x].a = maskbitmap[maskWidth*y+x];
+                     xx += dx;
+                 }
+                 yy += dy;
+             }
+             delete[] pic;
+             pic = newpic;
+             width = maskWidth;
+             height = maskHeight;
+         }
+      }
+      drawimagelossless(device, pic, width, height, x1,y1,x2,y2,x3,y3,x4,y4, config_multiply);
 
-      delete pic;
+      delete[] pic;
       delete imgStr;
       if(maskbitmap) free(maskbitmap);
       return;
@@ -1901,7 +2409,7 @@ void GFXOutputDev::drawImage(GfxState *state, Object *ref, Stream *str,
            maskColors?"maskColors":"no maskColors",
            inlineImg);
     if(colorMap)
-       msg("<verbose> colorMap pixcomps:%d bits:%d mode:%d\n", colorMap->getNumPixelComps(),
+       msg("<verbose> colorMap pixcomps:%d bits:%d mode:%d", colorMap->getNumPixelComps(),
                colorMap->getBits(),colorMap->getColorSpace()->getMode());
     drawGeneralImage(state,ref,str,width,height,colorMap,0,inlineImg,0,maskColors, 0,0,0,0, 0);
 }
@@ -1919,7 +2427,7 @@ void GFXOutputDev::drawMaskedImage(GfxState *state, Object *ref, Stream *str,
            colorMap?"colorMap":"no colorMap", 
            maskWidth, maskHeight);
     if(colorMap)
-       msg("<verbose> colorMap pixcomps:%d bits:%d mode:%d\n", colorMap->getNumPixelComps(),
+       msg("<verbose> colorMap pixcomps:%d bits:%d mode:%d", colorMap->getNumPixelComps(),
                colorMap->getBits(),colorMap->getColorSpace()->getMode());
     drawGeneralImage(state,ref,str,width,height,colorMap,0,0,0,0, maskStr, maskWidth, maskHeight, maskInvert, 0);
 }
@@ -1938,7 +2446,7 @@ void GFXOutputDev::drawSoftMaskedImage(GfxState *state, Object *ref, Stream *str
            colorMap?"colorMap":"no colorMap", 
            maskWidth, maskHeight);
     if(colorMap)
-       msg("<verbose> colorMap pixcomps:%d bits:%d mode:%d\n", colorMap->getNumPixelComps(),
+       msg("<verbose> colorMap pixcomps:%d bits:%d mode:%d", colorMap->getNumPixelComps(),
                colorMap->getBits(),colorMap->getColorSpace()->getMode());
     drawGeneralImage(state,ref,str,width,height,colorMap,0,0,0,0, maskStr, maskWidth, maskHeight, 0, maskColorMap);
 }
@@ -1960,6 +2468,11 @@ void GFXOutputDev::fill(GfxState *state)
 
     GfxPath * path = state->getPath();
     gfxline_t*line= gfxPath_to_gfxline(state, path, 1, user_movex + clipmovex, user_movey + clipmovey);
+    if(config_optimize_polygons) {
+        gfxline_t*line2 = gfxpoly_circular_to_evenodd(line, DEFAULT_GRID);
+        gfxline_free(line);
+        line = line2;
+    }
     fillGfxLine(state, line);
     gfxline_free(line);
 }
@@ -1987,14 +2500,25 @@ static const char* dirseparator()
 
 void addGlobalFont(const char*filename)
 {
-    fontfile_t f;
-    memset(&f, 0, sizeof(fontfile_t));
-    f.filename = filename;
-    if(fontnum < sizeof(fonts)/sizeof(fonts[0])) {
-       msg("<verbose> Adding font \"%s\".", filename);
-       fonts[fontnum++] = f;
+    fontfile_t* f = (fontfile_t*)malloc(sizeof(fontfile_t));
+    memset(f, 0, sizeof(fontfile_t));
+    f->filename = filename;
+    int len = strlen(filename);
+    char*r1 = strrchr(filename, '/');
+    char*r2 = strrchr(filename, '\\');
+    if(r2>r1)
+        r1 = r2;
+    if(r1) {
+        len = strlen(r1+1);
+    }
+    f->len = len;
+
+    msg("<verbose> Adding font \"%s\".", filename);
+    if(global_fonts_next) {
+        global_fonts_next->next = f;
+        global_fonts_next = global_fonts_next->next;
     } else {
-       msg("<error> Too many external fonts. Not adding font file \"%s\".", filename);
+        global_fonts_next = global_fonts = f;
     }
 }
 
@@ -2002,7 +2526,6 @@ void addGlobalLanguageDir(const char*dir)
 {
     msg("<notice> Adding %s to language pack directories", dir);
 
-    int l;
     FILE*fi = 0;
     char* config_file = (char*)malloc(strlen(dir) + 1 + sizeof("add-to-xpdfrc") + 1);
     strcpy(config_file, dir);
@@ -2021,14 +2544,13 @@ void addGlobalLanguageDir(const char*dir)
 void addGlobalFontDir(const 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);
+       msg("<warning> Couldn't open directory %s", dirname);
        return;
     }
     struct dirent*ent;
+    int fonts = 0;
     while(1) {
        ent = readdir (dir);
        if (!ent) 
@@ -2046,146 +2568,43 @@ void addGlobalFontDir(const char*dirname)
            type=3;
        if(!strncasecmp(&name[l-4], ".ttf", 4)) 
            type=2;
-       if(type)
-       {
+       if(type) {
            char*fontname = (char*)malloc(strlen(dirname)+strlen(name)+2);
            strcpy(fontname, dirname);
             strcat(fontname, dirseparator());
            strcat(fontname, name);
            addGlobalFont(fontname);
+            fonts++;
        }
     }
+    msg("<notice> Added %s to font directories (%d fonts)", dirname, fonts);
     closedir(dir);
 #else
-    msg("<warning> No dirent.h- unable to add font dir %s", dir);
+    msg("<warning> No dirent.h");
 #endif
 }
 
-void GFXOutputDev::preparePage(int pdfpage, int outputpage)
-{
-    if(pdfpage < 0)
-       return;
-
-    if(!this->pages) {
-       this->pagebuflen = 1024;
-       this->pages = (int*)malloc(this->pagebuflen*sizeof(int));
-       memset(this->pages, -1, this->pagebuflen*sizeof(int));
-    } else {
-       while(pdfpage >= this->pagebuflen)
-       {
-           int oldlen = this->pagebuflen;
-           this->pagebuflen+=1024;
-           this->pages = (int*)realloc(this->pages, this->pagebuflen*sizeof(int));
-           memset(&this->pages[oldlen], -1, (this->pagebuflen-oldlen)*sizeof(int));
-       }
-    }
-    this->pages[pdfpage] = outputpage;
-    if(pdfpage>this->pagepos)
-       this->pagepos = pdfpage;
-}
-
-struct BBox
-{
-    double posx,posy;
-    double width,height;
-};
-
-BBox mkBBox(GfxState*state, double*bbox, double width, double height)
-{
-    double xMin, yMin, xMax, yMax, x, y;
-    double tx, ty, w, h;
-    // transform the bbox
-    state->transform(bbox[0], bbox[1], &x, &y);
-    xMin = xMax = x;
-    yMin = yMax = y;
-    state->transform(bbox[0], bbox[3], &x, &y);
-    if (x < xMin) {
-      xMin = x;
-    } else if (x > xMax) {
-      xMax = x;
-    }
-    if (y < yMin) {
-      yMin = y;
-    } else if (y > yMax) {
-      yMax = y;
-    }
-    state->transform(bbox[2], bbox[1], &x, &y);
-    if (x < xMin) {
-      xMin = x;
-    } else if (x > xMax) {
-      xMax = x;
-    }
-    if (y < yMin) {
-      yMin = y;
-    } else if (y > yMax) {
-      yMax = y;
-    }
-    state->transform(bbox[2], bbox[3], &x, &y);
-    if (x < xMin) {
-      xMin = x;
-    } else if (x > xMax) {
-      xMax = x;
-    }
-    if (y < yMin) {
-      yMin = y;
-    } else if (y > yMax) {
-      yMax = y;
-    }
-    tx = (int)floor(xMin);
-    if (tx < 0) {
-      tx = 0;
-    } else if (tx > width) {
-      tx = width;
-    }
-    ty = (int)floor(yMin);
-    if (ty < 0) {
-      ty = 0;
-    } else if (ty > height) {
-      ty = height;
-    }
-    w = (int)ceil(xMax) - tx + 1;
-    if (tx + w > width) {
-      w = width - tx;
-    }
-    if (w < 1) {
-      w = 1;
-    }
-    h = (int)ceil(yMax) - ty + 1;
-    if (ty + h > height) {
-      h = height - ty;
-    }
-    if (h < 1) {
-      h = 1;
-    }
-    BBox nbbox;
-    nbbox.posx = xMin;
-    nbbox.posx = yMin;
-    nbbox.width = w;
-    nbbox.height = h;
-    return nbbox;
-}
-
 void GFXOutputDev::beginTransparencyGroup(GfxState *state, double *bbox,
                                      GfxColorSpace *blendingColorSpace,
                                      GBool isolated, GBool knockout,
                                      GBool forSoftMask)
 {
     const char*colormodename = "";
-    BBox rect = mkBBox(state, bbox, this->width, this->height);
-
+  
     if(blendingColorSpace) {
        colormodename = GfxColorSpace::getColorSpaceModeName(blendingColorSpace->getMode());
     }
-    dbg("beginTransparencyGroup %.1f/%.1f/%.1f/%.1f %s isolated=%d knockout=%d forsoftmask=%d", bbox[0],bbox[1],bbox[2],bbox[3], colormodename, isolated, knockout, forSoftMask);
-    dbg("using clipping rect %f/%f/%f/%f\n", rect.posx,rect.posy,rect.width,rect.height);
+    dbg("beginTransparencyGroup device=%08x %.1f/%.1f/%.1f/%.1f %s isolated=%d knockout=%d forsoftmask=%d", device, bbox[0],bbox[1],bbox[2],bbox[3], colormodename, isolated, knockout, forSoftMask);
     msg("<verbose> beginTransparencyGroup %.1f/%.1f/%.1f/%.1f %s isolated=%d knockout=%d forsoftmask=%d", bbox[0],bbox[1],bbox[2],bbox[3], colormodename, isolated, knockout, forSoftMask);
     
-    states[statepos].createsoftmask |= forSoftMask;
+    //states[statepos].createsoftmask |= forSoftMask;
+    states[statepos].createsoftmask = forSoftMask;
     states[statepos].transparencygroup = !forSoftMask;
     states[statepos].isolated = isolated;
 
     states[statepos].olddevice = this->device;
     this->device = (gfxdevice_t*)rfx_calloc(sizeof(gfxdevice_t));
+    dbg("this->device now %08x (old: %08x)", this->device, states[statepos].olddevice);
 
     gfxdevice_record_init(this->device);
     
@@ -2198,17 +2617,29 @@ void GFXOutputDev::beginTransparencyGroup(GfxState *state, double *bbox,
 void GFXOutputDev::endTransparencyGroup(GfxState *state)
 {
     dbgindent-=2;
-    dbg("endTransparencyGroup");
-    msg("<verbose> endTransparencyGroup");
-
     gfxdevice_t*r = this->device;
 
+    dbg("endTransparencyGroup this->device now back to %08x (destroying %08x)", states[statepos].olddevice, this->device);
+    
     this->device = states[statepos].olddevice;
+    if(!this->device) {
+       msg("<fatal> Bad state nesting in transparency group");
+       msg("<fatal> Notice: this is a known problem, which will be fixed in 0.9.1");
+       msg("<fatal> In the meantime, please convert the file with -s poly2bitmap");
+       restoreState(state);
+       this->device = states[statepos].olddevice;
+    }
+    states[statepos].olddevice = 0;
+
+    gfxresult_t*recording = r->finish(r);
+    
+    dbg("                     forsoftmask=%d recording=%08x/%08x", states[statepos].createsoftmask, r, recording);
+    msg("<verbose> endTransparencyGroup forsoftmask=%d recording=%08x/%08x", states[statepos].createsoftmask, r, recording);
 
     if(states[statepos].createsoftmask) {
-       states[statepos-1].softmaskrecording = r->finish(r);
+       states[statepos-1].softmaskrecording = recording;
     } else {
-       states[statepos-1].grouprecording = r->finish(r);
+       states[statepos-1].grouprecording = recording;
     }
     
     states[statepos].createsoftmask = 0;
@@ -2222,9 +2653,9 @@ void GFXOutputDev::paintTransparencyGroup(GfxState *state, double *bbox)
                                "colordodge","colorburn","hardlight","softlight","difference",
                                "exclusion","hue","saturation","color","luminosity"};
 
-    dbg("paintTransparencyGroup blend=%s softmaskon=%d", blendmodes[state->getBlendMode()], states[statepos].softmask);
+    dbg("paintTransparencyGroup blend=%s softmaskon=%d recording=%08x", blendmodes[state->getBlendMode()], states[statepos].softmask, states[statepos].grouprecording);
     msg("<verbose> paintTransparencyGroup blend=%s softmaskon=%d", blendmodes[state->getBlendMode()], states[statepos].softmask);
-   
+
     if(state->getBlendMode() == gfxBlendNormal)
        infofeature("transparency groups");
     else {
@@ -2234,10 +2665,15 @@ void GFXOutputDev::paintTransparencyGroup(GfxState *state, double *bbox)
     }
 
     gfxresult_t*grouprecording = states[statepos].grouprecording;
-   
-    if(state->getBlendMode() == gfxBlendNormal) {
+  
+    int blendmode = state->getBlendMode();
+    if(blendmode == gfxBlendNormal || blendmode == gfxBlendMultiply) {
+       int alpha = (int)(state->getFillOpacity()*255);
+       if(blendmode == gfxBlendMultiply && alpha>200)
+           alpha = 128;
        gfxdevice_t ops;
-       gfxdevice_ops_init(&ops, this->device, (unsigned char)(state->getFillOpacity()*255));
+       dbg("this->device=%08x, this->device->name=%s\n", this->device, this->device->name);
+       gfxdevice_ops_init(&ops, this->device, alpha);
        gfxresult_record_replay(grouprecording, &ops);
        ops.finish(&ops);
     }
@@ -2248,8 +2684,14 @@ void GFXOutputDev::paintTransparencyGroup(GfxState *state, double *bbox)
 
 void GFXOutputDev::setSoftMask(GfxState *state, double *bbox, GBool alpha, Function *transferFunc, GfxColor *rgb)
 {
+    if(states[statepos].softmask) {
+       /* shouldn't happen, but *does* happen */
+       clearSoftMask(state);
+    }
+
     /* alpha = 1: retrieve mask values from alpha layer
        alpha = 0: retrieve mask values from luminance */
+
     dbg("setSoftMask %.1f/%.1f/%.1f/%.1f alpha=%d backdrop=%02x%02x%02x",
            bbox[0], bbox[1], bbox[2], bbox[3], alpha, colToByte(rgb->c[0]), colToByte(rgb->c[1]), colToByte(rgb->c[2]));
     msg("<verbose> setSoftMask %.1f/%.1f/%.1f/%.1f alpha=%d backdrop=%02x%02x%02x",
@@ -2258,12 +2700,16 @@ void GFXOutputDev::setSoftMask(GfxState *state, double *bbox, GBool alpha, Funct
        infofeature("soft masks");
     else
        warnfeature("soft masks from alpha channel",0);
-    
+   
+    if(states[statepos].olddevice) {
+       msg("<fatal> Internal error: badly balanced softmasks/transparency groups");
+       exit(1);
+    }
     states[statepos].olddevice = this->device;
     this->device = (gfxdevice_t*)rfx_calloc(sizeof(gfxdevice_t));
     gfxdevice_record_init(this->device);
 
-    dbg("softmaskrecording is %08x at statepos %d\n", states[statepos].softmaskrecording, statepos);
+    dbg("softmaskrecording is %08x (dev=%08x) at statepos %d\n", states[statepos].softmaskrecording, this->device, statepos);
     
     states[statepos].softmask = 1;
     states[statepos].softmask_alpha = alpha;
@@ -2286,7 +2732,7 @@ void GFXOutputDev::clearSoftMask(GfxState *state)
        return;
     states[statepos].softmask = 0;
     dbg("clearSoftMask statepos=%d", statepos);
-    msg("<verbose> clearSoftMask");
+    msg("<verbose> clearSoftMask statepos=%d", statepos);
     
     if(!states[statepos].softmaskrecording || strcmp(this->device->name, "record")) {
        msg("<error> Error in softmask/tgroup ordering");
@@ -2294,7 +2740,7 @@ void GFXOutputDev::clearSoftMask(GfxState *state)
     }
   
     gfxresult_t*mask = states[statepos].softmaskrecording;
-    gfxresult_t*below = this->device->finish(this->device);
+    gfxresult_t*below = this->device->finish(this->device);free(this->device);
     this->device = states[statepos].olddevice;
 
     /* get outline of all objects below the soft mask */
@@ -2303,14 +2749,13 @@ void GFXOutputDev::clearSoftMask(GfxState *state)
     gfxresult_record_replay(below, &uniondev);
     gfxline_t*belowoutline = gfxdevice_union_getunion(&uniondev);
     uniondev.finish(&uniondev);
-
     gfxbbox_t bbox = gfxline_getbbox(belowoutline);
+    gfxline_free(belowoutline);belowoutline=0;
 #if 0 
     this->device->startclip(this->device, belowoutline);
     gfxresult_record_replay(below, this->device);
     gfxresult_record_replay(mask, this->device);
     this->device->endclip(this->device);
-    gfxline_free(belowoutline);
 #endif
     
     int width = (int)bbox.xmax,height = (int)bbox.ymax;