X-Git-Url: http://git.asbjorn.biz/?a=blobdiff_plain;f=lib%2Fpdf%2FGFXOutputDev.cc;h=210f88d62662a5f3fe9cae7aff21b6fce1cc44e1;hb=010a89e2887299503b1e8dfb08417e38ac7bc98e;hp=2878d5a5e046a75de0d84bb0c0bc7defe29b3a8a;hpb=191439ab6ffe1cad91825ae84286bf720370f863;p=swftools.git diff --git a/lib/pdf/GFXOutputDev.cc b/lib/pdf/GFXOutputDev.cc index 2878d5a..210f88d 100644 --- a/lib/pdf/GFXOutputDev.cc +++ b/lib/pdf/GFXOutputDev.cc @@ -19,6 +19,7 @@ #include #include +#include #include #include #include @@ -64,6 +65,11 @@ #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 @@ -100,9 +106,79 @@ 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; + 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; } GBool GFXOutputDev::interpretType3Chars() @@ -161,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; @@ -184,19 +259,17 @@ 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; /* 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 +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) { @@ -550,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) { @@ -649,23 +733,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 +751,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(); @@ -865,8 +949,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 +957,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) { @@ -1063,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) @@ -1118,7 +1200,7 @@ 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 +1385,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); @@ -1386,7 +1481,28 @@ 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) { @@ -1787,7 +1903,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 +2154,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 +2324,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 +2333,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 +2353,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 +2372,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 +2386,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 +2396,8 @@ void GFXOutputDev::stroke(GfxState *state) void GFXOutputDev::fill(GfxState *state) { + dbg("fill"); + GfxPath * path = state->getPath(); gfxline_t*line= gfxPath_to_gfxline(state, path, 1, user_movex + clipmovex, user_movey + clipmovey); fillGfxLine(state, line); @@ -2287,6 +2406,8 @@ void GFXOutputDev::fill(GfxState *state) void GFXOutputDev::eoFill(GfxState *state) { + dbg("eofill"); + GfxPath * path = state->getPath(); gfxcolor_t col = getFillColor(state); @@ -2412,46 +2533,281 @@ 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()); - 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].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; } 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); + 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(" 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 = 255; + l2->a = (77*l1->r + 151*l1->g + 28*l1->b) >> 8; + + /* premultiply alpha... do we need this? (depends on output device) + l2->r = (l2->a*l2->r) >> 8; + l2->g = (l2->a*l2->g) >> 8; + l2->b = (l2->a*l2->b) >> 8; + */ + + 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 {