X-Git-Url: http://git.asbjorn.biz/?a=blobdiff_plain;f=lib%2Fpdf%2FGFXOutputDev.cc;h=ed07c181ac7938f1648a1071a818b93223eb5f3c;hb=3051901f56742d21be265680e7948a5e7cbaf7ce;hp=2878d5a5e046a75de0d84bb0c0bc7defe29b3a8a;hpb=191439ab6ffe1cad91825ae84286bf720370f863;p=swftools.git diff --git a/lib/pdf/GFXOutputDev.cc b/lib/pdf/GFXOutputDev.cc index 2878d5a..ed07c18 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,33 +92,109 @@ 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 +{ + char*string; + struct _feature*next; +} feature_t; +feature_t*featurewarnings = 0; + +void GFXOutputDev::showfeature(const 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(const char*feature,char fully) +{ + showfeature(feature,fully,1); +} +void GFXOutputDev::infofeature(const char*feature) +{ + showfeature(feature,0,0); +} GFXOutputState::GFXOutputState() { this->clipping = 0; - this->textRender = 0; + this->createsoftmask = 0; + this->transparencygroup = 0; + this->softmask = 0; + this->grouprecording = 0; + this->isolated = 0; } GBool GFXOutputDev::interpretType3Chars() { - return gTrue; + return this->do_interpretType3Chars; } typedef struct _drawnchar @@ -161,7 +248,6 @@ GFXOutputDev::GFXOutputDev(parameter_t*p) { this->jpeginfo = 0; this->textmodeinfo = 0; - this->ttfinfo = 0; this->linkinfo = 0; this->pbminfo = 0; this->type3active = 0; @@ -184,29 +270,42 @@ 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->config_remapunicode=0; + this->do_interpretType3Chars = gTrue; this->parameters = p; + + memset(states, 0, sizeof(states)); /* configure device */ while(p) { - if(!strcmp(p->name,"forceType0Fonts")) { - this->forceType0Fonts = atoi(p->value); - } else if(!strcmp(p->name,"fontconfig")) { - this->config_use_fontconfig = atoi(p->value); - } + setParameter(p->name, p->value); p = p->next; } }; + +void GFXOutputDev::setParameter(const char*key, const char*value) +{ + if(!strcmp(key,"rawtext")) { + this->do_interpretType3Chars = atoi(value)^1; + } else if(!strcmp(key,"breakonwarning")) { + this->config_break_on_warning = atoi(value); + } else if(!strcmp(key,"fontconfig")) { + this->config_use_fontconfig = atoi(value); + } else if(!strcmp(key,"remapunicode")) { + this->config_remapunicode = atoi(value); + } else { + 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) { @@ -379,7 +478,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 @@ -402,11 +501,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); @@ -550,14 +649,45 @@ gfxline_t* gfxPath_to_gfxline(GfxState*state, GfxPath*path, int closed, int user draw.lineTo(&draw, lastx, lasty); } gfxline_t*result = (gfxline_t*)draw.result(&draw); + + gfxline_optimize(result); + return result; } -/*---------------------------------------------------------------------------- - * Primitive Graphic routines - *----------------------------------------------------------------------------*/ +GBool GFXOutputDev::useTilingPatternFill() +{ + infofeature("tiled patterns"); + return gFalse; +} -void GFXOutputDev::strokeGfxline(GfxState *state, gfxline_t*line) +GBool GFXOutputDev::useShadedFills() +{ + infofeature("shaded fills"); + return gFalse; +} + +GBool GFXOutputDev::useDrawForm() +{ + infofeature("forms"); + return gFalse; +} +void GFXOutputDev::drawForm(Ref id) +{ + msg(" drawForm not implemented"); +} +GBool GFXOutputDev::needNonText() +{ + return gTrue; +} +void GFXOutputDev::endPage() +{ + msg(" endPage"); +} + +#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 @@ -625,9 +755,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); @@ -649,23 +793,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 +811,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(); @@ -690,6 +834,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() { @@ -697,8 +848,6 @@ void GFXOutputDev::endframe() device->endclip(device); outer_clip_box = 0; } - - device->endpage(device); } void GFXOutputDev::finish() @@ -739,7 +888,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 @@ -777,7 +926,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 @@ -832,6 +983,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); @@ -864,10 +1023,20 @@ void GFXOutputDev::beginString(GfxState *state, GString *s) this->current_font_matrix.ty = 0; 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; +static gfxline_t* mkEmptyGfxShape(double x, double y) +{ + gfxline_t*line = (gfxline_t*)malloc(sizeof(gfxline_t)); + line->x = x;line->y = y;line->type = gfx_moveTo;line->next = 0; + return line; +} + +static char isValidUnicode(int c) +{ + if(c>=32 && c<0x2fffe) + return 1; + return 0; } void GFXOutputDev::drawChar(GfxState *state, double x, double y, @@ -875,8 +1044,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) { @@ -884,15 +1051,12 @@ void GFXOutputDev::drawChar(GfxState *state, double x, double y, return; } - if(states[statepos].textRender != render) - msg(" Internal error: drawChar.render!=beginString.render"); - gfxcolor_t col = getFillColor(state); Gushort *CIDToGIDMap = 0; GfxFont*font = state->getFont(); - if(font->getType() == fontType3) { + if(font->getType() == fontType3 && do_interpretType3Chars) { /* type 3 chars are passed as graphics */ msg(" type3 char at %f/%f", x, y); return; @@ -904,6 +1068,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; @@ -946,12 +1117,35 @@ void GFXOutputDev::drawChar(GfxState *state, double x, double y, FIXNULL(name),c, u, FIXNULL((char*)current_gfxfont->id), current_gfxfont->num_glyphs); return; } + //useless- the font has already been passed to the output device + //if(!isValidUnicode(current_gfxfont->glyphs[charid].unicode) && isValidUnicode(u)) { + // current_gfxfont->glyphs[charid]. + //} gfxmatrix_t m = this->current_font_matrix; state->transform(x, y, &m.tx, &m.ty); m.tx += user_movex + clipmovex; m.ty += user_movey + clipmovey; + if((!name || strcmp(name, "space")) && charid!=32 && u!=32) + { + gfxline_t*l = current_gfxfont->glyphs[charid].line; + double x,y; + char ok = 0; + while(l) { + if((l->type == gfx_lineTo || l->type == gfx_splineTo) && l->x!=x && l->y!=y) { + ok = 1; + } + l = l->next; + } + if(!ok) { + static int lastemptychar = 0; + if(charid != lastemptychar) + msg(" Drawing empty character charid=%d u=%d", charid, u); + lastemptychar = charid; + } + } + if(render == RENDER_FILL) { device->drawchar(device, current_gfxfont, charid, &col, &m); } else { @@ -970,6 +1164,9 @@ void GFXOutputDev::drawChar(GfxState *state, double x, double y, if(render&RENDER_CLIP) { gfxline_t*add = gfxline_clone(tglyph); current_text_clip = gfxline_append(current_text_clip, add); + if(!current_text_clip) { + current_text_clip = mkEmptyGfxShape(m.tx, m.ty); + } } gfxline_free(tglyph); } @@ -979,8 +1176,6 @@ void GFXOutputDev::endString(GfxState *state) { int render = state->getRender(); msg(" endString() render=%d textstroke=%08x", render, current_text_stroke); - if(states[statepos].textRender != render) - msg(" Internal error: drawChar.render!=beginString.render"); if(current_text_stroke) { /* fillstroke and stroke text rendering objects we can process right @@ -994,11 +1189,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; } @@ -1010,8 +1205,6 @@ void GFXOutputDev::endTextObject(GfxState *state) { int render = state->getRender(); msg(" endTextObject() render=%d textstroke=%08x clipstroke=%08x", render, current_text_stroke, current_text_clip); - if(states[statepos].textRender != render) - msg(" Internal error: drawChar.render!=beginString.render"); if(current_text_clip) { device->setparameter(device, "mark","TXT"); @@ -1037,6 +1230,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 +1266,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 +1323,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]; @@ -1162,7 +1368,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()); @@ -1303,19 +1509,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].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%s%s", states[statepos].softmask?" (end softmask)":"", + states[statepos].clipping?" (end clipping)":""); + if(states[statepos].softmask) { + clearSoftMask(state); + } updateAll(state); while(states[statepos].clipping) { device->endclip(device); @@ -1324,35 +1543,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); } } @@ -1386,8 +1627,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 +1704,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; @@ -1614,7 +1876,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))) { @@ -1674,7 +1936,7 @@ void GFXOutputDev::setXRef(PDFDoc*doc, XRef *xref) this->xref = xref; } -int GFXOutputDev::setGfxFont(char*id, char*name, char*filename, double maxSize) +int GFXOutputDev::setGfxFont(char*id, char*name, char*filename, double maxSize, CharCodeToUnicode*ctu) { gfxfont_t*font = 0; fontlist_t*last=0,*l = this->fontlist; @@ -1702,13 +1964,23 @@ int GFXOutputDev::setGfxFont(char*id, char*name, char*filename, double maxSize) double quality = (1024 * 0.05) / maxSize; msg(" Loading %s...", filename); - font = gfxfont_load(id, filename, quality); + font = gfxfont_load(id, filename, 0, quality); if(!font) { msg(" Couldn't load Font %s (%s)", filename, id); return 0; } msg(" Font %s (%s) loaded successfully", filename, id); + if(this->config_remapunicode && ctu) { + int c; + for(c=0;cnum_glyphs;c++) { + Unicode u[8]; + int uLen = ctu->mapToUnicode(c, u, 8); + if(uLen && !isValidUnicode(font->glyphs[c].unicode) && isValidUnicode(u[0])) + font->glyphs[c].unicode = u[0]; + } + } + l = new fontlist_t; l->font = font; l->filename = strdup(filename); @@ -1754,7 +2026,7 @@ void GFXOutputDev::updateFont(GfxState *state) /* second, see if this is a font which was used before- if so, we are done */ - if(setGfxFont(fontid, fontname, 0, 0)) { + if(setGfxFont(fontid, fontname, 0, 0, gfxFont->getCTU())) { free(fontid); free(fontname); return; @@ -1787,7 +2059,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 )) @@ -1827,8 +2099,8 @@ void GFXOutputDev::updateFont(GfxState *state) //swfoutput_setfont(&device, fontid, fileName); - if(!setGfxFont(fontid, fontname, 0, 0)) { - setGfxFont(fontid, fontname, fileName, maxSize); + if(!setGfxFont(fontid, fontname, 0, 0, gfxFont->getCTU())) { + setGfxFont(fontid, fontname, fileName, maxSize, gfxFont->getCTU()); } if(fileName && del) @@ -2038,7 +2310,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 +2480,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 +2489,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 +2509,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 +2528,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,17 +2542,19 @@ 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); - strokeGfxline(state, line); + strokeGfxline(state, line, 0); gfxline_free(line); } 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,22 +2563,17 @@ 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); } -static char* dirseparator() +static const char* dirseparator() { #ifdef WIN32 return "\\"; @@ -2311,7 +2582,7 @@ static char* dirseparator() #endif } -void addGlobalFont(char*filename) +void addGlobalFont(const char*filename) { fontfile_t f; memset(&f, 0, sizeof(fontfile_t)); @@ -2324,10 +2595,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); @@ -2347,7 +2618,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); @@ -2412,47 +2683,305 @@ 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; + const 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; + 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) { - 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); +} + +static unsigned char clampU8(unsigned char c, unsigned char min, unsigned char max) +{ + if(c < min) c = min; + if(c > max) c = max; + return c; } void GFXOutputDev::clearSoftMask(GfxState *state) { + if(!states[statepos].softmask) + return; + states[statepos].softmask = 0; + dbg("clearSoftMask statepos=%d", statepos); msg(" 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; + } + + l2->a = div255(alpha*l2->a); + + /* DON'T premultiply alpha- this is done by fillbitmap, + depending on the output device */ + //l2->r = div255(alpha*l2->r); + //l2->g = div255(alpha*l2->g); + //l2->b = div255(alpha*l2->b); + + l1++; + l2++; + } + } + gfxline_t*line = gfxline_makerectangle(0,0,width,height); + + gfxmatrix_t matrix; + matrix.m00 = 1.0; matrix.m10 = 0.0; matrix.tx = 0.0; + matrix.m01 = 0.0; matrix.m11 = 1.0; matrix.ty = 0.0; + + this->device->fillbitmap(this->device, line, belowimg, &matrix, 0); + + mask->destroy(mask); + below->destroy(below); + maskresult->destroy(maskresult); + belowresult->destroy(belowresult); + states[statepos].softmaskrecording = 0; +} + /*class MemCheck { public: ~MemCheck()