replaced libart with new polygon code
[swftools.git] / lib / pdf / GFXOutputDev.cc
index 8da82ec..e963148 100644 (file)
@@ -1,4 +1,4 @@
-/* pdfswf.cc
+/* GFXOutputDev.cc
    implements a pdf output device (OutputDev).
 
    This file is part of swftools.
 
 #include <stdio.h>
 #include <stdlib.h>
+#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/polyops.h"
+#include "../devices/render.h"
+
+#include "../png.h"
+#include "fonts.h"
 
 #include <math.h>
 
+#define SQRT2 1.41421356237309504880
+
 typedef struct _fontfile
 {
-    char*filename;
+    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 mapping {
-    char*pdffont;
-    char*filename;
+struct fontentry {
+    const char*pdffont;
+    const char*filename;
+    char*afm;
+    int afmlen;
+    char*pfb;
+    int pfblen;
+    char*fullfilename;
 } pdf2t1map[] ={
-{"Times-Roman",           "n021003l"},
-{"Times-Italic",          "n021023l"},
-{"Times-Bold",            "n021004l"},
-{"Times-BoldItalic",      "n021024l"},
-{"Helvetica",             "n019003l"},
-{"Helvetica-Oblique",     "n019023l"},
-{"Helvetica-Bold",        "n019004l"},
-{"Helvetica-BoldOblique", "n019024l"},
-{"Courier",               "n022003l"},
-{"Courier-Oblique",       "n022023l"},
-{"Courier-Bold",          "n022004l"},
-{"Courier-BoldOblique",   "n022024l"},
-{"Symbol",                "s050000l"},
-{"ZapfDingbats",          "d050000l"}};
-
-typedef struct _feature
-{
-    char*string;
-    struct _feature*next;
-} feature_t;
-feature_t*featurewarnings = 0;
-
-static void warnfeature(char*feature,char fully)
-{
-    feature_t*f = featurewarnings;
+{"Times-Roman",           "n021003l", n021003l_afm, n021003l_afm_len, n021003l_pfb, n021003l_pfb_len},
+{"Times-Italic",          "n021023l", n021023l_afm, n021023l_afm_len, n021023l_pfb, n021023l_pfb_len},
+{"Times-Bold",            "n021004l", n021004l_afm, n021004l_afm_len, n021004l_pfb, n021004l_pfb_len},
+{"Times-BoldItalic",      "n021024l", n021024l_afm, n021024l_afm_len, n021024l_pfb, n021024l_pfb_len},
+{"Helvetica",             "n019003l", n019003l_afm, n019003l_afm_len, n019003l_pfb, n019003l_pfb_len},
+{"Helvetica-Oblique",     "n019023l", n019023l_afm, n019023l_afm_len, n019023l_pfb, n019023l_pfb_len},
+{"Helvetica-Bold",        "n019004l", n019004l_afm, n019004l_afm_len, n019004l_pfb, n019004l_pfb_len},
+{"Helvetica-BoldOblique", "n019024l", n019024l_afm, n019024l_afm_len, n019024l_pfb, n019024l_pfb_len},
+{"Courier",               "n022003l", n022003l_afm, n022003l_afm_len, n022003l_pfb, n022003l_pfb_len},
+{"Courier-Oblique",       "n022023l", n022023l_afm, n022023l_afm_len, n022023l_pfb, n022023l_pfb_len},
+{"Courier-Bold",          "n022004l", n022004l_afm, n022004l_afm_len, n022004l_pfb, n022004l_pfb_len},
+{"Courier-BoldOblique",   "n022024l", n022024l_afm, n022024l_afm_len, n022024l_pfb, n022024l_pfb_len},
+{"Symbol",                "s050000l", s050000l_afm, s050000l_afm_len, s050000l_pfb, s050000l_pfb_len},
+{"ZapfDingbats",          "d050000l", d050000l_afm, d050000l_afm_len, d050000l_pfb, d050000l_pfb_len}};
+
+
+static int verbose = 0;
+static int dbgindent = 1;
+static void dbg(const char*format, ...)
+{
+    char buf[1024];
+    int l;
+    va_list arglist;
+    if(!verbose)
+       return;
+    va_start(arglist, format);
+    vsnprintf(buf, sizeof(buf)-1, format, arglist);
+    va_end(arglist);
+    l = strlen(buf);
+    while(l && buf[l-1]=='\n') {
+       buf[l-1] = 0;
+       l--;
+    }
+    printf("(pdf) ");
+    int indent = dbgindent;
+    while(indent) {
+       printf(" ");
+       indent--;
+    }
+    printf("%s\n", buf);
+    fflush(stdout);
+}
+
+GFXOutputGlobals*gfxglobals=0;
+
+GFXOutputGlobals::GFXOutputGlobals()
+{
+    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)
+{
+    feature_t*f = gfxglobals->featurewarnings;
     while(f) {
        if(!strcmp(feature, f->string))
            return;
@@ -117,16 +184,34 @@ static void warnfeature(char*feature,char fully)
     }
     f = (feature_t*)malloc(sizeof(feature_t));
     f->string = strdup(feature);
-    f->next = featurewarnings;
-    featurewarnings = f;
-    msg("<warning> %s not yet %ssupported!",feature,fully?"fully ":"");
+    f->next = gfxglobals->featurewarnings;
+    gfxglobals->featurewarnings = f;
+    if(warn) {
+       msg("<warning> %s not yet %ssupported!",feature,fully?"fully ":"");
+       if(this->config_break_on_warning) {
+           msg("<fatal> Aborting conversion due to unsupported feature");
+           exit(1);
+       }
+    } else {
+       msg("<notice> File contains %s",feature);
+    }
+}
+void GFXOutputDev::warnfeature(const char*feature,char fully)
+{
+    showfeature(feature,fully,1);
+}
+void GFXOutputDev::infofeature(const char*feature)
+{
+    showfeature(feature,0,0);
 }
 
 GFXOutputState::GFXOutputState() {
     this->clipping = 0;
-    this->textRender = 0;
     this->createsoftmask = 0;
     this->transparencygroup = 0;
+    this->softmask = 0;
+    this->grouprecording = 0;
+    this->isolated = 0;
 }
 
 GBool GFXOutputDev::interpretType3Chars() 
@@ -178,21 +263,324 @@ public:
        chars[num_chars].charid = charid;
     }
 };
+    
+char* writeOutStdFont(fontentry* f)
+{
+    FILE*fi;
+    char namebuf1[512];
+    char namebuf2[512];
+    char* tmpFileName = mktmpname(namebuf1);
 
-static char*getFontID(GfxFont*font);
+    sprintf(namebuf2, "%s.afm", tmpFileName);
+    fi = fopen(namebuf2, "wb");
+    if(!fi)
+        return 0;
+    fwrite(f->afm, 1, f->afmlen, fi);
+    fclose(fi);
 
-GFXOutputDev::GFXOutputDev(parameter_t*p)
+    sprintf(namebuf2, "%s.pfb", tmpFileName);
+    fi = fopen(namebuf2, "wb");
+    if(!fi)
+        return 0;
+    fwrite(f->pfb, 1, f->pfblen, fi);
+    fclose(fi);
+    return strdup(namebuf2);
+}
+void unlinkfont(char* filename)
 {
-    this->jpeginfo = 0;
-    this->textmodeinfo = 0;
-    this->ttfinfo = 0;
-    this->linkinfo = 0;
-    this->pbminfo = 0;
+    int l;
+    if(!filename)
+       return;
+    msg("<verbose> Removing temporary font file %s", filename);
+    l=strlen(filename);
+    unlink(filename);
+    if(!strncmp(&filename[l-4],".afm",4)) {
+       memcpy(&filename[l-4],".pfb",4); unlink(filename);
+       memcpy(&filename[l-4],".pfa",4); unlink(filename);
+       memcpy(&filename[l-4],".afm",4);
+       return;
+    } else 
+    if(!strncmp(&filename[l-4],".pfa",4)) {
+       memcpy(&filename[l-4],".afm",4); unlink(filename);
+       memcpy(&filename[l-4],".pfa",4);
+       return;
+    } else 
+    if(!strncmp(&filename[l-4],".pfb",4)) {
+       memcpy(&filename[l-4],".afm",4); unlink(filename);
+       memcpy(&filename[l-4],".pfb",4);
+       return;
+    }
+}
+
+static int config_use_fontconfig = 1;
+static int fcinitcalled = 0; 
+
+GFXGlobalParams::GFXGlobalParams()
+: GlobalParams((char*)"")
+{
+    //setupBaseFonts(char *dir); //not tested yet
+}
+GFXGlobalParams::~GFXGlobalParams()
+{
+    msg("<verbose> Performing cleanups");
+    int t;
+    for(t=0;t<sizeof(pdf2t1map)/sizeof(fontentry);t++) {
+       if(pdf2t1map[t].fullfilename) {
+           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", 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++) {
+       if(!strcmp(name, pdf2t1map[t].pdffont)) {
+           if(!pdf2t1map[t].fullfilename) {
+               pdf2t1map[t].fullfilename = writeOutStdFont(&pdf2t1map[t]);
+               if(!pdf2t1map[t].fullfilename) {
+                   msg("<error> Couldn't save default font- is the Temp Directory writable?");
+               } else {
+                   msg("<verbose> Storing standard PDF font %s at %s", name, pdf2t1map[t].fullfilename);
+               }
+           }
+           DisplayFontParam *dfp = new DisplayFontParam(new GString(fontName), displayFontT1);
+           dfp->t1.fileName = new GString(pdf2t1map[t].fullfilename);
+           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(InfoOutputDev*info, PDFDoc*doc)
+{
+    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;
@@ -201,42 +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->fontlist = 0;
     this->outer_clip_box = 0;
-    this->pages = 0;
-    this->pagebuflen = 0;
-    this->pagepos = 0;
+    this->config_bigchar=0;
+    this->config_convertgradients=1;
+    this->config_break_on_warning=0;
+    this->config_remapunicode=0;
+    this->config_transparent=0;
+    this->config_extrafontdata = 0;
+    this->config_optimize_polygons = 0;
+    this->config_multiply = 1;
+    this->page2page = 0;
+    this->num_pages = 0;
   
-    this->forceType0Fonts=1;
-    this->config_use_fontconfig=1;
-
-    this->parameters = p;
+    memset(states, 0, sizeof(states));
+};
 
-    /* configure device */
-    while(p) {
-       if(!strcmp(p->name,"forceType0Fonts")) {
-           this->forceType0Fonts = atoi(p->value);
-       } else if(!strcmp(p->name,"fontconfig")) {
-           this->config_use_fontconfig = atoi(p->value);
-       }
-       p = p->next;
+void GFXOutputDev::setParameter(const char*key, const char*value)
+{
+    if(!strcmp(key,"breakonwarning")) {
+       this->config_break_on_warning = 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;
-
-    /* TODO: get rid of this */
     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)
@@ -256,20 +650,6 @@ void GFXOutputDev::setClip(int x1,int y1,int x2,int y2)
     this->user_clipy2 = y2;
 }
 
-static char*getFontID(GfxFont*font)
-{
-    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*getFontName(GfxFont*font)
 {
     char*fontid;
@@ -295,114 +675,7 @@ static char*getFontName(GfxFont*font)
     return fontname;
 }
 
-static char mybuf[1024];
-static char* gfxstate2str(GfxState *state)
-{
-  char*bufpos = mybuf;
-  GfxRGB rgb;
-  bufpos+=sprintf(bufpos,"CTM[%.3f/%.3f/%.3f/%.3f/%.3f/%.3f] ",
-                                   state->getCTM()[0],
-                                   state->getCTM()[1],
-                                   state->getCTM()[2],
-                                   state->getCTM()[3],
-                                   state->getCTM()[4],
-                                   state->getCTM()[5]);
-  if(state->getX1()!=0.0)
-  bufpos+=sprintf(bufpos,"X1-%.1f ",state->getX1());
-  if(state->getY1()!=0.0)
-  bufpos+=sprintf(bufpos,"Y1-%.1f ",state->getY1());
-  bufpos+=sprintf(bufpos,"X2-%.1f ",state->getX2());
-  bufpos+=sprintf(bufpos,"Y2-%.1f ",state->getY2());
-  bufpos+=sprintf(bufpos,"PW%.1f ",state->getPageWidth());
-  bufpos+=sprintf(bufpos,"PH%.1f ",state->getPageHeight());
-  /*bufpos+=sprintf(bufpos,"FC[%.1f/%.1f] ",
-         state->getFillColor()->c[0], state->getFillColor()->c[1]);
-  bufpos+=sprintf(bufpos,"SC[%.1f/%.1f] ",
-         state->getStrokeColor()->c[0], state->getFillColor()->c[1]);*/
-/*  bufpos+=sprintf(bufpos,"FC[%.1f/%.1f/%.1f/%.1f/%.1f/%.1f/%.1f/%.1f]",
-         state->getFillColor()->c[0], state->getFillColor()->c[1],
-         state->getFillColor()->c[2], state->getFillColor()->c[3],
-         state->getFillColor()->c[4], state->getFillColor()->c[5],
-         state->getFillColor()->c[6], state->getFillColor()->c[7]);
-  bufpos+=sprintf(bufpos,"SC[%.1f/%.1f/%.1f/%.1f/%.1f/%.1f/%.1f/%.1f]",
-         state->getStrokeColor()->c[0], state->getFillColor()->c[1],
-         state->getStrokeColor()->c[2], state->getFillColor()->c[3],
-         state->getStrokeColor()->c[4], state->getFillColor()->c[5],
-         state->getStrokeColor()->c[6], state->getFillColor()->c[7]);*/
-  state->getFillRGB(&rgb);
-  if(rgb.r || rgb.g || rgb.b)
-  bufpos+=sprintf(bufpos,"FR[%.1f/%.1f/%.1f] ", rgb.r,rgb.g,rgb.b);
-  state->getStrokeRGB(&rgb);
-  if(rgb.r || rgb.g || rgb.b)
-  bufpos+=sprintf(bufpos,"SR[%.1f/%.1f/%.1f] ", rgb.r,rgb.g,rgb.b);
-  if(state->getFillColorSpace()->getNComps()>1)
-  bufpos+=sprintf(bufpos,"CS[[%d]] ",state->getFillColorSpace()->getNComps());
-  if(state->getStrokeColorSpace()->getNComps()>1)
-  bufpos+=sprintf(bufpos,"SS[[%d]] ",state->getStrokeColorSpace()->getNComps());
-  if(state->getFillPattern())
-  bufpos+=sprintf(bufpos,"FP%08x ", state->getFillPattern());
-  if(state->getStrokePattern())
-  bufpos+=sprintf(bufpos,"SP%08x ", state->getStrokePattern());
-  if(state->getFillOpacity()!=1.0)
-  bufpos+=sprintf(bufpos,"FO%.1f ", state->getFillOpacity());
-  if(state->getStrokeOpacity()!=1.0)
-  bufpos+=sprintf(bufpos,"SO%.1f ", state->getStrokeOpacity());
-
-  bufpos+=sprintf(bufpos,"LW%.1f ", state->getLineWidth());
-  double * dash;
-  int length;
-  double start;
-  state->getLineDash(&dash, &length, &start);
-  int t;
-  if(length)
-  {
-      bufpos+=sprintf(bufpos,"DASH%.1f[",start);
-      for(t=0;t<length;t++) {
-         bufpos+=sprintf(bufpos,"D%.1f",dash[t]);
-      }
-      bufpos+=sprintf(bufpos,"]");
-  }
-
-  if(state->getFlatness()!=1)
-  bufpos+=sprintf(bufpos,"F%d ", state->getFlatness());
-  if(state->getLineJoin()!=0)
-  bufpos+=sprintf(bufpos,"J%d ", state->getLineJoin());
-  if(state->getLineJoin()!=0)
-  bufpos+=sprintf(bufpos,"C%d ", state->getLineCap());
-  if(state->getLineJoin()!=0)
-  bufpos+=sprintf(bufpos,"ML%d ", state->getMiterLimit());
-
-  if(state->getFont() && getFontID(state->getFont()))
-  bufpos+=sprintf(bufpos,"F\"%s\" ",getFontID(state->getFont()));
-  bufpos+=sprintf(bufpos,"FS%.1f ", state->getFontSize());
-  bufpos+=sprintf(bufpos,"MAT[%.1f/%.1f/%.1f/%.1f/%.1f/%.1f] ", state->getTextMat()[0],state->getTextMat()[1],state->getTextMat()[2],
-                                  state->getTextMat()[3],state->getTextMat()[4],state->getTextMat()[5]);
-  if(state->getCharSpace())
-  bufpos+=sprintf(bufpos,"CS%.5f ", state->getCharSpace());
-  if(state->getWordSpace())
-  bufpos+=sprintf(bufpos,"WS%.5f ", state->getWordSpace());
-  if(state->getHorizScaling()!=1.0)
-  bufpos+=sprintf(bufpos,"SC%.1f ", state->getHorizScaling());
-  if(state->getLeading())
-  bufpos+=sprintf(bufpos,"L%.1f ", state->getLeading());
-  if(state->getRise())
-  bufpos+=sprintf(bufpos,"R%.1f ", state->getRise());
-  if(state->getRender())
-  bufpos+=sprintf(bufpos,"R%d ", state->getRender());
-  bufpos+=sprintf(bufpos,"P%08x ", state->getPath());
-  bufpos+=sprintf(bufpos,"CX%.1f ", state->getCurX());
-  bufpos+=sprintf(bufpos,"CY%.1f ", state->getCurY());
-  if(state->getLineX())
-  bufpos+=sprintf(bufpos,"LX%.1f ", state->getLineX());
-  if(state->getLineY())
-  bufpos+=sprintf(bufpos,"LY%.1f ", state->getLineY());
-  bufpos+=sprintf(bufpos," ");
-  return mybuf;
-}
-
-static void dumpFontInfo(char*loglevel, GfxFont*font);
+static void dumpFontInfo(const char*loglevel, GfxFont*font);
 static int lastdumps[1024];
 static int lastdumppos = 0;
 /* nr = 0  unknown
@@ -425,48 +698,48 @@ static void showFontError(GfxFont*font, int nr)
     else if(nr == 1)
       msg("<warning> The following font caused problems (substituting):");
     else if(nr == 2)
-      msg("<warning> The following Type 3 Font will be rendered as bitmap:");
+      msg("<warning> The following Type 3 Font will be rendered as graphics:");
     dumpFontInfo("<warning>", font);
 }
 
-static void dumpFontInfo(char*loglevel, GfxFont*font)
+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;
   }
   
@@ -477,18 +750,18 @@ static void dumpFontInfo(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);
@@ -497,9 +770,15 @@ static void dumpFontInfo(char*loglevel, GfxFont*font)
 //void GFXOutputDev::drawImageMask(GfxState *state, Object *ref, Stream *str, int width, int height, GBool invert, GBool inlineImg) {printf("void GFXOutputDev::drawImageMask(GfxState *state, Object *ref, Stream *str, int width, int height, GBool invert, GBool inlineImg) \n");}
 //void GFXOutputDev::drawImage(GfxState *state, Object *ref, Stream *str, int width, int height, GfxImageColorMap *colorMap, GBool inlineImg) {printf("void GFXOutputDev::drawImage(GfxState *state, Object *ref, Stream *str, int width, int height, GfxImageColorMap *colorMap, GBool inlineImg) \n");}
 
-
 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);
@@ -512,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;
@@ -534,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) {
@@ -573,14 +875,221 @@ gfxline_t* gfxPath_to_gfxline(GfxState*state, GfxPath*path, int closed, int user
        draw.lineTo(&draw, lastx, lasty);
     }
     gfxline_t*result = (gfxline_t*)draw.result(&draw);
+
+    gfxline_optimize(result);
+
     return result;
 }
 
-/*----------------------------------------------------------------------------
- * Primitive Graphic routines
- *----------------------------------------------------------------------------*/
+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::strokeGfxline(GfxState *state, gfxline_t*line)
+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() 
+{ 
+    infofeature("forms");
+    return gFalse; 
+}
+void GFXOutputDev::drawForm(Ref id) 
+{
+    msg("<error> drawForm not implemented");
+}
+GBool GFXOutputDev::needNonText() 
+{ 
+    return gTrue; 
+}
+void GFXOutputDev::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)
 {
     int lineCap = state->getLineCap(); // 0=butt, 1=round 2=square
     int lineJoin = state->getLineJoin(); // 0=miter, 1=round 2=bevel
@@ -598,59 +1107,100 @@ void GFXOutputDev::strokeGfxline(GfxState *state, gfxline_t*line)
     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;
-       double cut = 0;
-       int fixzero = 0;
-       msg("<trace> %d dashes", dashnum);
-       msg("<trace> |  phase: %f", dashphase);
-       for(t=0;t<dashnum;t++) {
-           dash[t] = ldash[t];
-           msg("<trace> |  d%-3d: %f", t, ldash[t]);
+
+        /* 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);
     }
-   
-    //swfoutput_drawgfxline(output, line, width, &col, capType, joinType, miterLimit);
-    device->stroke(device, line, width, &col, capType, joinType, miterLimit);
+
+    if(flags&STROKE_FILL) {
+        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);
+       }
+       if(!gfxline) {
+           msg("<warning> Empty polygon (resulting from stroked line)");
+       }
+        if(flags&STROKE_CLIP) {
+            device->startclip(device, gfxline);
+            states[statepos].clipping++;
+        } else {
+            device->fill(device, gfxline, &col);
+        }
+        gfxline_free(gfxline);
+       gfxpoly_destroy(poly);
+    } else {
+        if(flags&STROKE_CLIP) 
+            msg("<error> Stroke&clip not supported at the same time");
+        device->stroke(device, line, width, &col, capType, joinType, miterLimit);
+    }
     
     if(line2)
        gfxline_free(line2);
@@ -674,21 +1224,19 @@ 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);
     }
-    if(states[statepos].transparencygroup && col.a != 255)
-       return;
-
     device->fill(device, line, &col);
 }
 
 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++;
@@ -696,11 +1244,14 @@ void GFXOutputDev::clipToGfxLine(GfxState *state, gfxline_t*line)
 
 void GFXOutputDev::clip(GfxState *state) 
 {
-    if(states[statepos].createsoftmask)
-       return;
-
     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);
 }
@@ -709,27 +1260,24 @@ void GFXOutputDev::eoClip(GfxState *state)
 {
     GfxPath * path = state->getPath();
     gfxline_t*line = gfxPath_to_gfxline(state, path, 1, user_movex + clipmovex, user_movey + clipmovey);
+    clipToGfxLine(state, line);
+    gfxline_free(line);
+}
+void GFXOutputDev::clipToStrokePath(GfxState *state)
+{
+    GfxPath * path = state->getPath();
+    gfxline_t*line= gfxPath_to_gfxline(state, path, 0, user_movex + clipmovex, user_movey + clipmovey);
 
     if(getLogLevel() >= LOGLEVEL_TRACE)  {
-        msg("<trace> eoclip\n");
+        double width = state->getTransformedLineWidth();
+        msg("<trace> cliptostrokepath width=%f", width);
         dump_outline(line);
     }
 
-    device->startclip(device, line);
-    states[statepos].clipping++;
+    strokeGfxline(state, line, STROKE_FILL|STROKE_CLIP);
     gfxline_free(line);
 }
 
-void GFXOutputDev::endframe()
-{
-    if(outer_clip_box) {
-       device->endclip(device);
-       outer_clip_box = 0;
-    }
-
-    device->endpage(device);
-}
-
 void GFXOutputDev::finish()
 {
     if(outer_clip_box) {
@@ -743,21 +1291,6 @@ void GFXOutputDev::finish()
 GFXOutputDev::~GFXOutputDev() 
 {
     finish();
-
-    if(this->pages) {
-       free(this->pages); this->pages = 0;
-    }
-
-    fontlist_t*l = this->fontlist;
-    while(l) {
-       fontlist_t*next = l->next;
-       l->next = 0;
-       gfxfont_free(l->font);
-       free(l->filename);l->filename=0;
-       free(l);
-       l = next;
-    }
-    this->fontlist = 0;
 };
 GBool GFXOutputDev::upsideDown() 
 {
@@ -768,15 +1301,9 @@ GBool GFXOutputDev::useDrawChar()
     return gTrue;
 }
 
-char*renderModeDesc[]= {"fill", "stroke", "fill+stroke", "invisible",
+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)
 {
@@ -802,124 +1329,76 @@ char* makeStringPrintable(char*str)
     tmp_printstr[len] = 0;
     return tmp_printstr;
 }
-
-
-int getGfxCharID(gfxfont_t*font, int charnr, char *charname, int u)
+void GFXOutputDev::updateFontMatrix(GfxState*state)
 {
-    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);
-               return t;
-           }
-       }
-       /* if we didn't find the character, maybe
-          we can find the capitalized version */
-       for(t=0;t<font->num_glyphs;t++) {
-           if(font->glyphs[t].name && !strcasecmp(font->glyphs[t].name,charname)) {
-               msg("<debug> Char [%d,>>%s<<,%d] maps to %d\n", charnr, charname, u, t);
-               return t;
-           }
-       }
-    }
-
-    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]);
-       return font->unicode2glyph[u];
-    }
+    double* ctm = state->getCTM();
+    double fontSize = state->getFontSize();
+    double*textMat = state->getTextMat();
 
-    if(charnr>=0 && charnr<font->num_glyphs) {
-       msg("<debug> Char [>%d<,%s,%d] maps to %d\n", charnr, charname, u, charnr);
-       return charnr;
-    }
-    
-    return -1;
+    /*  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) 
 { 
     int render = state->getRender();
     if(current_text_stroke) {
        msg("<error> Error: Incompatible change of text rendering to %d while inside cliptext", render);
     }
-
     msg("<trace> beginString(%s) render=%d", makeStringPrintable(s->getCString()), render);
-    double m11,m21,m12,m22;
-//    msg("<debug> %s beginstring \"%s\"\n", gfxstate2str(state), s->getCString());
-    state->getFontTransMat(&m11, &m12, &m21, &m22);
-    m11 *= state->getHorizScaling();
-    m21 *= state->getHorizScaling();
-
-    this->current_font_matrix.m00 = m11 / 1024.0;
-    this->current_font_matrix.m01 = m12 / 1024.0;
-    this->current_font_matrix.m10 = -m21 / 1024.0;
-    this->current_font_matrix.m11 = -m22 / 1024.0;
-    this->current_font_matrix.tx = 0;
-    this->current_font_matrix.ty = 0;
+}
 
-    gfxmatrix_t m = this->current_font_matrix;
+static gfxline_t* mkEmptyGfxShape(double x, double y)
+{
+    gfxline_t*line = (gfxline_t*)malloc(sizeof(gfxline_t));
+    line->x = x;line->y = y;line->type = gfx_moveTo;line->next = 0;
+    return line;
+}
 
-    /*if(render != 3 && render != 0)
-       msg("<warning> Text rendering mode %d (%s) not fully supported yet (for text \"%s\")", render, renderModeDesc[render&7], makeStringPrintable(s->getCString()));*/
-    states[statepos].textRender = render;
+static char isValidUnicode(int c)
+{
+    if(c>=32 && c<0x2fffe)
+       return 1;
+    return 0;
 }
 
 void GFXOutputDev::drawChar(GfxState *state, double x, double y,
                        double dx, double dy,
                        double originX, double originY,
-                       CharCode c, int nBytes, Unicode *_u, int uLen)
+                       CharCode charid, int nBytes, Unicode *_u, int uLen)
 {
-    if(states[statepos].createsoftmask)
+    if(!current_fontinfo || (unsigned)charid >= current_fontinfo->num_glyphs || !current_fontinfo->glyphs[charid]) {
+       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();
+    gfxcolor_t col = getFillColor(state);
+
     // check for invisible text -- this is used by Acrobat Capture
-    if (render == 3) {
-       msg("<debug> Ignoring invisible text: char %d at %f,%f", c, x, y);
-       return;
+    if (render == RENDER_INVISIBLE) {
+       col.a = 0;
+       if(!config_extrafontdata)
+           return;
     }
 
-    if(states[statepos].textRender != render)
-       msg("<error> Internal error: drawChar.render!=beginString.render");
-
-    gfxcolor_t col = getFillColor(state);
-
-    Gushort *CIDToGIDMap = 0;
     GfxFont*font = state->getFont();
 
     if(font->getType() == fontType3) {
@@ -927,70 +1406,24 @@ void GFXOutputDev::drawChar(GfxState *state, double x, double y,
        msg("<debug> type3 char at %f/%f", x, y);
        return;
     }
-    
-    Unicode u=0;
-    char*name=0;
-
-    if(uLen)
-       u = _u[0];
-
-    if(font->isCIDFont()) {
-       GfxCIDFont*cfont = (GfxCIDFont*)font;
-
-       if(font->getType() == fontCIDType2)
-           CIDToGIDMap = cfont->getCIDToGID();
-    } else {
-       Gfx8BitFont*font8;
-       font8 = (Gfx8BitFont*)font;
-       char**enc=font8->getEncoding();
-       name = enc[c];
-    }
-    if (CIDToGIDMap) {
-       msg("<debug> drawChar(%f, %f, c='%c' (%d), GID=%d, u=%d <%d>) CID=%d name=\"%s\" render=%d\n", x, y, (c&127)>=32?c:'?', c, CIDToGIDMap[c], u, uLen, font->isCIDFont(), FIXNULL(name), render);
-       c = CIDToGIDMap[c];
-    } else {
-       msg("<debug> drawChar(%f,%f,c='%c' (%d), u=%d <%d>) CID=%d name=\"%s\" render=%d\n",x,y,(c&127)>=32?c:'?',c,u, uLen, font->isCIDFont(), FIXNULL(name), render);
-    }
-
-    int charid = -1;
-   
-    if(uLen<=1) {
-       charid = getGfxCharID(current_gfxfont, c, name, u);
-    } else {
-       charid = getGfxCharID(current_gfxfont, c, 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_gfxfont->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_gfxfont->id), current_gfxfont->num_glyphs);
-       return;
-    }
+    Unicode u = uLen?(_u[0]):0;
 
     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) {
-       device->drawchar(device, current_gfxfont, charid, &col, &m);
+    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[charid].line;
+       gfxline_t*glyph = current_gfxfont->glyphs[glyphid].line;
        gfxline_t*tglyph = gfxline_clone(glyph);
        gfxline_transform(tglyph, &m);
        if((render&3) != RENDER_INVISIBLE) {
@@ -1000,6 +1433,9 @@ void GFXOutputDev::drawChar(GfxState *state, double x, double y,
        if(render&RENDER_CLIP) {
            gfxline_t*add = gfxline_clone(tglyph);
            current_text_clip = gfxline_append(current_text_clip, add);
+           if(!current_text_clip) {
+               current_text_clip = mkEmptyGfxShape(m.tx, m.ty);
+           }
        }
        gfxline_free(tglyph);
     }
@@ -1009,8 +1445,6 @@ void GFXOutputDev::endString(GfxState *state)
 { 
     int render = state->getRender();
     msg("<trace> endString() render=%d textstroke=%08x", render, current_text_stroke);
-    if(states[statepos].textRender != render)
-       msg("<error> Internal error: drawChar.render!=beginString.render");
     
     if(current_text_stroke) {
        /* fillstroke and stroke text rendering objects we can process right
@@ -1024,11 +1458,11 @@ void GFXOutputDev::endString(GfxState *state)
            current_text_stroke = 0;
        } else if((render&3) == RENDER_FILLSTROKE) {
            fillGfxLine(state, current_text_stroke);
-           strokeGfxline(state, current_text_stroke);
+           strokeGfxline(state, current_text_stroke,0);
            gfxline_free(current_text_stroke);
            current_text_stroke = 0;
        } else if((render&3) == RENDER_STROKE) {
-           strokeGfxline(state, current_text_stroke);
+           strokeGfxline(state, current_text_stroke,0);
            gfxline_free(current_text_stroke);
            current_text_stroke = 0;
        }
@@ -1040,8 +1474,6 @@ void GFXOutputDev::endTextObject(GfxState *state)
 {
     int render = state->getRender();
     msg("<trace> endTextObject() render=%d textstroke=%08x clipstroke=%08x", render, current_text_stroke, current_text_clip);
-    if(states[statepos].textRender != render)
-       msg("<error> Internal error: drawChar.render!=beginString.render");
     
     if(current_text_clip) {
        device->setparameter(device, "mark","TXT");
@@ -1055,28 +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);
+    }
+
+
     /* 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)
@@ -1085,27 +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);
-}
-
 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
@@ -1131,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];
@@ -1145,16 +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;
-    device->fill(device, clippath, &white);
+    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::drawLink(Link *link, Catalog *catalog) 
+
+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);
@@ -1183,19 +1639,23 @@ void GFXOutputDev::drawLink(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];
     char*s = 0;
-    char*type = "-?-";
+    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: {
@@ -1204,7 +1664,8 @@ void GFXOutputDev::drawLink(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();
@@ -1213,6 +1674,7 @@ void GFXOutputDev::drawLink(Link *link, Catalog *catalog)
               else  page=dest->getPageNum();
               sprintf(buf, "%d", page);
               s = strdup(buf);
+              delete dest;
             }
         }
         break;
@@ -1251,8 +1713,9 @@ void GFXOutputDev::drawLink(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"))
                     {
@@ -1291,606 +1754,238 @@ void GFXOutputDev::drawLink(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);
     }
     else if(s)
     {
-        device->drawlink(device, points, s);
-    }
-
-    msg("<verbose> \"%s\" link to \"%s\" (%d)\n", type, FIXNULL(s), page);
-    free(s);s=0;
-}
-
-void GFXOutputDev::saveState(GfxState *state) {
-  msg("<trace> saveState\n");
-  updateAll(state);
-  if(statepos>=64) {
-    msg("<error> Too many nested states in pdf.");
-    return;
-  }
-  statepos ++;
-  states[statepos].textRender = states[statepos-1].textRender;
-  states[statepos].createsoftmask = states[statepos-1].createsoftmask;
-  states[statepos].transparencygroup = states[statepos-1].transparencygroup;
-  states[statepos].clipping = 0;
-};
-
-void GFXOutputDev::restoreState(GfxState *state) {
-  if(statepos==0) {
-      msg("<error> Invalid restoreState");
-      return;
-  }
-  msg("<trace> restoreState");
-  updateAll(state);
-  while(states[statepos].clipping) {
-      device->endclip(device);
-      states[statepos].clipping--;
-  }
-  statepos--;
-}
-
-char* GFXOutputDev::searchFont(char*name) 
-{      
-    int i;
-    char*filename=0;
-    int is_standard_font = 0;
-       
-    msg("<verbose> SearchFont(%s)", name);
-
-    /* see if it is a pdf standard font */
-    for(i=0;i<sizeof(pdf2t1map)/sizeof(mapping);i++) 
-    {
-       if(!strcmp(name, pdf2t1map[i].pdffont))
-       {
-           name = pdf2t1map[i].filename;
-           is_standard_font = 1;
-           break;
-       }
-    }
-    /* look in all font files */
-    for(i=0;i<fontnum;i++) 
-    {
-       if(strstr(fonts[i].filename, name))
-       {
-           if(!fonts[i].used) {
-
-               fonts[i].used = 1;
-               if(!is_standard_font)
-                   msg("<notice> Using %s for %s", fonts[i].filename, name);
-           }
-           return strdup(fonts[i].filename);
-       }
-    }
-    return 0;
-}
-
-void GFXOutputDev::updateLineWidth(GfxState *state)
-{
-    double width = state->getTransformedLineWidth();
-    //swfoutput_setlinewidth(&device, width);
-}
-
-void GFXOutputDev::updateLineCap(GfxState *state)
-{
-    int c = state->getLineCap();
-}
-
-void GFXOutputDev::updateLineJoin(GfxState *state)
-{
-    int j = state->getLineJoin();
-}
-
-void GFXOutputDev::updateFillColor(GfxState *state) 
-{
-    GfxRGB rgb;
-    double opaq = state->getFillOpacity();
-    state->getFillRGB(&rgb);
-}
-void GFXOutputDev::updateFillOpacity(GfxState *state)
-{
-    GfxRGB rgb;
-    double opaq = state->getFillOpacity();
-    state->getFillRGB(&rgb);
-}
-
-void GFXOutputDev::updateStrokeColor(GfxState *state) 
-{
-    GfxRGB rgb;
-    double opaq = state->getStrokeOpacity();
-    state->getStrokeRGB(&rgb);
-}
-
-void FoFiWrite(void *stream, char *data, int len)
-{
-   int ret = fwrite(data, len, 1, (FILE*)stream);
-}
-
-char*GFXOutputDev::writeEmbeddedFontToFile(XRef*ref, GfxFont*font)
-{
-    char*tmpFileName = NULL;
-    FILE *f;
-    int c;
-    char *fontBuf;
-    int fontLen;
-    Ref embRef;
-    Object refObj, strObj;
-    char namebuf[512];
-    tmpFileName = mktmpname(namebuf);
-    int ret;
-
-    ret = font->getEmbeddedFontID(&embRef);
-    if(!ret) {
-       msg("<verbose> Didn't get embedded font id");
-       /* not embedded- the caller should now search the font
-          directories for this font */
-       return 0;
-    }
-
-    f = fopen(tmpFileName, "wb");
-    if (!f) {
-      msg("<error> Couldn't create temporary Type 1 font file");
-       return 0;
-    }
-
-    /*if(font->isCIDFont()) {
-       GfxCIDFont* cidFont = (GfxCIDFont *)font;
-       GString c = cidFont->getCollection();
-       msg("<notice> Collection: %s", c.getCString());
-    }*/
-
-    //if (font->getType() == fontType1C) {
-    if (0) { //font->getType() == fontType1C) {
-      if (!(fontBuf = font->readEmbFontFile(xref, &fontLen))) {
-       fclose(f);
-       msg("<error> Couldn't read embedded font file");
-       return 0;
-      }
-      FoFiType1C *cvt = FoFiType1C::make(fontBuf, fontLen);
-      if(!cvt) return 0;
-      cvt->convertToType1(NULL, gTrue, FoFiWrite, f);
-      //cvt->convertToCIDType0("test", f);
-      //cvt->convertToType0("test", f);
-      delete cvt;
-      gfree(fontBuf);
-    } else if(font->getType() == fontTrueType) {
-      msg("<verbose> writing font using TrueTypeFontFile::writeTTF");
-      if (!(fontBuf = font->readEmbFontFile(xref, &fontLen))) {
-       fclose(f);
-       msg("<error> Couldn't read embedded font file");
-       return 0;
-      }
-      FoFiTrueType *cvt = FoFiTrueType::make(fontBuf, fontLen);
-      cvt->writeTTF(FoFiWrite, f);
-      delete cvt;
-      gfree(fontBuf);
-    } else {
-      font->getEmbeddedFontID(&embRef);
-      refObj.initRef(embRef.num, embRef.gen);
-      refObj.fetch(ref, &strObj);
-      refObj.free();
-      strObj.streamReset();
-      int f4[4];
-      char f4c[4];
-      int t;
-      for(t=0;t<4;t++) {
-         f4[t] = strObj.streamGetChar();
-         f4c[t] = (char)f4[t];
-         if(f4[t] == EOF)
-             break;
-      }
-      if(t==4) {
-         if(!strncmp(f4c, "true", 4)) {
-             /* some weird TTF fonts don't start with 0,1,0,0 but with "true".
-                Change this on the fly */
-             f4[0] = f4[2] = f4[3] = 0;
-             f4[1] = 1;
-         }
-         fputc(f4[0], f);
-         fputc(f4[1], f);
-         fputc(f4[2], f);
-         fputc(f4[3], f);
-
-         while ((c = strObj.streamGetChar()) != EOF) {
-           fputc(c, f);
-         }
-      }
-      strObj.streamClose();
-      strObj.free();
-    }
-    fclose(f);
-
-    return strdup(tmpFileName);
-}
-    
-char* GFXOutputDev::searchForSuitableFont(GfxFont*gfxFont)
-{
-    char*name = getFontName(gfxFont);
-    char*fontname = 0;
-    char*filename = 0;
-
-    if(!this->config_use_fontconfig)
-        return 0;
-    
-#ifdef HAVE_FONTCONFIG
-    FcPattern *pattern, *match;
-    FcResult result;
-    FcChar8 *v;
-
-    static int fcinitcalled = false; 
-        
-    msg("<debug> searchForSuitableFont(%s)", name);
-    
-    // call init ony once
-    if (!fcinitcalled) {
-        msg("<debug> Initializing FontConfig...");
-        fcinitcalled = true;
-       if(!FcInit()) {
-            msg("<debug> FontConfig Initialization failed. Disabling.");
-            config_use_fontconfig = 0;
-            return 0;
-        }
-        msg("<debug> ...initialized FontConfig");
-    }
-   
-    msg("<debug> FontConfig: Create \"%s\" Family Pattern", name);
-    pattern = FcPatternBuild(NULL, FC_FAMILY, FcTypeString, name, NULL);
-    if (gfxFont->isItalic()) // check for italic
-        msg("<debug> FontConfig: Adding Italic Slant");
-       FcPatternAddInteger(pattern, FC_SLANT, FC_SLANT_ITALIC);
-    if (gfxFont->isBold()) // check for bold
-        msg("<debug> FontConfig: Adding Bold Weight");
-        FcPatternAddInteger(pattern, FC_WEIGHT, FC_WEIGHT_BOLD);
-
-    msg("<debug> FontConfig: Try to match...");
-    // configure and match using the original font name 
-    FcConfigSubstitute(0, pattern, FcMatchPattern); 
-    FcDefaultSubstitute(pattern);
-    match = FcFontMatch(0, pattern, &result);
-    
-    if (FcPatternGetString(match, "family", 0, &v) == FcResultMatch) {
-        msg("<debug> FontConfig: family=%s", (char*)v);
-        // if we get an exact match
-        if (strcmp((char *)v, name) == 0) {
-           if (FcPatternGetString(match, "file", 0, &v) == FcResultMatch) {
-               filename = strdup((char*)v); // mem leak
-               char *nfn = strrchr(filename, '/');
-               if(nfn) fontname = strdup(nfn+1);
-               else    fontname = filename;
-            }
-            msg("<debug> FontConfig: Returning \"%s\"", fontname);
-        } else {
-            // initialize patterns
-            FcPatternDestroy(pattern);
-           FcPatternDestroy(match);
-
-            // now match against serif etc.
-           if (gfxFont->isSerif()) {
-                msg("<debug> FontConfig: Create Serif Family Pattern");
-                pattern = FcPatternBuild (NULL, FC_FAMILY, FcTypeString, "serif", NULL);
-            } else if (gfxFont->isFixedWidth()) {
-                msg("<debug> FontConfig: Create Monospace Family Pattern");
-                pattern = FcPatternBuild (NULL, FC_FAMILY, FcTypeString, "monospace", NULL);
-            } else {
-                msg("<debug> FontConfig: Create Sans Family Pattern");
-                pattern = FcPatternBuild (NULL, FC_FAMILY, FcTypeString, "sans", NULL);
-            }
+        device->drawlink(device, points, s);
+    }
 
-            // check for italic
-            if (gfxFont->isItalic()) {
-                msg("<debug> FontConfig: Adding Italic Slant");
-                int bb = FcPatternAddInteger(pattern, FC_SLANT, FC_SLANT_ITALIC);
-            }
-            // check for bold
-            if (gfxFont->isBold()) {
-                msg("<debug> FontConfig: Adding Bold Weight");
-                int bb = FcPatternAddInteger(pattern, FC_WEIGHT, FC_WEIGHT_BOLD);
-            }
+    msg("<verbose> \"%s\" link to \"%s\" (%d)", type, FIXNULL(s), page);
+    free(s);s=0;
+}
 
-            msg("<debug> FontConfig: Try to match... (2)");
-            // configure and match using serif etc
-           FcConfigSubstitute (0, pattern, FcMatchPattern);
-            FcDefaultSubstitute (pattern);
-            match = FcFontMatch (0, pattern, &result);
-            
-            if (FcPatternGetString(match, "file", 0, &v) == FcResultMatch) {
-               filename = strdup((char*)v); // mem leak
-               char *nfn = strrchr(filename, '/');
-               if(nfn) fontname = strdup(nfn+1);
-               else    fontname = filename;
-           }
-            msg("<debug> FontConfig: Returning \"%s\"", fontname);
-        }        
-    }
-
-    //printf("FONTCONFIG: pattern");
-    //FcPatternPrint(pattern);
-    //printf("FONTCONFIG: match");
-    //FcPatternPrint(match);
-    FcPatternDestroy(pattern);
-    FcPatternDestroy(match);
+void GFXOutputDev::saveState(GfxState *state) {
+    dbg("saveState %08x", state); dbgindent+=2;
+
+    msg("<trace> saveState %08x", state);
+    updateAll(state);
+    if(statepos>=64) {
+      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;
+};
 
-    pdfswf_addfont(filename);
-    return fontname;
-#else
-    return 0;
-#endif
-}
+void GFXOutputDev::restoreState(GfxState *state) {
+  dbgindent-=2; dbg("restoreState %08x", state);
 
-char* GFXOutputDev::substituteFont(GfxFont*gfxFont, char* oldname)
-{
-    char*fontname = 0, *filename = 0;
-    msg("<notice> substituteFont(%s)", oldname);
+  if(statepos==0) {
+      msg("<fatal> Invalid restoreState");
+      exit(1);
+  }
+  msg("<trace> restoreState %08x%s%s", state,
+                                 states[statepos].softmask?" (end softmask)":"",
+                                 states[statepos].clipping?" (end clipping)":"");
+  if(states[statepos].softmask) {
+      clearSoftMask(state);
+  }
 
-    if(!(fontname = searchForSuitableFont(gfxFont))) {
-       fontname = "Times-Roman";
-    }
-    filename = searchFont(fontname);
-    if(!filename) {
-       msg("<error> Couldn't find font %s- did you install the default fonts?", fontname);
-       return 0;
-    }
+  if(states[statepos].dashPattern) {
+      if(!statepos || states[statepos-1].dashPattern != states[statepos].dashPattern) {
+          free(states[statepos].dashPattern);
+          states[statepos].dashPattern = 0;
+      }
+  }
 
-    if(substitutepos>=sizeof(substitutesource)/sizeof(char*)) {
-       msg("<fatal> Too many fonts in file.");
-       exit(1);
-    }
-    if(oldname) {
-       substitutesource[substitutepos] = strdup(oldname); //mem leak
-       substitutetarget[substitutepos] = fontname;
-       msg("<notice> substituting %s -> %s", FIXNULL(oldname), FIXNULL(fontname));
-       substitutepos ++;
+  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;
     }
-    return strdup(filename); //mem leak
 }
-
-void unlinkfont(char* filename)
+  
+void GFXOutputDev::setPageMap(int*page2page, int num_pages)
 {
-    int l;
-    if(!filename)
-       return;
-    l=strlen(filename);
-    unlink(filename);
-    if(!strncmp(&filename[l-4],".afm",4)) {
-       memcpy(&filename[l-4],".pfb",4);
-       unlink(filename);
-       memcpy(&filename[l-4],".pfa",4);
-       unlink(filename);
-       memcpy(&filename[l-4],".afm",4);
-       return;
-    } else 
-    if(!strncmp(&filename[l-4],".pfa",4)) {
-       memcpy(&filename[l-4],".afm",4);
-       unlink(filename);
-       memcpy(&filename[l-4],".pfa",4);
-       return;
-    } else 
-    if(!strncmp(&filename[l-4],".pfb",4)) {
-       memcpy(&filename[l-4],".afm",4);
-       unlink(filename);
-       memcpy(&filename[l-4],".pfb",4);
-       return;
-    }
+    this->page2page = page2page;
+    this->num_pages = num_pages;
 }
 
-void GFXOutputDev::setXRef(PDFDoc*doc, XRef *xref) 
+void GFXOutputDev::updateLineWidth(GfxState *state)
 {
-    this->doc = doc;
-    this->xref = xref;
+    double width = state->getTransformedLineWidth();
 }
 
-int GFXOutputDev::setGfxFont(char*id, char*name, char*filename, double maxSize)
+void GFXOutputDev::updateLineCap(GfxState *state)
 {
-    gfxfont_t*font = 0;
-    fontlist_t*last=0,*l = this->fontlist;
+    int c = state->getLineCap();
+}
 
-    if(!id)
-       msg("<error> Internal Error: FontID is null");
+void GFXOutputDev::updateLineJoin(GfxState *state)
+{
+    int j = state->getLineJoin();
+}
 
-    /* TODO: should this be part of the state? */
-    while(l) {
-       last = l;
-       if(!strcmp(l->font->id, id)) {
-           current_gfxfont = l->font;
-           font = l->font;
-           device->addfont(device, current_gfxfont);
-           return 1;
-       }
-       l = l->next;
-    }
-    if(!filename) return 0;
+void GFXOutputDev::updateFillColor(GfxState *state) 
+{
+    GfxRGB rgb;
+    double opaq = state->getFillOpacity();
+    state->getFillRGB(&rgb);
+}
+void GFXOutputDev::updateFillOpacity(GfxState *state)
+{
+    GfxRGB rgb;
+    double opaq = state->getFillOpacity();
+    state->getFillRGB(&rgb);
+    dbg("update fillopaq %f", opaq);
+}
+void GFXOutputDev::updateStrokeOpacity(GfxState *state)
+{
+    double opaq = state->getFillOpacity();
+    dbg("update strokeopaq %f", opaq);
+}
+void GFXOutputDev::updateFillOverprint(GfxState *state)
+{
+    double opaq = state->getFillOverprint();
+    dbg("update filloverprint %f", opaq);
+}
+void GFXOutputDev::updateStrokeOverprint(GfxState *state)
+{
+    double opaq = state->getStrokeOverprint();
+    dbg("update strokeoverprint %f", opaq);
+}
+void GFXOutputDev::updateTransfer(GfxState *state)
+{
+    dbg("update transfer");
+}
 
-    /* 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(id, filename, quality);
-    if(!font) {
-       msg("<verbose> Couldn't load Font %s (%s)", filename, id);
-       return 0;
-    }
-    msg("<verbose> Font %s (%s) loaded successfully", filename, id);
 
-    l = new fontlist_t;
-    l->font = font;
-    l->filename = strdup(filename);
-    l->next = 0;
-    current_gfxfont = l->font;
-    if(last) {
-       last->next = l;
-    } else {
-       this->fontlist = l;
-    }
-    device->addfont(device, current_gfxfont);
-    return 1;
+void GFXOutputDev::updateStrokeColor(GfxState *state) 
+{
+    GfxRGB rgb;
+    double opaq = state->getStrokeOpacity();
+    state->getStrokeRGB(&rgb);
 }
 
 void GFXOutputDev::updateFont(GfxState *state) 
 {
-    GfxFont*gfxFont = state->getFont();
-      
+    GfxFont* gfxFont = state->getFont();
     if (!gfxFont) {
-       return;
+       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-
-       this way, we don't initialize the T1 Fonts
-       too often */
-    for(t=0;t<substitutepos;t++) {
-       if(!strcmp(fontid, substitutesource[t])) {
-           free(fontid);fontid=0;
-           fontid = strdup(substitutetarget[t]);
-           break;
+    char*id = getFontID(gfxFont);
+    msg("<verbose> Updating font to %s", id);
+    if(gfxFont->getType() == fontType3) {
+       infofeature("Type3 fonts");
+       if(!config_extrafontdata) {
+           return;
        }
     }
-
-    /* second, see if this is a font which was used before-
-       if so, we are done */
-    if(setGfxFont(fontid, fontname, 0, 0)) {
-       free(fontid);
-       free(fontname);
-       return;
-    }
-/*    if(swfoutput_queryfont(&device, fontid))
-       swfoutput_setfont(&device, fontid, 0);
-       
-       msg("<debug> updateFont(%s) [cached]", fontid);
-       return;
-    }*/
-
-    // look for Type 3 font
-    if (gfxFont->getType() == fontType3) {
-       if(!type3Warning) {
-           type3Warning = gTrue;
-           showFontError(gfxFont, 2);
-       }
-       free(fontid);
-       free(fontname);
-       return;
+    if(!id) {
+       msg("<error> Internal Error: FontID is null");
+       return; 
     }
 
-    /* now either load the font, or find a substitution */
-
-    Ref embRef;
-    GBool embedded = gfxFont->getEmbeddedFontID(&embRef);
-
-    char*fileName = 0;
-    int del = 0;
-    if(embedded &&
-       (gfxFont->getType() == fontType1 ||
-       gfxFont->getType() == fontType1C ||
-       (gfxFont->getType() == fontCIDType0C && forceType0Fonts) ||
-       gfxFont->getType() == fontTrueType ||
-       gfxFont->getType() == fontCIDType2
-       ))
-    {
-      fileName = writeEmbeddedFontToFile(xref, gfxFont);
-      if(!fileName) showFontError(gfxFont,0);
-      else del = 1;
-    } else {
-      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) ");
-       
-       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);
-    }
+    this->current_fontinfo = this->info->getFont(id);
 
-    if(!fileName) {
-       msg("<error> Couldn't set font %s\n", fontid);
-       free(fontid);
-       free(fontname);
+    if(!this->current_fontinfo) {
+       msg("<error> Internal Error: no fontinfo for font %s", id);
        return;
     }
-       
-    msg("<verbose> updateFont(%s) -> %s (max size: %f)", fontid, fileName, maxSize);
-    dumpFontInfo("<verbose>", gfxFont);
-
-    //swfoutput_setfont(&device, fontid, fileName);
-    
-    if(!setGfxFont(fontid, fontname, 0, 0)) {
-       setGfxFont(fontid, fontname, fileName, maxSize);
+    if(!this->current_fontinfo->seen) {
+       dumpFontInfo("<verbose>", gfxFont);
     }
-   
-    if(fileName && del)
-       unlinkfont(fileName);
 
-    if(fileName)
-        free(fileName);
-    free(fontid);
-    free(fontname);
+    current_gfxfont = this->current_fontinfo->getGfxFont();
+    device->addfont(device, current_gfxfont);
+    free(id);
 
-    msg("<verbose> |");
+    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;
@@ -1898,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];
@@ -1935,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;
     
@@ -1960,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;
@@ -1979,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);
 }
 
 
@@ -2000,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];
@@ -2054,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];
@@ -2069,27 +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];
@@ -2114,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;
@@ -2126,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);
@@ -2146,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];
@@ -2154,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;
@@ -2175,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;
@@ -2227,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;
@@ -2245,8 +2391,7 @@ void GFXOutputDev::drawImageMask(GfxState *state, Object *ref, Stream *str,
                                   int width, int height, GBool invert,
                                   GBool inlineImg) 
 {
-    if(states[statepos].createsoftmask)
-       return;
+    dbg("drawImageMask %dx%d, invert=%d inline=%d", width, height, invert, inlineImg);
     msg("<verbose> drawImageMask %dx%d, invert=%d inline=%d", width, height, invert, inlineImg);
     drawGeneralImage(state,ref,str,width,height,0,invert,inlineImg,1, 0, 0,0,0,0, 0);
 }
@@ -2255,14 +2400,16 @@ void GFXOutputDev::drawImage(GfxState *state, Object *ref, Stream *str,
                         int width, int height, GfxImageColorMap *colorMap,
                         int *maskColors, GBool inlineImg)
 {
-    if(states[statepos].createsoftmask)
-       return;
+    dbg("drawImage %dx%d, %s, %s, inline=%d", width, height, 
+           colorMap?"colorMap":"no colorMap", 
+           maskColors?"maskColors":"no maskColors",
+           inlineImg);
     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(),
+       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);
 }
@@ -2273,13 +2420,14 @@ void GFXOutputDev::drawMaskedImage(GfxState *state, Object *ref, Stream *str,
                               Stream *maskStr, int maskWidth, int maskHeight,
                               GBool maskInvert)
 {
-    if(states[statepos].createsoftmask)
-       return;
+    dbg("drawMaskedImage %dx%d, %s, %dx%d mask", width, height, 
+           colorMap?"colorMap":"no colorMap", 
+           maskWidth, maskHeight);
     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(),
+       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);
 }
@@ -2291,60 +2439,57 @@ void GFXOutputDev::drawSoftMaskedImage(GfxState *state, Object *ref, Stream *str
                                   int maskWidth, int maskHeight,
                                   GfxImageColorMap *maskColorMap)
 {
-    if(states[statepos].createsoftmask)
-       return;
+    dbg("drawSoftMaskedImage %dx%d, %s, %dx%d mask", width, height, 
+           colorMap?"colorMap":"no colorMap", 
+           maskWidth, maskHeight);
     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(),
+       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);
 }
 
 void GFXOutputDev::stroke(GfxState *state) 
 {
-    if(states[statepos].createsoftmask)
-        return;
+    dbg("stroke");
 
     GfxPath * path = state->getPath();
     gfxline_t*line= gfxPath_to_gfxline(state, path, 0, user_movex + clipmovex, user_movey + clipmovey);
-    strokeGfxline(state, line);
+    strokeGfxline(state, line, 0);
     gfxline_free(line);
 }
 
 void GFXOutputDev::fill(GfxState *state) 
 {
-    if(states[statepos].createsoftmask)
-       return;
+    gfxcolor_t col = getFillColor(state);
+    dbg("fill %02x%02x%02x%02x",col.r,col.g,col.b,col.a);
 
     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);
 }
 
 void GFXOutputDev::eoFill(GfxState *state) 
 {
-    if(states[statepos].createsoftmask)
-       return;
-
-    GfxPath * path = state->getPath();
     gfxcolor_t col = getFillColor(state);
+    dbg("eofill %02x%02x%02x%02x",col.r,col.g,col.b,col.a);
 
+    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> eofill\n");
-        dump_outline(line);
-    }
-
-    device->fill(device, line, &col);
+    fillGfxLine(state, line);
     gfxline_free(line);
 }
 
 
-static char* dirseparator()
+static const char* dirseparator()
 {
 #ifdef WIN32
     return "\\";
@@ -2353,27 +2498,34 @@ static char* dirseparator()
 #endif
 }
 
-void addGlobalFont(char*filename)
+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;
     }
 }
 
-void addGlobalLanguageDir(char*dir)
+void addGlobalLanguageDir(const char*dir)
 {
-    if(!globalParams)
-        globalParams = new GlobalParams("");
-    
     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);
@@ -2389,17 +2541,16 @@ void addGlobalLanguageDir(char*dir)
     fclose(fi);
 }
 
-void addGlobalFontDir(char*dirname)
+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) 
@@ -2417,93 +2568,272 @@ void addGlobalFontDir(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;
-}
-  
-#if xpdfUpdateVersion >= 16
 void GFXOutputDev::beginTransparencyGroup(GfxState *state, double *bbox,
                                      GfxColorSpace *blendingColorSpace,
                                      GBool isolated, GBool knockout,
                                      GBool forSoftMask)
 {
-    char*colormodename = GfxColorSpace::getColorSpaceModeName(blendingColorSpace->getMode());
+    const char*colormodename = "";
+  
+    if(blendingColorSpace) {
+       colormodename = GfxColorSpace::getColorSpaceModeName(blendingColorSpace->getMode());
+    }
+    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].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);
     
-    if(!forSoftMask) {
+    /*if(!forSoftMask) { ////???
        state->setFillOpacity(0.0);
-    }
-    warnfeature("transparency groups",1);
+    }*/
+    dbgindent+=2;
 }
 
 void GFXOutputDev::endTransparencyGroup(GfxState *state)
 {
-    msg("<verbose> endTransparencyGroup");
+    dbgindent-=2;
+    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 = recording;
+    } else {
+       states[statepos-1].grouprecording = recording;
+    }
+    
     states[statepos].createsoftmask = 0;
     states[statepos].transparencygroup = 0;
+    free(r);
 }
 
 void GFXOutputDev::paintTransparencyGroup(GfxState *state, double *bbox)
 {
-    msg("<verbose> paintTransparencyGroup");
+    const char*blendmodes[] = {"normal","multiply","screen","overlay","darken", "lighten",
+                               "colordodge","colorburn","hardlight","softlight","difference",
+                               "exclusion","hue","saturation","color","luminosity"};
+
+    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 {
+       char buffer[80];
+       sprintf(buffer, "%s blended transparency groups", blendmodes[state->getBlendMode()]);
+       warnfeature(buffer, 0);
+    }
+
+    gfxresult_t*grouprecording = states[statepos].grouprecording;
+  
+    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;
+       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);
+    }
+    grouprecording->destroy(grouprecording);
+
+    states[statepos].grouprecording = 0;
 }
 
 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",
            bbox[0], bbox[1], bbox[2], bbox[3], alpha, colToByte(rgb->c[0]), colToByte(rgb->c[1]), colToByte(rgb->c[2]));
-    warnfeature("soft masks",0);
+    if(!alpha)
+       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 (dev=%08x) at statepos %d\n", states[statepos].softmaskrecording, this->device, statepos);
+    
+    states[statepos].softmask = 1;
+    states[statepos].softmask_alpha = alpha;
 }
 
-void GFXOutputDev::clearSoftMask(GfxState *state)
+static inline Guchar div255(int x) {
+  return (Guchar)((x + (x >> 8) + 0x80) >> 8);
+}
+
+static unsigned char clampU8(unsigned char c, unsigned char min, unsigned char max)
 {
-    msg("<verbose> clearSoftMask");
+    if(c < min) c = min;
+    if(c > max) c = max;
+    return c;
 }
-#endif
 
-/*class MemCheck
+void GFXOutputDev::clearSoftMask(GfxState *state)
 {
-    public: ~MemCheck()
-    {
-        delete globalParams;globalParams=0;
-        Object::memCheck(stderr);
-        gMemReport(stderr);
+    if(!states[statepos].softmask)
+       return;
+    states[statepos].softmask = 0;
+    dbg("clearSoftMask statepos=%d", statepos);
+    msg("<verbose> clearSoftMask statepos=%d", statepos);
+    
+    if(!states[statepos].softmaskrecording || strcmp(this->device->name, "record")) {
+       msg("<error> Error in softmask/tgroup ordering");
+       return;
+    }
+  
+    gfxresult_t*mask = states[statepos].softmaskrecording;
+    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 */
+    gfxdevice_t uniondev;
+    gfxdevice_union_init(&uniondev, 0);
+    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);
+#endif
+    
+    int width = (int)bbox.xmax,height = (int)bbox.ymax;
+    if(width<=0 || height<=0)
+        return;
+
+    gfxdevice_t belowrender;
+    gfxdevice_render_init(&belowrender);
+    if(states[statepos+1].isolated) {
+       belowrender.setparameter(&belowrender, "fillwhite", "1");
+    }
+    belowrender.setparameter(&belowrender, "antialize", "2");
+    belowrender.startpage(&belowrender, width, height);
+    gfxresult_record_replay(below, &belowrender);
+    belowrender.endpage(&belowrender);
+    gfxresult_t* belowresult = belowrender.finish(&belowrender);
+    gfximage_t* belowimg = (gfximage_t*)belowresult->get(belowresult,"page0");
+    //writePNG("below.png", (unsigned char*)belowimg->data, belowimg->width, belowimg->height);
+
+    gfxdevice_t maskrender;
+    gfxdevice_render_init(&maskrender);
+    maskrender.startpage(&maskrender, width, height);
+    gfxresult_record_replay(mask, &maskrender);
+    maskrender.endpage(&maskrender);
+    gfxresult_t* maskresult = maskrender.finish(&maskrender);
+    gfximage_t* maskimg = (gfximage_t*)maskresult->get(maskresult,"page0");
+
+    if(belowimg->width != maskimg->width || belowimg->height != maskimg->height) {
+       msg("<fatal> Internal error in mask drawing");
+       return;
+    }
+
+    int y,x;
+    for(y=0;y<height;y++) {
+       gfxcolor_t* l1 = &maskimg->data[maskimg->width*y];
+       gfxcolor_t* l2 = &belowimg->data[belowimg->width*y];
+       for(x=0;x<width;x++) {
+           int alpha;
+           if(states[statepos].softmask_alpha) {
+               alpha = l1->a;
+           } else {
+               alpha = (77*l1->r + 151*l1->g + 28*l1->b) >> 8;
+           }
+
+           l2->a = div255(alpha*l2->a);
+
+           /* DON'T premultiply alpha- this is done by fillbitmap,
+              depending on the output device */
+           //l2->r = div255(alpha*l2->r);
+           //l2->g = div255(alpha*l2->g);
+           //l2->b = div255(alpha*l2->b);
+
+           l1++;
+           l2++;
+       }
     }
-} myMemCheck;*/
+    gfxline_t*line = gfxline_makerectangle(0,0,width,height);
+
+    gfxmatrix_t matrix;
+    matrix.m00 = 1.0; matrix.m10 = 0.0; matrix.tx = 0.0;
+    matrix.m01 = 0.0; matrix.m11 = 1.0; matrix.ty = 0.0;
+
+    this->device->fillbitmap(this->device, line, belowimg, &matrix, 0);
+
+    mask->destroy(mask);
+    below->destroy(below);
+    maskresult->destroy(maskresult);
+    belowresult->destroy(belowresult);
+    states[statepos].softmaskrecording = 0;
+}
+  
+//class MemCheck
+//{
+//    public: ~MemCheck()
+//    {
+//        delete globalParams;globalParams=0;
+//        Object::memCheck(stderr);
+//        gMemReport(stderr);
+//    }
+//} myMemCheck;