X-Git-Url: http://git.asbjorn.biz/?a=blobdiff_plain;f=lib%2Fpdf%2FGFXOutputDev.cc;h=f6a361d079302e2fa04c59783d05a1ce2da5076b;hb=41a32828cd45f872e07ed621a3929d24de1b161a;hp=2878d5a5e046a75de0d84bb0c0bc7defe29b3a8a;hpb=191439ab6ffe1cad91825ae84286bf720370f863;p=swftools.git diff --git a/lib/pdf/GFXOutputDev.cc b/lib/pdf/GFXOutputDev.cc index 2878d5a..f6a361d 100644 --- a/lib/pdf/GFXOutputDev.cc +++ b/lib/pdf/GFXOutputDev.cc @@ -19,6 +19,7 @@ #include #include +#include #include #include #include @@ -64,6 +65,12 @@ #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 "fonts.h" #include @@ -81,28 +88,105 @@ static int fontnum = 0; static char* lastfontdir = 0; -struct mapping { +struct fontentry { char*pdffont; 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(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(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(" %s not yet %ssupported!",feature,fully?"fully ":""); + if(this->config_break_on_warning) { + msg(" Aborting conversion due to unsupported feature"); + exit(1); + } + } else { + msg(" 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() { this->clipping = 0; this->textRender = 0; + this->createsoftmask = 0; + this->transparencygroup = 0; + this->softmask = 0; + this->grouprecording = 0; + this->isolated = 0; } GBool GFXOutputDev::interpretType3Chars() @@ -161,7 +245,6 @@ GFXOutputDev::GFXOutputDev(parameter_t*p) { this->jpeginfo = 0; this->textmodeinfo = 0; - this->ttfinfo = 0; this->linkinfo = 0; this->pbminfo = 0; this->type3active = 0; @@ -184,19 +267,19 @@ GFXOutputDev::GFXOutputDev(parameter_t*p) 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->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")) { + 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; } @@ -206,7 +289,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) { @@ -550,12 +633,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) { @@ -649,23 +743,14 @@ gfxcolor_t getFillColor(GfxState * state) void GFXOutputDev::fillGfxLine(GfxState *state, gfxline_t*line) { gfxcolor_t col = getFillColor(state); - + if(getLogLevel() >= LOGLEVEL_TRACE) { msg(" 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 +761,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(); @@ -697,8 +791,6 @@ void GFXOutputDev::endframe() device->endclip(device); outer_clip_box = 0; } - - device->endpage(device); } void GFXOutputDev::finish() @@ -865,8 +957,6 @@ void GFXOutputDev::beginString(GfxState *state, GString *s) gfxmatrix_t m = this->current_font_matrix; - /*if(render != 3 && render != 0) - msg(" Text rendering mode %d (%s) not fully supported yet (for text \"%s\")", render, renderModeDesc[render&7], makeStringPrintable(s->getCString()));*/ states[statepos].textRender = render; } @@ -875,8 +965,6 @@ void GFXOutputDev::drawChar(GfxState *state, double x, double y, double originX, double originY, CharCode c, 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) { @@ -1037,6 +1125,16 @@ GBool GFXOutputDev::beginType3Char(GfxState *state, double x, double y, double d { msg(" 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;tdrawchar(device, 0, u[t], &col, &m); + } + /* the character itself is going to be passed using the draw functions */ return gFalse; /* gTrue= is_in_cache? */ } @@ -1063,6 +1161,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) @@ -1118,7 +1218,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]; @@ -1303,19 +1404,32 @@ void GFXOutputDev::drawLink(Link *link, Catalog *catalog) } void GFXOutputDev::saveState(GfxState *state) { - msg(" saveState\n"); - updateAll(state); - if(statepos>=64) { - msg(" 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(" saveState\n"); + updateAll(state); + if(statepos>=64) { + msg(" 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) { - msg(" restoreState\n"); + dbgindent-=2; dbg("restoreState"); + + if(statepos==0) { + msg(" Invalid restoreState"); + return; + } + msg(" restoreState"); + if(states[statepos].softmask) { + clearSoftMask(state); + } updateAll(state); while(states[statepos].clipping) { device->endclip(device); @@ -1324,35 +1438,57 @@ void GFXOutputDev::restoreState(GfxState *state) { statepos--; } +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(char*name) { int i; char*filename=0; - int is_standard_font = 0; msg(" SearchFont(%s)", name); /* see if it is a pdf standard font */ - for(i=0;i Couldn't save default font- is the Temp Directory writable?"); + } else { + msg(" 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 Using %s for %s", fonts[i].filename, name); - } + if(strstr(fonts[i].filename, name)) { return strdup(fonts[i].filename); } } @@ -1386,8 +1522,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; @@ -1442,7 +1599,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; @@ -1787,7 +1944,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 )) @@ -2038,7 +2195,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(" file contains pbm pictures %s",mask?"(masked)":""); @@ -2209,8 +2365,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(" 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 +2374,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(" drawImage %dx%d, %s, %s, inline=%d", width, height, colorMap?"colorMap":"no colorMap", maskColors?"maskColors":"no maskColors", @@ -2237,8 +2394,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(" drawMaskedImage %dx%d, %s, %dx%d mask", width, height, colorMap?"colorMap":"no colorMap", maskWidth, maskHeight); @@ -2255,8 +2413,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(" drawSoftMaskedImage %dx%d, %s, %dx%d mask", width, height, colorMap?"colorMap":"no colorMap", maskWidth, maskHeight); @@ -2268,8 +2427,7 @@ 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); @@ -2279,6 +2437,9 @@ void GFXOutputDev::stroke(GfxState *state) 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,17 +2448,12 @@ 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(" eofill\n"); - dump_outline(line); - } - - device->fill(device, line, &col); + fillGfxLine(state, line); gfxline_free(line); } @@ -2412,47 +2568,294 @@ 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(" 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; + char*colormodename = ""; + BBox rect = mkBBox(state, bbox, this->width, this->height); - 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); + dbg("using clipping rect %f/%f/%f/%f\n", rect.posx,rect.posy,rect.width,rect.height); + msg(" 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(" 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(" paintTransparencyGroup"); - createsoftmask = 0; + 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(" 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(" 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(" 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; +} + +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(" clearSoftMask"); -} + + if(!states[statepos].softmaskrecording || strcmp(this->device->name, "record")) { + msg(" 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); + 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(" Internal error in mask drawing"); + return; + } + int y,x; + for(y=0;ydata[maskimg->width*y]; + gfxcolor_t* l2 = &belowimg->data[belowimg->width*y]; + for(x=0;xa; + } else { + alpha = (77*l1->r + 151*l1->g + 28*l1->b) >> 8; + } + + /* premultiply alpha */ + l2->a = div255(alpha*l2->a); + 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()