X-Git-Url: http://git.asbjorn.biz/?a=blobdiff_plain;f=lib%2Fpdf%2FGFXOutputDev.cc;h=d3a9e4e0786d1360abbb7b74a12c5809cad1f207;hb=35f52bc6c36d116f9916b60e3dde4cce3501a41c;hp=8da82ec2b7e57c7672bc4ed3010b07520a6c440e;hpb=77a2b9688db7ae0c9bda7801f19042adcc4ef1ce;p=swftools.git diff --git a/lib/pdf/GFXOutputDev.cc b/lib/pdf/GFXOutputDev.cc index 8da82ec..d3a9e4e 100644 --- a/lib/pdf/GFXOutputDev.cc +++ b/lib/pdf/GFXOutputDev.cc @@ -19,6 +19,7 @@ #include #include +#include #include #include #include @@ -64,12 +65,22 @@ #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 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,7 +164,23 @@ static void warnfeature(char*feature,char fully) f->string = strdup(feature); f->next = featurewarnings; featurewarnings = f; - msg(" %s not yet %ssupported!",feature,fully?"fully ":""); + 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(const char*feature,char fully) +{ + showfeature(feature,fully,1); +} +void GFXOutputDev::infofeature(const char*feature) +{ + showfeature(feature,0,0); } GFXOutputState::GFXOutputState() { @@ -127,11 +188,14 @@ GFXOutputState::GFXOutputState() { 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 +249,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 +271,39 @@ 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->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(char*key, 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 { + msg(" 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 +476,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 +499,11 @@ static void showFontError(GfxFont*font, int nr) else if(nr == 1) msg(" The following font caused problems (substituting):"); else if(nr == 2) - msg(" The following Type 3 Font will be rendered as bitmap:"); + msg(" The following Type 3 Font will be rendered as graphics:"); dumpFontInfo("", 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 +647,27 @@ 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) +#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 +735,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(" Stroke&clip not supported at the same time"); + device->stroke(device, line, width, &col, capType, joinType, miterLimit); + } if(line2) gfxline_free(line2); @@ -677,9 +778,6 @@ void GFXOutputDev::fillGfxLine(GfxState *state, gfxline_t*line) msg(" 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 +794,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 +814,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 +828,6 @@ void GFXOutputDev::endframe() device->endclip(device); outer_clip_box = 0; } - - device->endpage(device); } void GFXOutputDev::finish() @@ -768,7 +868,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 +906,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 +963,14 @@ int getGfxCharID(gfxfont_t*font, int charnr, char *charname, int u) msg(" 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 && umax_unicode && font->unicode2glyph[u]>=0) { + msg(" Char [%d,%s,>%d<] maps to %d\n", charnr, charname, u, font->unicode2glyph[u]); + return font->unicode2glyph[u]; + } if(charnr>=0 && charnrnum_glyphs) { msg(" Char [>%d<,%s,%d] maps to %d\n", charnr, charname, u, charnr); @@ -894,8 +1004,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; } @@ -904,9 +1012,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) { @@ -922,7 +1027,7 @@ void GFXOutputDev::drawChar(GfxState *state, double x, double y, 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(" type3 char at %f/%f", x, y); return; @@ -934,6 +1039,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; @@ -982,6 +1094,22 @@ void GFXOutputDev::drawChar(GfxState *state, double x, double y, 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) { + msg(" Drawing empty character charid=%d", charid); + } + } + if(render == RENDER_FILL) { device->drawchar(device, current_gfxfont, charid, &col, &m); } else { @@ -1024,11 +1152,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; } @@ -1067,6 +1195,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? */ } @@ -1093,6 +1231,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 +1288,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]; @@ -1192,7 +1333,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(" drawlink action=%d\n", action->getKind()); @@ -1333,25 +1474,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].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(" 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) { + 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); @@ -1360,35 +1508,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(" 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); } } @@ -1422,8 +1592,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 +1669,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 +1841,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(" substituteFont(%s)", oldname); if(!(fontname = searchForSuitableFont(gfxFont))) { @@ -1823,7 +2014,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 +2265,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)":""); @@ -2245,8 +2435,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(" 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 +2444,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(" drawImage %dx%d, %s, %s, inline=%d", width, height, colorMap?"colorMap":"no colorMap", maskColors?"maskColors":"no maskColors", @@ -2273,8 +2464,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(" drawMaskedImage %dx%d, %s, %dx%d mask", width, height, colorMap?"colorMap":"no colorMap", maskWidth, maskHeight); @@ -2291,8 +2483,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(" drawSoftMaskedImage %dx%d, %s, %dx%d mask", width, height, colorMap?"colorMap":"no colorMap", maskWidth, maskHeight); @@ -2304,19 +2497,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 +2518,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(" 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 +2537,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 +2550,10 @@ void addGlobalFont(char*filename) } } -void addGlobalLanguageDir(char*dir) +void addGlobalLanguageDir(const char*dir) { if(!globalParams) - globalParams = new GlobalParams(""); + globalParams = new GlobalParams((char*)""); msg(" Adding %s to language pack directories", dir); @@ -2389,7 +2573,7 @@ void addGlobalLanguageDir(char*dir) fclose(fi); } -void addGlobalFontDir(char*dirname) +void addGlobalFontDir(const char*dirname) { #ifdef HAVE_DIRENT_H msg(" Adding %s to font directories", dirname); @@ -2454,49 +2638,296 @@ 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 = GfxColorSpace::getColorSpaceModeName(blendingColorSpace->getMode()); + 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(" 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(" 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(" 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(" 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(" 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); } 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; + 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(" 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()