added new output devices
[swftools.git] / lib / pdf / GFXOutputDev.cc
index 2878d5a..da5df32 100644 (file)
@@ -1,4 +1,4 @@
-/* pdfswf.cc
+/* GFXOutputDev.cc
    implements a pdf output device (OutputDev).
 
    This file is part of swftools.
@@ -19,6 +19,7 @@
 
 #include <stdio.h>
 #include <stdlib.h>
+#include <stdarg.h>
 #include <stddef.h>
 #include <string.h>
 #include <unistd.h>
@@ -30,9 +31,6 @@
 #ifdef HAVE_SYS_STAT_H
 #include <sys/stat.h>
 #endif
-#ifdef HAVE_FONTCONFIG
-#include <fontconfig.h>
-#endif
 //xpdf header files
 #include "config.h"
 #include "gfile.h"
 #include "../gfxdevice.h"
 #include "../gfxtools.h"
 #include "../gfxfont.h"
+#include "../devices/record.h"
+#include "../devices/ops.h"
+#include "../devices/arts.h"
+#include "../devices/render.h"
+
+#include "../art/libart.h"
+#include "../devices/artsutils.c"
+
+#include "../png.h"
+#include "fonts.h"
 
 #include <math.h>
 
 typedef struct _fontfile
 {
-    char*filename;
+    const char*filename;
     int used;
 } fontfile_t;
 
@@ -81,28 +89,104 @@ static int fontnum = 0;
 
 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"}};
+{"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 = 0;
+static void dbg(const char*format, ...)
+{
+    char buf[1024];
+    int l;
+    va_list arglist;
+    if(!verbose)
+       return;
+    va_start(arglist, format);
+    vsprintf(buf, 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);
+}
+
+
+typedef struct _feature
+{
+    char*string;
+    struct _feature*next;
+} feature_t;
+feature_t*featurewarnings = 0;
+
+void GFXOutputDev::showfeature(const char*feature,char fully, char warn)
+{
+    feature_t*f = featurewarnings;
+    while(f) {
+       if(!strcmp(feature, f->string))
+           return;
+       f = f->next;
+    }
+    f = (feature_t*)malloc(sizeof(feature_t));
+    f->string = strdup(feature);
+    f->next = featurewarnings;
+    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() 
@@ -154,14 +238,111 @@ 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)
 {
+    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;
+    }
+}
+
+
+GFXGlobalParams::GFXGlobalParams()
+: GlobalParams("")
+{
+    //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);
+       }
+    }
+}
+DisplayFontParam *GFXGlobalParams::getDisplayFont(GString *fontName)
+{
+    msg("<verbose> looking for font %s in global params\n", 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;
+       }
+    }
+    for(t=0;t<fontnum;t++) {
+       if(strstr(fonts[t].filename, name)) {
+           DisplayFontParam *dfp = new DisplayFontParam(new GString(fontName), displayFontT1);
+           dfp->t1.fileName = new GString(fonts[t].filename);
+           return dfp;
+       }
+    }
+    return GlobalParams::getDisplayFont(fontName);
+}
+
+GFXOutputDev::GFXOutputDev(InfoOutputDev*info, PDFDoc*doc)
+{
+    this->info = info;
+    this->doc = doc;
+    this->xref = doc->getXRef();
+
     this->jpeginfo = 0;
     this->textmodeinfo = 0;
-    this->ttfinfo = 0;
     this->linkinfo = 0;
     this->pbminfo = 0;
     this->type3active = 0;
@@ -179,41 +360,43 @@ GFXOutputDev::GFXOutputDev(parameter_t*p)
     this->user_clipy2 = 0;
     this->current_text_stroke = 0;
     this->current_text_clip = 0;
-    this->fontlist = 0;
     this->outer_clip_box = 0;
     this->pages = 0;
     this->pagebuflen = 0;
     this->pagepos = 0;
-    this->transparencyGroup = 0;
-  
-    this->forceType0Fonts=1;
-    this->config_use_fontconfig=1;
+    this->config_break_on_warning=0;
+    this->config_remapunicode=0;
+    this->config_transparent=0;
+    this->config_extrafontdata = 0;
 
-    this->parameters = p;
+    this->gfxfontlist = gfxfontlist_create();
+  
+    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,"help")) {
+       printf("\nPDF layer options:\n");
+       printf("breakonwarning=0/1  Abort conversion if graphic objects are found which\n");
+       printf("                    are not 100%% supported\n");
+       printf("transparent=0/1     Make PDF transparent (alpha background)\n");
+       printf("extrafontdata=0/1   Store Type3 characters and capture characters\n");
     }
-};
+    
+}
   
 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)
@@ -233,20 +416,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;
@@ -272,114 +441,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
@@ -402,11 +464,11 @@ 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);
@@ -474,7 +536,6 @@ 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)
 {
     while(line) {
@@ -550,14 +611,49 @@ 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");
+    return gFalse;
+}
+
+GBool GFXOutputDev::useShadedFills()
+{
+    infofeature("shaded fills");
+    return gFalse;
+}
+  
+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");
+    if(outer_clip_box) {
+       device->endclip(device);
+       outer_clip_box = 0;
+    }
+}
 
-void GFXOutputDev::strokeGfxline(GfxState *state, gfxline_t*line)
+#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
@@ -596,8 +692,6 @@ void GFXOutputDev::strokeGfxline(GfxState *state, gfxline_t*line)
     if(dashnum && ldash) {
        float * dash = (float*)malloc(sizeof(float)*(dashnum+1));
        int t;
-       double cut = 0;
-       int fixzero = 0;
        msg("<trace> %d dashes", dashnum);
        msg("<trace> |  phase: %f", dashphase);
        for(t=0;t<dashnum;t++) {
@@ -625,9 +719,29 @@ void GFXOutputDev::strokeGfxline(GfxState *state, gfxline_t*line)
                );
         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) {
+        ArtSVP* svp = gfxstrokeToSVP(line, width, capType, joinType, miterLimit);
+        gfxline_t*gfxline = SVPtogfxline(svp);
+       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);
+        }
+        free(gfxline);
+       art_svp_free(svp);
+    } 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);
@@ -649,23 +763,14 @@ gfxcolor_t getFillColor(GfxState * state)
 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);
         dump_outline(line);
     }
-
     device->fill(device, line, &col);
 }
 
-void GFXOutputDev::clip(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::clipToGfxLine(GfxState *state, gfxline_t*line) 
 {
     if(getLogLevel() >= LOGLEVEL_TRACE)  {
@@ -676,6 +781,15 @@ void GFXOutputDev::clipToGfxLine(GfxState *state, gfxline_t*line)
     device->startclip(device, line);
     states[statepos].clipping++;
 }
+
+void GFXOutputDev::clip(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::eoClip(GfxState *state) 
 {
     GfxPath * path = state->getPath();
@@ -690,15 +804,18 @@ void GFXOutputDev::eoClip(GfxState *state)
     states[statepos].clipping++;
     gfxline_free(line);
 }
-
-void GFXOutputDev::endframe()
+void GFXOutputDev::clipToStrokePath(GfxState *state)
 {
-    if(outer_clip_box) {
-       device->endclip(device);
-       outer_clip_box = 0;
+    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> cliptostrokepath\n");
+        dump_outline(line);
     }
 
-    device->endpage(device);
+    strokeGfxline(state, line, STROKE_FILL|STROKE_CLIP);
+    gfxline_free(line);
 }
 
 void GFXOutputDev::finish()
@@ -719,16 +836,7 @@ GFXOutputDev::~GFXOutputDev()
        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;
+    gfxfontlist_free(this->gfxfontlist, 1);
 };
 GBool GFXOutputDev::upsideDown() 
 {
@@ -739,7 +847,7 @@ 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
@@ -773,75 +881,32 @@ char* makeStringPrintable(char*str)
     tmp_printstr[len] = 0;
     return tmp_printstr;
 }
-
-
-int getGfxCharID(gfxfont_t*font, int charnr, char *charname, int u)
+#define INTERNAL_FONT_SIZE 1024.0
+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();
@@ -850,46 +915,44 @@ void GFXOutputDev::beginString(GfxState *state, GString *s)
     }
 
     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(createsoftmask)
-       return;
-    int render = state->getRender();
-    // 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);
+    if(!current_fontinfo || (unsigned)charid >= current_fontinfo->num_glyphs || !current_fontinfo->glyphs[charid]) {
+       msg("<error> Invalid charid %d for font %s", charid, current_font_id);
        return;
     }
+  
+    CharCode glyphid = current_fontinfo->glyphs[charid]->glyphid;
 
-    if(states[statepos].textRender != render)
-       msg("<error> Internal error: drawChar.render!=beginString.render");
-
+    int render = state->getRender();
     gfxcolor_t col = getFillColor(state);
 
-    Gushort *CIDToGIDMap = 0;
+    // check for invisible text -- this is used by Acrobat Capture
+    if (render == RENDER_INVISIBLE) {
+       col.a = 0;
+       if(!config_extrafontdata)
+           return;
+    }
+
     GfxFont*font = state->getFont();
 
     if(font->getType() == fontType3) {
@@ -897,70 +960,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;
+    msg("<debug> drawChar(%f,%f,c='%c' (%d), u=%d <%d>) CID=%d render=%d glyphid=%d\n",x,y,(charid&127)>=32?charid:'?', charid, u, uLen, font->isCIDFont(), render, glyphid);
 
     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;
 
-    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) {
            msg("<notice> Some texts will be rendered as shape");
            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) {
@@ -970,6 +987,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);
     }
@@ -979,8 +999,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
@@ -994,11 +1012,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;
        }
@@ -1010,8 +1028,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");
@@ -1025,28 +1041,49 @@ 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;
+       state->transform(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;
+       m.tx += user_movex + clipmovex;
+       m.ty += user_movey + clipmovey;
+
+       if(!current_fontinfo || (unsigned)charid >= current_fontinfo->num_glyphs || !current_fontinfo->glyphs[charid]) {
+           msg("<error> Invalid charid %d for font %s", charid, current_font_id);
+           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)
@@ -1055,16 +1092,6 @@ 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;
@@ -1115,10 +1142,13 @@ 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);
+    }
 }
 
-void GFXOutputDev::drawLink(Link *link, Catalog *catalog) 
+
+void GFXOutputDev::processLink(Link *link, Catalog *catalog)
 {
     double x1, y1, x2, y2, w;
     gfxline_t points[5];
@@ -1162,7 +1192,7 @@ void GFXOutputDev::drawLink(Link *link, Catalog *catalog)
     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());
@@ -1303,19 +1333,32 @@ void GFXOutputDev::drawLink(Link *link, Catalog *catalog)
 }
 
 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].clipping = 0; //? shouldn't this be the current value?
-  states[statepos].textRender = states[statepos-1].textRender;
+    dbg("saveState");dbgindent+=2;
+
+    msg("<trace> saveState\n");
+    updateAll(state);
+    if(statepos>=64) {
+      msg("<error> Too many nested states in pdf.");
+      return;
+    }
+    statepos ++;
+    states[statepos].createsoftmask = states[statepos-1].createsoftmask;
+    states[statepos].transparencygroup = states[statepos-1].transparencygroup;
+    states[statepos].clipping = 0;
 };
 
 void GFXOutputDev::restoreState(GfxState *state) {
-  msg("<trace> restoreState\n");
+  dbgindent-=2; dbg("restoreState");
+
+  if(statepos==0) {
+      msg("<error> Invalid restoreState");
+      return;
+  }
+  msg("<trace> restoreState%s%s", states[statepos].softmask?" (end softmask)":"",
+                                 states[statepos].clipping?" (end clipping)":"");
+  if(states[statepos].softmask) {
+      clearSoftMask(state);
+  }
   updateAll(state);
   while(states[statepos].clipping) {
       device->endclip(device);
@@ -1324,41 +1367,6 @@ void GFXOutputDev::restoreState(GfxState *state) {
   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();
@@ -1386,460 +1394,146 @@ void GFXOutputDev::updateFillOpacity(GfxState *state)
     GfxRGB rgb;
     double opaq = state->getFillOpacity();
     state->getFillRGB(&rgb);
+    dbg("update fillopaq %f", opaq);
 }
-
-void GFXOutputDev::updateStrokeColor(GfxState *state) 
+void GFXOutputDev::updateStrokeOpacity(GfxState *state)
 {
-    GfxRGB rgb;
-    double opaq = state->getStrokeOpacity();
-    state->getStrokeRGB(&rgb);
+    double opaq = state->getFillOpacity();
+    dbg("update strokeopaq %f", opaq);
 }
-
-void FoFiWrite(void *stream, char *data, int len)
+void GFXOutputDev::updateFillOverprint(GfxState *state)
 {
-   int ret = fwrite(data, len, 1, (FILE*)stream);
+    double opaq = state->getFillOverprint();
+    dbg("update filloverprint %f", opaq);
 }
-
-char*GFXOutputDev::writeEmbeddedFontToFile(XRef*ref, GfxFont*font)
+void GFXOutputDev::updateStrokeOverprint(GfxState *state)
 {
-    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);
+    double opaq = state->getStrokeOverprint();
+    dbg("update strokeoverprint %f", opaq);
 }
-    
-char* GFXOutputDev::searchForSuitableFont(GfxFont*gfxFont)
+void GFXOutputDev::updateTransfer(GfxState *state)
 {
-    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);
-            }
-
-            // 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("<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);
-
-    pdfswf_addfont(filename);
-    return fontname;
-#else
-    return 0;
-#endif
+    dbg("update transfer");
 }
 
-char* GFXOutputDev::substituteFont(GfxFont*gfxFont, char* oldname)
-{
-    char*fontname = 0, *filename = 0;
-    msg("<notice> substituteFont(%s)", oldname);
-
-    if(!(fontname = searchForSuitableFont(gfxFont))) {
-       fontname = "Times-Roman";
-    }
-    filename = searchFont(fontname);
-    if(!filename) {
-       msg("<error> Couldn't find font %s- did you install the default fonts?", fontname);
-       return 0;
-    }
-
-    if(substitutepos>=sizeof(substitutesource)/sizeof(char*)) {
-       msg("<fatal> Too many fonts in file.");
-       exit(1);
-    }
-    if(oldname) {
-       substitutesource[substitutepos] = strdup(oldname); //mem leak
-       substitutetarget[substitutepos] = fontname;
-       msg("<notice> substituting %s -> %s", FIXNULL(oldname), FIXNULL(fontname));
-       substitutepos ++;
-    }
-    return strdup(filename); //mem leak
-}
 
-void unlinkfont(char* filename)
+void GFXOutputDev::updateStrokeColor(GfxState *state) 
 {
-    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;
-    }
+    GfxRGB rgb;
+    double opaq = state->getStrokeOpacity();
+    state->getStrokeRGB(&rgb);
 }
 
-void GFXOutputDev::setXRef(PDFDoc*doc, XRef *xref) 
-{
-    this->doc = doc;
-    this->xref = xref;
-}
 
-int GFXOutputDev::setGfxFont(char*id, char*name, char*filename, double maxSize)
+gfxfont_t* createGfxFont(GfxFont*xpdffont, FontInfo*src)
 {
-    gfxfont_t*font = 0;
-    fontlist_t*last=0,*l = this->fontlist;
-
-    if(!id)
-       msg("<error> Internal Error: FontID is null");
+    gfxfont_t*font = (gfxfont_t*)malloc(sizeof(gfxfont_t));
+    memset(font, 0, sizeof(gfxfont_t));
 
-    /* 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;
+    font->glyphs = (gfxglyph_t*)malloc(sizeof(gfxglyph_t)*src->num_glyphs);
+    memset(font->glyphs, 0, sizeof(gfxglyph_t)*src->num_glyphs);
+    font->id = strdup(getFontID(xpdffont));
+    int t;
+    double quality = (INTERNAL_FONT_SIZE * 0.05) / src->max_size;
+    double scale = 1;
+    //printf("%d glyphs\n", font->num_glyphs);
+    font->num_glyphs = 0;
+    for(t=0;t<src->num_glyphs;t++) {
+       if(src->glyphs[t]) {
+           SplashPath*path = src->glyphs[t]->path;
+           int len = path?path->getLength():0;
+           //printf("glyph %d) %08x (%d line segments)\n", t, path, len);
+           gfxglyph_t*glyph = &font->glyphs[font->num_glyphs];
+           src->glyphs[t]->glyphid = font->num_glyphs;
+           glyph->unicode = src->glyphs[t]->unicode;
+           if(glyph->unicode >= font->max_unicode)
+               font->max_unicode = glyph->unicode+1;
+           gfxdrawer_t drawer;
+           gfxdrawer_target_gfxline(&drawer);
+           int s;
+           int count = 0;
+           double xmax = 0;
+           for(s=0;s<len;s++) {
+               Guchar f;
+               double x, y;
+               path->getPoint(s, &x, &y, &f);
+               if(!s || x > xmax)
+                   xmax = x;
+               if(f&splashPathFirst) {
+                   drawer.moveTo(&drawer, x*scale, y*scale);
+               }
+               if(f&splashPathCurve) {
+                   double x2,y2;
+                   path->getPoint(++s, &x2, &y2, &f);
+                   if(f&splashPathCurve) {
+                       double x3,y3;
+                       path->getPoint(++s, &x3, &y3, &f);
+                       gfxdraw_cubicTo(&drawer, x*scale, y*scale, x2*scale, y2*scale, x3*scale, y3*scale, quality);
+                   } else {
+                       drawer.splineTo(&drawer, x*scale, y*scale, x2*scale, y2*scale);
+                   }
+               } else {
+                   drawer.lineTo(&drawer, x*scale, y*scale);
+               }
+            //   printf("%f %f %s %s\n", x, y, (f&splashPathCurve)?"curve":"",
+            //                           (f&splashPathFirst)?"first":"",
+            //                           (f&splashPathLast)?"last":"");
+           }
+           glyph->line = (gfxline_t*)drawer.result(&drawer);
+           glyph->advance = xmax*scale; // we don't know the real advance value, so this'll have to do
+           font->num_glyphs++;
        }
-       l = l->next;
-    }
-    if(!filename) return 0;
-
-    /* A font size of e.g. 9 means the font will be scaled down by
-       1024 and scaled up by 9. So to have a maximum error of 1/20px,
-       we have to divide 0.05 by (fontsize/1024)
-     */
-    double quality = (1024 * 0.05) / maxSize;
-   
-    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);
+    font->unicode2glyph = (int*)malloc(sizeof(int)*font->max_unicode);
+    memset(font->unicode2glyph, -1, sizeof(int)*font->max_unicode);
+    for(t=0;t<font->num_glyphs;t++) {
+       if(font->glyphs[t].unicode>0 && font->glyphs[t].unicode<font->max_unicode) {
+           font->unicode2glyph[font->glyphs[t].unicode] = t;
+       }
 
-    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;
+    msg("<trace> %d glyphs.", t, font->num_glyphs);
+    return font;
 }
 
 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(!this->current_fontinfo) {
+       msg("<error> Internal Error: no fontinfo for font %s\n", id);
     }
-
-    if(!fileName) {
-       msg("<error> Couldn't set font %s\n", fontid);
-       free(fontid);
-       free(fontname);
-       return;
+    if(!this->current_fontinfo->seen) {
+       dumpFontInfo("<verbose>", gfxFont);
     }
-       
-    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);
+    gfxfont_t*font = gfxfontlist_findfont(this->gfxfontlist,id);
+    if(!font) {
+       font = createGfxFont(gfxFont, current_fontinfo);
+        font->id = strdup(id);
+       this->gfxfontlist = gfxfontlist_addfont(this->gfxfontlist, font);
+       device->addfont(device, font);
     }
-   
-    if(fileName && del)
-       unlinkfont(fileName);
-
-    if(fileName)
-        free(fileName);
-    free(fontid);
-    free(fontname);
+    current_gfxfont = font;
+    free(id);
 
-    msg("<verbose> |");
+    updateFontMatrix(state);
 }
 
 #define SQR(x) ((x)*(x))
@@ -2038,7 +1732,6 @@ void GFXOutputDev::drawGeneralImage(GfxState *state, Object *ref, Stream *str,
   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;
 
-
   if(!pbminfo && !(str->getKind()==strDCT)) {
       if(!type3active) {
          msg("<notice> file contains pbm pictures %s",mask?"(masked)":"");
@@ -2209,8 +1902,7 @@ void GFXOutputDev::drawImageMask(GfxState *state, Object *ref, Stream *str,
                                   int width, int height, GBool invert,
                                   GBool inlineImg) 
 {
-    if(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);
 }
@@ -2219,8 +1911,10 @@ void GFXOutputDev::drawImage(GfxState *state, Object *ref, Stream *str,
                         int width, int height, GfxImageColorMap *colorMap,
                         int *maskColors, GBool inlineImg)
 {
-    if(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",
@@ -2237,8 +1931,9 @@ void GFXOutputDev::drawMaskedImage(GfxState *state, Object *ref, Stream *str,
                               Stream *maskStr, int maskWidth, int maskHeight,
                               GBool maskInvert)
 {
-    if(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);
@@ -2255,8 +1950,9 @@ void GFXOutputDev::drawSoftMaskedImage(GfxState *state, Object *ref, Stream *str
                                   int maskWidth, int maskHeight,
                                   GfxImageColorMap *maskColorMap)
 {
-    if(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);
@@ -2268,17 +1964,19 @@ void GFXOutputDev::drawSoftMaskedImage(GfxState *state, Object *ref, Stream *str
 
 void GFXOutputDev::stroke(GfxState *state) 
 {
-    if(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) 
 {
+    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);
     fillGfxLine(state, line);
@@ -2287,22 +1985,17 @@ void GFXOutputDev::fill(GfxState *state)
 
 void GFXOutputDev::eoFill(GfxState *state) 
 {
-    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 "\\";
@@ -2311,24 +2004,21 @@ 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);
+       msg("<notice> Adding font \"%s\".", filename);
        fonts[fontnum++] = f;
     } else {
        msg("<error> Too many external fonts. Not adding font file \"%s\".", filename);
     }
 }
 
-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;
@@ -2347,7 +2037,7 @@ 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);
@@ -2375,8 +2065,7 @@ 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());
@@ -2412,54 +2101,310 @@ void GFXOutputDev::preparePage(int pdfpage, int outputpage)
     if(pdfpage>this->pagepos)
        this->pagepos = pdfpage;
 }
-  
+
+struct BBox
+{
+    double posx,posy;
+    double width,height;
+};
+
+BBox mkBBox(GfxState*state, double*bbox, double width, double height)
+{
+    double xMin, yMin, xMax, yMax, x, y;
+    double tx, ty, w, h;
+    // transform the bbox
+    state->transform(bbox[0], bbox[1], &x, &y);
+    xMin = xMax = x;
+    yMin = yMax = y;
+    state->transform(bbox[0], bbox[3], &x, &y);
+    if (x < xMin) {
+      xMin = x;
+    } else if (x > xMax) {
+      xMax = x;
+    }
+    if (y < yMin) {
+      yMin = y;
+    } else if (y > yMax) {
+      yMax = y;
+    }
+    state->transform(bbox[2], bbox[1], &x, &y);
+    if (x < xMin) {
+      xMin = x;
+    } else if (x > xMax) {
+      xMax = x;
+    }
+    if (y < yMin) {
+      yMin = y;
+    } else if (y > yMax) {
+      yMax = y;
+    }
+    state->transform(bbox[2], bbox[3], &x, &y);
+    if (x < xMin) {
+      xMin = x;
+    } else if (x > xMax) {
+      xMax = x;
+    }
+    if (y < yMin) {
+      yMin = y;
+    } else if (y > yMax) {
+      yMax = y;
+    }
+    tx = (int)floor(xMin);
+    if (tx < 0) {
+      tx = 0;
+    } else if (tx > width) {
+      tx = width;
+    }
+    ty = (int)floor(yMin);
+    if (ty < 0) {
+      ty = 0;
+    } else if (ty > height) {
+      ty = height;
+    }
+    w = (int)ceil(xMax) - tx + 1;
+    if (tx + w > width) {
+      w = width - tx;
+    }
+    if (w < 1) {
+      w = 1;
+    }
+    h = (int)ceil(yMax) - ty + 1;
+    if (ty + h > height) {
+      h = height - ty;
+    }
+    if (h < 1) {
+      h = 1;
+    }
+    BBox nbbox;
+    nbbox.posx = xMin;
+    nbbox.posx = yMin;
+    nbbox.width = w;
+    nbbox.height = h;
+    return nbbox;
+}
+
 void GFXOutputDev::beginTransparencyGroup(GfxState *state, double *bbox,
                                      GfxColorSpace *blendingColorSpace,
                                      GBool isolated, GBool knockout,
                                      GBool forSoftMask)
 {
-    char*colormodename = GfxColorSpace::getColorSpaceModeName(blendingColorSpace->getMode());
-    msg("<verbose> beginTransparencyGroup %f/%f/%f/%f %s isolated=%d knockout=%d forsoftmask=%d", bbox[0],bbox[1],bbox[2],bbox[3], colormodename, isolated, knockout, forSoftMask);
-    createsoftmask = forSoftMask;
-    transparencyGroup = !forSoftMask;
+    const char*colormodename = "";
 
-    if(transparencyGroup) {
-       state->setFillOpacity(0.0);
-       clip(state);
+    if(blendingColorSpace) {
+       colormodename = GfxColorSpace::getColorSpaceModeName(blendingColorSpace->getMode());
     }
+    dbg("beginTransparencyGroup %.1f/%.1f/%.1f/%.1f %s isolated=%d knockout=%d forsoftmask=%d", bbox[0],bbox[1],bbox[2],bbox[3], colormodename, isolated, knockout, forSoftMask);
+    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].transparencygroup = !forSoftMask;
+    states[statepos].isolated = isolated;
+
+    states[statepos].olddevice = this->device;
+    this->device = (gfxdevice_t*)rfx_calloc(sizeof(gfxdevice_t));
+
+    gfxdevice_record_init(this->device);
+    
+    /*if(!forSoftMask) { ////???
+       state->setFillOpacity(0.0);
+    }*/
+    dbgindent+=2;
 }
 
 void GFXOutputDev::endTransparencyGroup(GfxState *state)
 {
+    dbgindent-=2;
+    dbg("endTransparencyGroup");
     msg("<verbose> endTransparencyGroup");
-    createsoftmask = 0;
-    transparencyGroup = 0;
+
+    gfxdevice_t*r = this->device;
+
+    this->device = states[statepos].olddevice;
+
+    if(states[statepos].createsoftmask) {
+       states[statepos-1].softmaskrecording = r->finish(r);
+    } else {
+       states[statepos-1].grouprecording = r->finish(r);
+    }
+    
+    states[statepos].createsoftmask = 0;
+    states[statepos].transparencygroup = 0;
+    free(r);
 }
 
 void GFXOutputDev::paintTransparencyGroup(GfxState *state, double *bbox)
 {
-    msg("<verbose> paintTransparencyGroup");
-    createsoftmask = 0;
+    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", blendmodes[state->getBlendMode()], states[statepos].softmask);
+    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;
+   
+    if(state->getBlendMode() == gfxBlendNormal) {
+       gfxdevice_t ops;
+       gfxdevice_ops_init(&ops, this->device, (unsigned char)(state->getFillOpacity()*255));
+       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)
 {
-    msg("<verbose> setSoftMask %f/%f/%f/%f alpha=%d backdrop=%02x%02x%02x",
+    /* 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]));
+    if(!alpha)
+       infofeature("soft masks");
+    else
+       warnfeature("soft masks from alpha channel",0);
+    
+    states[statepos].olddevice = this->device;
+    this->device = (gfxdevice_t*)rfx_calloc(sizeof(gfxdevice_t));
+    gfxdevice_record_init(this->device);
+
+    dbg("softmaskrecording is %08x at statepos %d\n", states[statepos].softmaskrecording, statepos);
+    
+    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;
 }
 
-/*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");
+    
+    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);
+    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);
+#if 0 
+    this->device->startclip(this->device, belowoutline);
+    gfxresult_record_replay(below, this->device);
+    gfxresult_record_replay(mask, this->device);
+    this->device->endclip(this->device);
+    gfxline_free(belowoutline);
+#endif
+    
+    int width = (int)bbox.xmax,height = (int)bbox.ymax;
+    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;
     }
-} myMemCheck;*/
+
+    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++;
+       }
+    }
+    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;