X-Git-Url: http://git.asbjorn.biz/?a=blobdiff_plain;f=lib%2Fpdf%2FGFXOutputDev.cc;h=0fb2591ab49571a8a4feaac9ca1ef72d13887569;hb=8e4e0278a8fbde7e4f65402f3692724aad4e9698;hp=2ac6e478dfda75f82146ae3b17c97a3812d469a1;hpb=e302be3ec8aeedc8a13dea67e7e42ed08da31ee0;p=swftools.git diff --git a/lib/pdf/GFXOutputDev.cc b/lib/pdf/GFXOutputDev.cc index 2ac6e47..0fb2591 100644 --- a/lib/pdf/GFXOutputDev.cc +++ b/lib/pdf/GFXOutputDev.cc @@ -33,6 +33,9 @@ #ifdef HAVE_SYS_STAT_H #include #endif +#ifdef HAVE_FONTCONFIG +#include +#endif //xpdf header files #include "config.h" #include "gfile.h" @@ -77,6 +80,8 @@ #include +#define SQRT2 1.41421356237309504880 + typedef struct _fontfile { const char*filename; @@ -149,16 +154,9 @@ static void dbg(const char*format, ...) } -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; + feature_t*f = this->featurewarnings; while(f) { if(!strcmp(feature, f->string)) return; @@ -166,8 +164,8 @@ void GFXOutputDev::showfeature(const char*feature, char fully, char warn) } f = (feature_t*)malloc(sizeof(feature_t)); f->string = strdup(feature); - f->next = featurewarnings; - featurewarnings = f; + f->next = this->featurewarnings; + this->featurewarnings = f; if(warn) { msg(" %s not yet %ssupported!",feature,fully?"fully ":""); if(this->config_break_on_warning) { @@ -294,6 +292,8 @@ void unlinkfont(char* filename) } } +static int config_use_fontconfig = 1; +static int fcinitcalled = 0; GFXGlobalParams::GFXGlobalParams() : GlobalParams("") @@ -309,12 +309,179 @@ GFXGlobalParams::~GFXGlobalParams() unlinkfont(pdf2t1map[t].fullfilename); } } +#ifdef HAVE_FONTCONFIG + if(config_use_fontconfig && fcinitcalled) + FcFini(); +#endif +} +#ifdef HAVE_FONTCONFIG +static char fc_ismatch(FcPattern*match, char*family, char*style) +{ + char*fcfamily=0,*fcstyle=0,*fcfullname=0,*filename=0; + FcBool scalable=FcFalse, outline=FcFalse; + FcPatternGetString(match, "family", 0, (FcChar8**)&fcfamily); + FcPatternGetString(match, "style", 0, (FcChar8**)&fcstyle); + FcPatternGetString(match, "file", 0, (FcChar8**)&filename); + FcPatternGetBool(match, "outline", 0, &outline); + FcPatternGetBool(match, "scalable", 0, &scalable); + + if(scalable!=FcTrue || outline!=FcTrue) + return 0; + + if (!strcasecmp(fcfamily, family)) { + msg(" Font %s-%s (%s) is a match for %s%s%s", fcfamily, fcstyle, filename, family, style?"-":"", style?style:""); + return 1; + } else { + //msg(" Font %s-%s (%s) is NOT a match for %s%s%s", fcfamily, fcstyle, filename, family, style?"-":"", style?style:""); + return 0; + } +} +#endif + +char* fontconfig_searchForFont(char*name) +{ +#ifdef HAVE_FONTCONFIG + if(!config_use_fontconfig) + return 0; + + // call init ony once + if (!fcinitcalled) { + fcinitcalled = 1; + + // check whether we have a config file + char* configfile = (char*)FcConfigFilename(0); + int configexists = 0; + FILE*fi = fopen(configfile, "rb"); + if(fi) { + configexists = 1;fclose(fi); + msg(" Initializing FontConfig (configfile=%s)", configfile); + } else { + msg(" Initializing FontConfig (no configfile)"); + } + + if(!configexists) { + /* A fontconfig instance which didn't find a configfile is unbelievably + cranky, so let's just write out a small xml file and make fontconfig + happy */ + FcConfig*c = FcConfigCreate(); + char namebuf[512]; + char* tmpFileName = mktmpname(namebuf); + FILE*fi = fopen(tmpFileName, "wb"); + fprintf(fi, "\n\n");// +#ifdef WIN32 + fprintf(fi, "WINDOWSFONTDIR\n"); +#endif + fprintf(fi, "~/.fonts\n"); +#ifdef WIN32 + fprintf(fi, "WINDOWSTEMPDIR_FONTCONFIG_CACHE\n"); +#endif + fprintf(fi, "~/.fontconfig\n"); + fprintf(fi, "\n"); + fclose(fi); + FcConfigParseAndLoad(c, (FcChar8*)tmpFileName, 1); + FcConfigBuildFonts(c); + FcConfigSetCurrent(c); + } + + if(!FcInit()) { + msg(" FontConfig Initialization failed. Disabling."); + config_use_fontconfig = 0; + return 0; + } + FcConfig * config = FcConfigGetCurrent(); + if(!config) { + msg(" FontConfig Config Initialization failed. Disabling."); + config_use_fontconfig = 0; + return 0; + } + FcFontSet * set = FcConfigGetFonts(config, FcSetSystem); + msg(" FontConfig initialized. Found %d fonts", set?set->nfont:0); + if(!set || !set->nfont) { + msg(" FontConfig has zero fonts. Disabling."); + config_use_fontconfig = 0; + return 0; + } + + if(getLogLevel() >= LOGLEVEL_TRACE) { + int t; + for(t=0;tnfont;t++) { + char*fcfamily=0,*fcstyle=0,*filename=0; + FcBool scalable=FcFalse, outline=FcFalse; + FcPatternGetString(set->fonts[t], "family", 0, (FcChar8**)&fcfamily); + FcPatternGetString(set->fonts[t], "style", 0, (FcChar8**)&fcstyle); + FcPatternGetString(set->fonts[t], "file", 0, (FcChar8**)&filename); + FcPatternGetBool(set->fonts[t], "outline", 0, &outline); + FcPatternGetBool(set->fonts[t], "scalable", 0, &scalable); + if(scalable && outline) { + msg(" %s-%s -> %s", fcfamily, fcstyle, filename); + } + } + } + } + + char*family = strdup(name); + char*style = 0; + char*dash = strchr(family, '-'); + FcPattern*pattern = 0; + if(dash) { + *dash = 0; + style = dash+1; + msg(" FontConfig: Looking for font %s (family=%s style=%s)", name, family, style); + pattern = FcPatternBuild(NULL, FC_OUTLINE, FcTypeBool, FcTrue, FC_SCALABLE, FcTypeBool, FcTrue, FC_FAMILY, FcTypeString, family, FC_STYLE, FcTypeString, style, NULL); + } else { + msg(" FontConfig: Looking for font %s (family=%s)", name, family); + pattern = FcPatternBuild(NULL, FC_OUTLINE, FcTypeBool, FcTrue, FC_SCALABLE, FcTypeBool, FcTrue, FC_FAMILY, FcTypeString, family, NULL); + } + + FcResult result; + FcConfigSubstitute(0, pattern, FcMatchPattern); + FcDefaultSubstitute(pattern); + + FcFontSet*set = FcFontSort(0, pattern, 1, 0, &result); + if(set) { + int t; + for(t=0;tnfont;t++) { + FcPattern*match = set->fonts[t]; + //FcPattern*match = FcFontMatch(0, pattern, &result); + if(fc_ismatch(match, family, style)) { + char*filename=0; + if(FcPatternGetString(match, "file", 0, (FcChar8**)&filename) != FcResultMatch) { + msg(" FontConfig: Couldn't get fontconfig's filename for font %s", name); + filename=0; + } + //FcPatternDestroy(match); + msg(" fontconfig: returning filename %s", filename); + free(family); + FcPatternDestroy(pattern); + FcFontSetDestroy(set); + return filename?strdup(filename):0; + } + } + } + free(family); + FcPatternDestroy(pattern); + FcFontSetDestroy(set); + return 0; +#else + return 0; +#endif } + +static DisplayFontParamKind detectFontType(const char*filename) +{ + if(strstr(filename, ".ttf") || strstr(filename, ".TTF")) + return displayFontTT; + if(strstr(filename, ".pfa") || strstr(filename, ".PFA") || strstr(filename, ".pfb")) + return displayFontT1; + return displayFontTT; +} + DisplayFontParam *GFXGlobalParams::getDisplayFont(GString *fontName) { msg(" looking for font %s in global params", fontName->getCString()); char*name = fontName->getCString(); + /* see if it is a pdf standard font */ int t; for(t=0;tfilename, name)) { if(f->len < bestlen) { @@ -346,9 +513,25 @@ DisplayFontParam *GFXGlobalParams::getDisplayFont(GString *fontName) } f = f->next; } - if(bestfilename) { - DisplayFontParam *dfp = new DisplayFontParam(new GString(fontName), displayFontT1); - dfp->t1.fileName = new GString(bestfilename); + + /* if we didn't find anything up to now, try looking for the + font via fontconfig */ + char*filename = 0; + if(!bestfilename) { + filename = fontconfig_searchForFont(name); + } else { + filename = strdup(bestfilename); + } + + if(filename) { + DisplayFontParamKind kind = detectFontType(filename); + DisplayFontParam *dfp = new DisplayFontParam(new GString(fontName), kind); + if(kind == displayFontTT) { + dfp->tt.fileName = new GString(filename); + } else { + dfp->t1.fileName = new GString(filename); + } + free(filename); return dfp; } return GlobalParams::getDisplayFont(fontName); @@ -394,6 +577,7 @@ GFXOutputDev::GFXOutputDev(InfoOutputDev*info, PDFDoc*doc) this->gfxfontlist = gfxfontlist_create(); memset(states, 0, sizeof(states)); + this->featurewarnings = 0; }; void GFXOutputDev::setParameter(const char*key, const char*value) @@ -676,7 +860,7 @@ void GFXOutputDev::tilingPatternFill(GfxState *state, Object *str, int x0, int y0, int x1, int y1, double xStep, double yStep) #else -void GFXBitmapOutputDev::tilingPatternFill(GfxState *state, Gfx *gfx, Object *str, +void GFXOutputDev::tilingPatternFill(GfxState *state, Gfx *gfx, Object *str, int paintType, Dict *resDict, double *mat, double *bbox, int x0, int y0, int x1, int y1, @@ -844,8 +1028,13 @@ void GFXOutputDev::endPage() device->endclip(device); outer_clip_box = 0; } + this->dashPattern = 0; + /* notice: we're not fully done yet with this page- there might still be + a few calls to drawLink() yet to come */ } +static inline double sqr(double x) {return x*x;} + #define STROKE_FILL 1 #define STROKE_CLIP 2 void GFXOutputDev::strokeGfxline(GfxState *state, gfxline_t*line, int flags) @@ -866,7 +1055,7 @@ void GFXOutputDev::strokeGfxline(GfxState *state, gfxline_t*line, int flags) col.g = colToByte(rgb.g); col.b = colToByte(rgb.b); col.a = (unsigned char)(opaq*255); - + gfx_capType capType = gfx_capRound; if(lineCap == 0) capType = gfx_capButt; else if(lineCap == 1) capType = gfx_capRound; @@ -877,28 +1066,37 @@ void GFXOutputDev::strokeGfxline(GfxState *state, gfxline_t*line, int flags) else if(lineJoin == 1) joinType = gfx_joinRound; else if(lineJoin == 2) joinType = gfx_joinBevel; - int dashnum = 0; - double dashphase = 0; - double * ldash = 0; - state->getLineDash(&ldash, &dashnum, &dashphase); - gfxline_t*line2 = 0; - if(dashnum && ldash) { - float * dash = (float*)malloc(sizeof(float)*(dashnum+1)); + if(this->dashLength && this->dashPattern) { + float * dash = (float*)malloc(sizeof(float)*(this->dashLength+1)); int t; - msg(" %d dashes", dashnum); - msg(" | phase: %f", dashphase); - for(t=0;t | d%-3d: %f", t, ldash[t]); + + /* try to find out how much the transformation matrix would + stretch the dashes, and factor that into the dash lengths. + This is not the entirely correct approach- it would be + better to first convert the path to an unscaled version, + then apply dashing, and then transform the path using + the current transformation matrix. However there are few + PDFs which actually stretch a dashed path in a non-orthonormal + way */ + double tx1, ty1, tx2, ty2; + this->transformXY(state, 0, 0, &tx1, &ty1); + this->transformXY(state, 1, 1, &tx2, &ty2); + double f = sqrt(sqr(tx2-tx1)+sqr(ty2-ty1)) / SQRT2; + + msg(" %d dashes", this->dashLength); + msg(" | phase: %f", this->dashStart); + for(t=0;tdashLength;t++) { + dash[t] = (float)this->dashPattern[t] * f; + msg(" | d%-3d: %f", t, this->dashPattern[t]); } - dash[dashnum] = -1; + dash[this->dashLength] = -1; if(getLogLevel() >= LOGLEVEL_TRACE) { dump_outline(line); } - line2 = gfxtool_dash_line(line, dash, (float)dashphase); + line2 = gfxtool_dash_line(line, dash, (float)(this->dashStart*f)); line = line2; free(dash); msg(" After dashing:"); @@ -909,7 +1107,7 @@ void GFXOutputDev::strokeGfxline(GfxState *state, gfxline_t*line, int flags) width, lineJoin==0?"miter": (lineJoin==1?"round":"bevel"), lineCap==0?"butt": (lineJoin==1?"round":"square"), - dashnum, + this->dashLength, col.r,col.g,col.b,col.a ); dump_outline(line); @@ -1030,6 +1228,18 @@ GFXOutputDev::~GFXOutputDev() if(this->pages) { free(this->pages); this->pages = 0; } + + feature_t*f = this->featurewarnings; + while(f) { + feature_t*next = f->next; + if(f->string) { + free(f->string);f->string =0; + } + f->next = 0; + free(f); + f = next; + } + this->featurewarnings = 0; gfxfontlist_free(this->gfxfontlist, 1);this->gfxfontlist = 0; }; @@ -1137,7 +1347,7 @@ void GFXOutputDev::drawChar(GfxState *state, double x, double y, // check for invisible text -- this is used by Acrobat Capture if (render == RENDER_INVISIBLE) { - col.a = 0; + col.a = 255; if(!config_extrafontdata) return; } @@ -1333,6 +1543,10 @@ void GFXOutputDev::startPage(int pageNum, GfxState *state, double crop_x1, doubl states[statepos].clipbbox.ymin = x1; states[statepos].clipbbox.xmax = x2; states[statepos].clipbbox.ymax = y2; + + this->dashPattern = 0; + this->dashLength = 0; + this->dashStart = 0; } @@ -1559,11 +1773,19 @@ void GFXOutputDev::restoreState(GfxState *state) { } statepos--; } + +void GFXOutputDev::updateLineDash(GfxState *state) +{ + state->getLineDash(&this->dashPattern, &this->dashLength, &this->dashStart); + msg(" updateLineDash, %d dashes", this->dashLength); + if(!this->dashLength) { + this->dashPattern = 0; + } +} void GFXOutputDev::updateLineWidth(GfxState *state) { double width = state->getTransformedLineWidth(); - //swfoutput_setlinewidth(&device, width); } void GFXOutputDev::updateLineCap(GfxState *state) @@ -1618,14 +1840,14 @@ void GFXOutputDev::updateStrokeColor(GfxState *state) } -gfxfont_t* createGfxFont(GfxFont*xpdffont, FontInfo*src, double config_fontquality) +static gfxfont_t* createGfxFont(GfxFont*xpdffont, FontInfo*src, double config_fontquality) { gfxfont_t*font = (gfxfont_t*)malloc(sizeof(gfxfont_t)); memset(font, 0, sizeof(gfxfont_t)); font->glyphs = (gfxglyph_t*)malloc(sizeof(gfxglyph_t)*src->num_glyphs); memset(font->glyphs, 0, sizeof(gfxglyph_t)*src->num_glyphs); - font->id = strdup(getFontID(xpdffont)); + font->id = 0; int t; double quality = (INTERNAL_FONT_SIZE * 200 / config_fontquality) / src->max_size; @@ -1926,14 +2148,14 @@ void GFXOutputDev::drawGeneralImage(GfxState *state, Object *ref, Stream *str, if(!pbminfo && !(str->getKind()==strDCT)) { if(!type3active) { - msg(" file contains pbm pictures %s",mask?"(masked)":""); + msg(" File contains pbm pictures %s",mask?"(masked)":""); pbminfo = 1; } if(mask) msg(" drawing %d by %d masked picture", width, height); } if(!jpeginfo && (str->getKind()==strDCT)) { - msg(" file contains jpeg pictures"); + msg(" File contains jpeg pictures"); jpeginfo = 1; } @@ -1981,7 +2203,7 @@ void GFXOutputDev::drawGeneralImage(GfxState *state, Object *ref, Stream *str, width = realwidth; height = realheight; - free(pic); + delete[] pic; pic = pic2; /* make a black/white palette */ @@ -2003,8 +2225,8 @@ void GFXOutputDev::drawGeneralImage(GfxState *state, Object *ref, Stream *str, } } drawimagelossless(device, pic2, width, height, x1,y1,x2,y2,x3,y3,x4,y4); - free(pic2); - free(pic); + delete[] pic2; + delete[] pic; delete imgStr; if(maskbitmap) free(maskbitmap); return; @@ -2365,14 +2587,15 @@ void GFXOutputDev::beginTransparencyGroup(GfxState *state, double *bbox, void GFXOutputDev::endTransparencyGroup(GfxState *state) { dbgindent-=2; - dbg("endTransparencyGroup"); - msg(" endTransparencyGroup"); - gfxdevice_t*r = this->device; this->device = states[statepos].olddevice; gfxresult_t*recording = r->finish(r); + + dbg("endTransparencyGroup forsoftmask=%d recording=%08x/%08x", states[statepos].createsoftmask, r, recording); + msg(" endTransparencyGroup forsoftmask=%d recording=%08x/%08x", states[statepos].createsoftmask, r, recording); + if(states[statepos].createsoftmask) { states[statepos-1].softmaskrecording = recording; } else { @@ -2405,7 +2628,7 @@ void GFXOutputDev::paintTransparencyGroup(GfxState *state, double *bbox) int blendmode = state->getBlendMode(); if(blendmode == gfxBlendNormal || blendmode == gfxBlendMultiply) { - int alpha = state->getFillOpacity()*255; + int alpha = (int)(state->getFillOpacity()*255); if(blendmode == gfxBlendMultiply && alpha>200) alpha = 128; gfxdevice_t ops; @@ -2466,7 +2689,7 @@ void GFXOutputDev::clearSoftMask(GfxState *state) } gfxresult_t*mask = states[statepos].softmaskrecording; - gfxresult_t*below = this->device->finish(this->device); + gfxresult_t*below = this->device->finish(this->device);free(this->device); this->device = states[statepos].olddevice; /* get outline of all objects below the soft mask */ @@ -2475,14 +2698,13 @@ void GFXOutputDev::clearSoftMask(GfxState *state) gfxresult_record_replay(below, &uniondev); gfxline_t*belowoutline = gfxdevice_union_getunion(&uniondev); uniondev.finish(&uniondev); - gfxbbox_t bbox = gfxline_getbbox(belowoutline); + gfxline_free(belowoutline);belowoutline=0; #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;