xpdf-3.02 fixes
[swftools.git] / lib / pdf / GFXOutputDev.cc
index 8da82ec..df8f27f 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 "../png.h"
 
 #include <math.h>
 
@@ -100,6 +106,34 @@ struct mapping {
 {"Symbol",                "s050000l"},
 {"ZapfDingbats",          "d050000l"}};
 
+static int verbose = 0;
+static int dbgindent = 0;
+static void dbg(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;
@@ -107,7 +141,7 @@ typedef struct _feature
 } feature_t;
 feature_t*featurewarnings = 0;
 
-static void warnfeature(char*feature,char fully)
+void GFXOutputDev::showfeature(char*feature,char fully, char warn)
 {
     feature_t*f = featurewarnings;
     while(f) {
@@ -119,7 +153,23 @@ 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(char*feature,char fully)
+{
+    showfeature(feature,fully,1);
+}
+void GFXOutputDev::infofeature(char*feature)
+{
+    showfeature(feature,0,0);
 }
 
 GFXOutputState::GFXOutputState() {
@@ -127,6 +177,8 @@ GFXOutputState::GFXOutputState() {
     this->textRender = 0;
     this->createsoftmask = 0;
     this->transparencygroup = 0;
+    this->softmask = 0;
+    this->grouprecording = 0;
 }
 
 GBool GFXOutputDev::interpretType3Chars() 
@@ -185,7 +237,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,18 +259,17 @@ 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->parameters = p;
 
     /* configure device */
     while(p) {
-       if(!strcmp(p->name,"forceType0Fonts")) {
-           this->forceType0Fonts = atoi(p->value);
-       } else if(!strcmp(p->name,"fontconfig")) {
+       if(!strcmp(p->name,"fontconfig")) {
            this->config_use_fontconfig = atoi(p->value);
+       } else if(!strcmp(p->name,"breakonwarning")) {
+           this->config_break_on_warning = atoi(p->value);
        }
        p = p->next;
     }
@@ -229,7 +279,7 @@ 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) {
@@ -573,12 +623,23 @@ 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;
+}
 
 void GFXOutputDev::strokeGfxline(GfxState *state, gfxline_t*line)
 {
@@ -677,9 +738,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 +754,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);
@@ -894,8 +949,6 @@ void GFXOutputDev::beginString(GfxState *state, GString *s)
 
     gfxmatrix_t m = this->current_font_matrix;
 
-    /*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;
 }
 
@@ -904,9 +957,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) {
@@ -1093,6 +1143,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) 
@@ -1148,7 +1200,8 @@ void GFXOutputDev::startPage(int pageNum, GfxState *state, double crop_x1, doubl
     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];
@@ -1333,25 +1386,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].textRender = states[statepos-1].textRender;
+    states[statepos].createsoftmask = states[statepos-1].createsoftmask;
+    states[statepos].transparencygroup = states[statepos-1].transparencygroup;
+    states[statepos].clipping = 0;
 };
 
 void GFXOutputDev::restoreState(GfxState *state) {
+  dbgindent-=2; dbg("restoreState");
+
   if(statepos==0) {
       msg("<error> Invalid restoreState");
       return;
   }
   msg("<trace> restoreState");
+  if(states[statepos].softmask) {
+      clearSoftMask(state);
+  }
   updateAll(state);
   while(states[statepos].clipping) {
       device->endclip(device);
@@ -1422,8 +1482,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 +1559,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;
@@ -1823,7 +1904,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
        ))
@@ -2074,7 +2155,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 +2325,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 +2334,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 +2354,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 +2373,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,8 +2387,7 @@ 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);
@@ -2315,8 +2397,8 @@ void GFXOutputDev::stroke(GfxState *state)
 
 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,20 +2408,12 @@ 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);
 }
 
@@ -2454,47 +2528,283 @@ 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;
+}
+
 #if xpdfUpdateVersion >= 16
 void GFXOutputDev::beginTransparencyGroup(GfxState *state, double *bbox,
                                      GfxColorSpace *blendingColorSpace,
                                      GBool isolated, GBool knockout,
                                      GBool forSoftMask)
 {
-    char*colormodename = GfxColorSpace::getColorSpaceModeName(blendingColorSpace->getMode());
+    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].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");
+    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;
+}
+
+static inline Guchar div255(int x) {
+  return (Guchar)((x + (x >> 8) + 0x80) >> 8);
 }
 
 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;
+
+    gfxdevice_t belowrender;
+    gfxdevice_render_init(&belowrender);
+    belowrender.setparameter(&belowrender, "fillwhite", "1"); //for isolated=0?
+    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");
+
+    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++) {
+           l2->a = (77*l1->r + 151*l1->g + 28*l1->b) >> 8;
+
+           /* premultiply alpha */
+           l2->r = div255(l2->a*l2->r);
+           l2->g = div255(l2->a*l2->g);
+           l2->b = div255(l2->a*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;
 }
+  
 #endif
 
 /*class MemCheck