applied transparency patch
[swftools.git] / lib / pdf / GFXOutputDev.cc
index e00096c..8b114ce 100644 (file)
@@ -19,6 +19,7 @@
 
 #include <stdio.h>
 #include <stdlib.h>
+#include <stdarg.h>
 #include <stddef.h>
 #include <string.h>
 #include <unistd.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,24 +92,58 @@ 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
 {
@@ -107,7 +152,7 @@ typedef struct _feature
 } feature_t;
 feature_t*featurewarnings = 0;
 
-static void warnfeature(char*feature,char fully)
+void GFXOutputDev::showfeature(const char*feature,char fully, char warn)
 {
     feature_t*f = featurewarnings;
     while(f) {
@@ -119,19 +164,37 @@ static void warnfeature(char*feature,char fully)
     f->string = strdup(feature);
     f->next = featurewarnings;
     featurewarnings = f;
-    msg("<warning> %s not yet %ssupported!",feature,fully?"fully ":"");
+    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() 
 {
-    return gTrue;
+    return this->do_interpretType3Chars;
 }
 
 typedef struct _drawnchar
@@ -185,7 +248,6 @@ GFXOutputDev::GFXOutputDev(parameter_t*p)
 {
     this->jpeginfo = 0;
     this->textmodeinfo = 0;
-    this->ttfinfo = 0;
     this->linkinfo = 0;
     this->pbminfo = 0;
     this->type3active = 0;
@@ -208,28 +270,45 @@ GFXOutputDev::GFXOutputDev(parameter_t*p)
     this->pages = 0;
     this->pagebuflen = 0;
     this->pagepos = 0;
-  
-    this->forceType0Fonts=1;
     this->config_use_fontconfig=1;
+    this->config_break_on_warning=0;
+    this->config_remapunicode=0;
+    this->config_transparent=0;
+    this->do_interpretType3Chars = gTrue;
 
     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);
-       }
+        setParameter(p->name, p->value);
        p = p->next;
     }
 };
+
+void GFXOutputDev::setParameter(const char*key, const char*value)
+{
+    if(!strcmp(key,"rawtext")) {
+        this->do_interpretType3Chars = atoi(value)^1;
+    } else if(!strcmp(key,"breakonwarning")) {
+       this->config_break_on_warning = atoi(value);
+    } else if(!strcmp(key,"fontconfig")) {
+        this->config_use_fontconfig = atoi(value);
+    } else if(!strcmp(key,"remapunicode")) {
+        this->config_remapunicode = atoi(value);
+    } else if(!strcmp(p->name,"transparent")) {
+        this->config_transparent = atoi(p->value);
+    } else {
+        msg("<warning> Ignored parameter: %s=%s", key, value);
+    }
+}
   
 void GFXOutputDev::setDevice(gfxdevice_t*dev)
 {
     parameter_t*p = this->parameters;
 
-    /* TODO: get rid of this */
+    /* pass parameters to output device */
     this->device = dev;
     if(this->device) {
        while(p) {
@@ -402,7 +481,7 @@ static char* gfxstate2str(GfxState *state)
   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,11 +504,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);
@@ -573,14 +652,45 @@ 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");
+}
 
-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
@@ -648,9 +758,23 @@ 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(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);
@@ -677,9 +801,6 @@ void GFXOutputDev::fillGfxLine(GfxState *state, gfxline_t*line)
         msg("<trace> fill %02x%02x%02x%02x\n", col.r, col.g, col.b, col.a);
         dump_outline(line);
     }
-    if(states[statepos].transparencygroup && col.a != 255)
-       return;
-
     device->fill(device, line, &col);
 }
 
@@ -696,9 +817,6 @@ void GFXOutputDev::clipToGfxLine(GfxState *state, gfxline_t*line)
 
 void GFXOutputDev::clip(GfxState *state) 
 {
-    if(states[statepos].createsoftmask)
-       return;
-
     GfxPath * path = state->getPath();
     gfxline_t*line = gfxPath_to_gfxline(state, path, 1, user_movex + clipmovex, user_movey + clipmovey);
     clipToGfxLine(state, line);
@@ -719,6 +837,13 @@ void GFXOutputDev::eoClip(GfxState *state)
     states[statepos].clipping++;
     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);
+    strokeGfxline(state, line, STROKE_FILL|STROKE_CLIP);
+    gfxline_free(line);
+}
 
 void GFXOutputDev::endframe()
 {
@@ -726,8 +851,6 @@ void GFXOutputDev::endframe()
        device->endclip(device);
        outer_clip_box = 0;
     }
-
-    device->endpage(device);
 }
 
 void GFXOutputDev::finish()
@@ -768,7 +891,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
@@ -806,7 +929,9 @@ char* makeStringPrintable(char*str)
 
 int getGfxCharID(gfxfont_t*font, int charnr, char *charname, int u)
 {
-    char*uniname = 0;
+    const char*uniname = 0;
+    if(!font)
+        return charnr;
     if(u>0) {
        int t;
        /* find out char name from unicode index 
@@ -861,6 +986,14 @@ int getGfxCharID(gfxfont_t*font, int charnr, char *charname, int u)
        msg("<debug> Char [%d,%s,>%d<] maps to %d\n", charnr, charname, u, font->unicode2glyph[u]);
        return font->unicode2glyph[u];
     }
+    /* try to use the unicode|0xe000 (needed for some WingDings fonts)
+       FIXME: do this only if we know the font is wingdings?
+     */
+    u |= 0xe000;
+    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];
+    }
 
     if(charnr>=0 && charnr<font->num_glyphs) {
        msg("<debug> Char [>%d<,%s,%d] maps to %d\n", charnr, charname, u, charnr);
@@ -893,10 +1026,20 @@ void GFXOutputDev::beginString(GfxState *state, GString *s)
     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,
@@ -904,9 +1047,6 @@ void GFXOutputDev::drawChar(GfxState *state, double x, double y,
                        double originX, double originY,
                        CharCode c, int nBytes, Unicode *_u, int uLen)
 {
-    if(states[statepos].createsoftmask)
-       return;
-
     int render = state->getRender();
     // check for invisible text -- this is used by Acrobat Capture
     if (render == 3) {
@@ -914,15 +1054,12 @@ void GFXOutputDev::drawChar(GfxState *state, double x, double y,
        return;
     }
 
-    if(states[statepos].textRender != render)
-       msg("<error> Internal error: drawChar.render!=beginString.render");
-
     gfxcolor_t col = getFillColor(state);
 
     Gushort *CIDToGIDMap = 0;
     GfxFont*font = state->getFont();
 
-    if(font->getType() == fontType3) {
+    if(font->getType() == fontType3 && do_interpretType3Chars) {
        /* type 3 chars are passed as graphics */
        msg("<debug> type3 char at %f/%f", x, y);
        return;
@@ -934,6 +1071,13 @@ void GFXOutputDev::drawChar(GfxState *state, double x, double y,
     if(uLen)
        u = _u[0];
 
+/*    char*fontname = getFontName(font);
+    if(u<256 && strstr(fontname, "ingdings")) {
+        // symbols are at 0xe000 in the unicode table
+        u |= 0xe000;
+    }
+    free(fontname);*/
+
     if(font->isCIDFont()) {
        GfxCIDFont*cfont = (GfxCIDFont*)font;
 
@@ -976,12 +1120,35 @@ void GFXOutputDev::drawChar(GfxState *state, double x, double y,
                FIXNULL(name),c, u, FIXNULL((char*)current_gfxfont->id), current_gfxfont->num_glyphs);
        return;
     }
+    //useless- the font has already been passed to the output device
+    //if(!isValidUnicode(current_gfxfont->glyphs[charid].unicode) && isValidUnicode(u)) {
+    //    current_gfxfont->glyphs[charid].
+    //}
 
     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((!name || strcmp(name, "space")) && charid!=32 && u!=32)
+    {
+        gfxline_t*l = current_gfxfont->glyphs[charid].line;
+        double x,y;
+        char ok = 0;
+        while(l) {
+            if((l->type == gfx_lineTo || l->type == gfx_splineTo) && l->x!=x && l->y!=y) {
+                ok = 1;
+            }
+            l = l->next;
+        }
+        if(!ok) {
+           static int lastemptychar = 0;
+           if(charid != lastemptychar)
+               msg("<warning> Drawing empty character charid=%d u=%d", charid, u);
+           lastemptychar = charid;
+        }
+    }
+
     if(render == RENDER_FILL) {
        device->drawchar(device, current_gfxfont, charid, &col, &m);
     } else {
@@ -1000,6 +1167,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 +1179,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 +1192,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 +1208,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");
@@ -1067,6 +1233,16 @@ GBool GFXOutputDev::beginType3Char(GfxState *state, double x, double y, double d
 {
     msg("<debug> beginType3Char %d, %08x, %d", code, *u, uLen);
     type3active = 1;
+
+    /*int t;
+
+    gfxcolor_t col={255,0,0,0};
+    gfxmatrix_t m = {1,0,0, 0,1,0};
+
+    for(t=0;t<uLen;t++) {
+       device->drawchar(device, 0, u[t], &col, &m);
+    }*/
+
     /* the character itself is going to be passed using the draw functions */
     return gFalse; /* gTrue= is_in_cache? */
 }
@@ -1093,6 +1269,8 @@ void GFXOutputDev::startFrame(int width, int height)
     }
 
     device->startpage(device, width, height);
+    this->width = width;
+    this->height = height;
 }
 
 void GFXOutputDev::startPage(int pageNum, GfxState *state, double crop_x1, double crop_y1, double crop_x2, double crop_y2) 
@@ -1145,10 +1323,12 @@ 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];
@@ -1192,7 +1372,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());
@@ -1333,25 +1513,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].textRender = states[statepos-1].textRender;
-  states[statepos].createsoftmask = states[statepos-1].createsoftmask;
-  states[statepos].transparencygroup = states[statepos-1].transparencygroup;
-  states[statepos].clipping = 0;
+    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) {
+  dbgindent-=2; dbg("restoreState");
+
   if(statepos==0) {
       msg("<error> Invalid restoreState");
       return;
   }
-  msg("<trace> restoreState");
+  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);
@@ -1360,35 +1547,57 @@ void GFXOutputDev::restoreState(GfxState *state) {
   statepos--;
 }
 
-char* GFXOutputDev::searchFont(char*name) 
+char* writeOutStdFont(fontentry* f)
+{
+    FILE*fi;
+    char namebuf1[512];
+    char namebuf2[512];
+    char* tmpFileName = mktmpname(namebuf1);
+
+    sprintf(namebuf2, "%s.afm", tmpFileName);
+    fi = fopen(namebuf2, "wb");
+    if(!fi)
+        return 0;
+    fwrite(f->afm, 1, f->afmlen, fi);
+    fclose(fi);
+
+    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);
+}
+
+char* GFXOutputDev::searchFont(const 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++) 
+    for(i=0;i<sizeof(pdf2t1map)/sizeof(fontentry);i++) 
     {
        if(!strcmp(name, pdf2t1map[i].pdffont))
        {
-           name = pdf2t1map[i].filename;
-           is_standard_font = 1;
-           break;
+            if(!pdf2t1map[i].fullfilename) {
+                pdf2t1map[i].fullfilename = writeOutStdFont(&pdf2t1map[i]);
+                if(!pdf2t1map[i].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[i].fullfilename);
+                }
+            }
+           return strdup(pdf2t1map[i].fullfilename);
        }
     }
-    /* look in all font files */
+    /* else 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);
-           }
+       if(strstr(fonts[i].filename, name)) {
            return strdup(fonts[i].filename);
        }
     }
@@ -1422,8 +1631,29 @@ 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");
 }
 
+
 void GFXOutputDev::updateStrokeColor(GfxState *state) 
 {
     GfxRGB rgb;
@@ -1478,7 +1708,7 @@ char*GFXOutputDev::writeEmbeddedFontToFile(XRef*ref, GfxFont*font)
       }
       FoFiType1C *cvt = FoFiType1C::make(fontBuf, fontLen);
       if(!cvt) return 0;
-      cvt->convertToType1(NULL, gTrue, FoFiWrite, f);
+      cvt->convertToType1(0, NULL, gTrue, FoFiWrite, f);
       //cvt->convertToCIDType0("test", f);
       //cvt->convertToType0("test", f);
       delete cvt;
@@ -1650,7 +1880,7 @@ char* GFXOutputDev::searchForSuitableFont(GfxFont*gfxFont)
 
 char* GFXOutputDev::substituteFont(GfxFont*gfxFont, char* oldname)
 {
-    char*fontname = 0, *filename = 0;
+    const char*fontname = 0, *filename = 0;
     msg("<notice> substituteFont(%s)", oldname);
 
     if(!(fontname = searchForSuitableFont(gfxFont))) {
@@ -1710,7 +1940,7 @@ void GFXOutputDev::setXRef(PDFDoc*doc, XRef *xref)
     this->xref = xref;
 }
 
-int GFXOutputDev::setGfxFont(char*id, char*name, char*filename, double maxSize)
+int GFXOutputDev::setGfxFont(char*id, char*name, char*filename, double maxSize, CharCodeToUnicode*ctu)
 {
     gfxfont_t*font = 0;
     fontlist_t*last=0,*l = this->fontlist;
@@ -1738,13 +1968,23 @@ int GFXOutputDev::setGfxFont(char*id, char*name, char*filename, double maxSize)
     double quality = (1024 * 0.05) / maxSize;
    
     msg("<verbose> Loading %s...", filename);
-    font = gfxfont_load(id, filename, quality);
+    font = gfxfont_load(id, filename, 0, quality);
     if(!font) {
        msg("<verbose> Couldn't load Font %s (%s)", filename, id);
        return 0;
     }
     msg("<verbose> Font %s (%s) loaded successfully", filename, id);
 
+    if(this->config_remapunicode && ctu) {
+       int c;
+       for(c=0;c<font->num_glyphs;c++) {
+           Unicode u[8];
+           int uLen = ctu->mapToUnicode(c, u, 8);
+           if(uLen && !isValidUnicode(font->glyphs[c].unicode) && isValidUnicode(u[0]))
+               font->glyphs[c].unicode = u[0];
+       }
+    }
+
     l = new fontlist_t;
     l->font = font;
     l->filename = strdup(filename);
@@ -1790,7 +2030,7 @@ void GFXOutputDev::updateFont(GfxState *state)
 
     /* second, see if this is a font which was used before-
        if so, we are done */
-    if(setGfxFont(fontid, fontname, 0, 0)) {
+    if(setGfxFont(fontid, fontname, 0, 0, gfxFont->getCTU())) {
        free(fontid);
        free(fontname);
        return;
@@ -1823,7 +2063,7 @@ void GFXOutputDev::updateFont(GfxState *state)
     if(embedded &&
        (gfxFont->getType() == fontType1 ||
        gfxFont->getType() == fontType1C ||
-       (gfxFont->getType() == fontCIDType0C && forceType0Fonts) ||
+        gfxFont->getType() == fontCIDType0C ||
        gfxFont->getType() == fontTrueType ||
        gfxFont->getType() == fontCIDType2
        ))
@@ -1863,8 +2103,8 @@ void GFXOutputDev::updateFont(GfxState *state)
 
     //swfoutput_setfont(&device, fontid, fileName);
     
-    if(!setGfxFont(fontid, fontname, 0, 0)) {
-       setGfxFont(fontid, fontname, fileName, maxSize);
+    if(!setGfxFont(fontid, fontname, 0, 0, gfxFont->getCTU())) {
+       setGfxFont(fontid, fontname, fileName, maxSize, gfxFont->getCTU());
     }
    
     if(fileName && del)
@@ -2074,7 +2314,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)":"");
@@ -2245,8 +2484,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,8 +2493,10 @@ 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",
@@ -2273,8 +2513,9 @@ 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);
@@ -2291,8 +2532,9 @@ 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);
@@ -2304,19 +2546,18 @@ void GFXOutputDev::drawSoftMaskedImage(GfxState *state, Object *ref, Stream *str
 
 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);
@@ -2326,25 +2567,17 @@ void GFXOutputDev::fill(GfxState *state)
 
 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,7 +2586,7 @@ static char* dirseparator()
 #endif
 }
 
-void addGlobalFont(char*filename)
+void addGlobalFont(const char*filename)
 {
     fontfile_t f;
     memset(&f, 0, sizeof(fontfile_t));
@@ -2366,10 +2599,10 @@ void addGlobalFont(char*filename)
     }
 }
 
-void addGlobalLanguageDir(char*dir)
+void addGlobalLanguageDir(const char*dir)
 {
     if(!globalParams)
-        globalParams = new GlobalParams("");
+        globalParams = new GlobalParams((char*)"");
     
     msg("<notice> Adding %s to language pack directories", dir);
 
@@ -2389,7 +2622,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);
@@ -2454,52 +2687,305 @@ void GFXOutputDev::preparePage(int pdfpage, int outputpage)
     if(pdfpage>this->pagepos)
        this->pagepos = pdfpage;
 }
-  
-#if xpdfUpdateVersion >= 16
+
+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 = "";
+    const char*colormodename = "";
+    BBox rect = mkBBox(state, bbox, this->width, this->height);
+
     if(blendingColorSpace) {
        colormodename = GfxColorSpace::getColorSpaceModeName(blendingColorSpace->getMode());
     }
+    dbg("beginTransparencyGroup %.1f/%.1f/%.1f/%.1f %s isolated=%d knockout=%d forsoftmask=%d", bbox[0],bbox[1],bbox[2],bbox[3], colormodename, isolated, knockout, forSoftMask);
+    dbg("using clipping rect %f/%f/%f/%f\n", rect.posx,rect.posy,rect.width,rect.height);
     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));
+
+    gfxdevice_record_init(this->device);
     
-    if(!forSoftMask) {
+    /*if(!forSoftMask) { ////???
        state->setFillOpacity(0.0);
-    }
-    warnfeature("transparency groups",1);
+    }*/
+    dbgindent+=2;
 }
 
 void GFXOutputDev::endTransparencyGroup(GfxState *state)
 {
+    dbgindent-=2;
+    dbg("endTransparencyGroup");
     msg("<verbose> endTransparencyGroup");
+
+    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");
+    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)
 {
+    /* 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);
+    
+    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;
+}
+
+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)
+{
+    if(c < min) c = min;
+    if(c > max) c = max;
+    return c;
 }
 
 void GFXOutputDev::clearSoftMask(GfxState *state)
 {
+    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;
+    }
+
+    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()