#ifdef HAVE_SYS_STAT_H
#include <sys/stat.h>
#endif
+#ifdef HAVE_FONTCONFIG
+#include <fontconfig.h>
+#endif
//xpdf header files
#include "config.h"
#include "gfile.h"
#include "../devices/polyops.h"
#include "../devices/render.h"
-#include "../art/libart.h"
-
#include "../png.h"
#include "fonts.h"
#include <math.h>
+#define SQRT2 1.41421356237309504880
+
typedef struct _fontfile
{
const char*filename;
}
-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;
}
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("<warning> %s not yet %ssupported!",feature,fully?"fully ":"");
if(this->config_break_on_warning) {
}
}
+static int config_use_fontconfig = 1;
+static int fcinitcalled = 0;
GFXGlobalParams::GFXGlobalParams()
: GlobalParams("")
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("<debug> Font %s-%s (%s) is a match for %s%s%s", fcfamily, fcstyle, filename, family, style?"-":"", style?style:"");
+ return 1;
+ } else {
+ //msg("<debug> 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("<debug> Initializing FontConfig (configfile=%s)", configfile);
+ } else {
+ msg("<debug> 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, "<?xml version=\"1.0\"?>\n<fontconfig>\n");//<!DOCTYPE fontconfig SYSTEM "fonts.dtd">
+#ifdef WIN32
+ fprintf(fi, "<dir>WINDOWSFONTDIR</dir>\n");
+#endif
+ fprintf(fi, "<dir>~/.fonts</dir>\n");
+#ifdef WIN32
+ fprintf(fi, "<cachedir>WINDOWSTEMPDIR_FONTCONFIG_CACHE</cachedir>\n");
+#endif
+ fprintf(fi, "<cachedir>~/.fontconfig</cachedir>\n");
+ fprintf(fi, "</fontconfig>\n");
+ fclose(fi);
+ FcConfigParseAndLoad(c, (FcChar8*)tmpFileName, 1);
+ FcConfigBuildFonts(c);
+ FcConfigSetCurrent(c);
+ }
+
+ if(!FcInit()) {
+ msg("<debug> FontConfig Initialization failed. Disabling.");
+ config_use_fontconfig = 0;
+ return 0;
+ }
+ FcConfig * config = FcConfigGetCurrent();
+ if(!config) {
+ msg("<debug> FontConfig Config Initialization failed. Disabling.");
+ config_use_fontconfig = 0;
+ return 0;
+ }
+ FcFontSet * set = FcConfigGetFonts(config, FcSetSystem);
+ msg("<verbose> FontConfig initialized. Found %d fonts", set?set->nfont:0);
+ if(!set || !set->nfont) {
+ msg("<debug> FontConfig has zero fonts. Disabling.");
+ config_use_fontconfig = 0;
+ return 0;
+ }
+
+ if(getLogLevel() >= LOGLEVEL_TRACE) {
+ int t;
+ for(t=0;t<set->nfont;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("<trace> %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("<debug> 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("<debug> 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;t<set->nfont;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("<debug> FontConfig: Couldn't get fontconfig's filename for font %s", name);
+ filename=0;
+ }
+ //FcPatternDestroy(match);
+ msg("<debug> 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("<verbose> 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;t<sizeof(pdf2t1map)/sizeof(fontentry);t++) {
}
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);
this->pages = 0;
this->pagebuflen = 0;
this->pagepos = 0;
- this->config_convertgradients=0;
+ this->config_convertgradients=1;
this->config_break_on_warning=0;
this->config_remapunicode=0;
this->config_transparent=0;
this->gfxfontlist = gfxfontlist_create();
memset(states, 0, sizeof(states));
+ this->featurewarnings = 0;
};
void GFXOutputDev::setParameter(const char*key, const char*value)
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,
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)
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;
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("<trace> %d dashes", dashnum);
- msg("<trace> | phase: %f", dashphase);
- for(t=0;t<dashnum;t++) {
- dash[t] = (float)ldash[t];
- msg("<trace> | 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("<trace> %d dashes", this->dashLength);
+ msg("<trace> | phase: %f", this->dashStart);
+ for(t=0;t<this->dashLength;t++) {
+ dash[t] = (float)this->dashPattern[t] * f;
+ msg("<trace> | 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("<trace> After dashing:");
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);
} else {
device->fill(device, gfxline, &col);
}
- free(gfxline);
+ gfxline_free(gfxline);
gfxpoly_free(poly);
} else {
if(flags&STROKE_CLIP)
gfxline_t*line= gfxPath_to_gfxline(state, path, 0, user_movex + clipmovex, user_movey + clipmovey);
if(getLogLevel() >= LOGLEVEL_TRACE) {
- msg("<trace> cliptostrokepath");
+ double width = state->getTransformedLineWidth();
+ msg("<trace> cliptostrokepath width=%f", width);
dump_outline(line);
}
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;
};
// check for invisible text -- this is used by Acrobat Capture
if (render == RENDER_INVISIBLE) {
- col.a = 0;
+ col.a = 255;
if(!config_extrafontdata)
return;
}
states[statepos].clipbbox.ymin = x1;
states[statepos].clipbbox.xmax = x2;
states[statepos].clipbbox.ymax = y2;
+
+ this->dashPattern = 0;
+ this->dashLength = 0;
+ this->dashStart = 0;
}
}
void GFXOutputDev::saveState(GfxState *state) {
- dbg("saveState");dbgindent+=2;
+ dbg("saveState"); dbgindent+=2;
msg("<trace> saveState");
updateAll(state);
}
statepos--;
}
+
+void GFXOutputDev::updateLineDash(GfxState *state)
+{
+ state->getLineDash(&this->dashPattern, &this->dashLength, &this->dashStart);
+ msg("<debug> 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)
}
-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;
unsigned char*newdata;
int x,y;
newdata= (unsigned char*)malloc(newwidth*newheight);
- double fx = (double)(width)/newwidth;
- double fy = (double)(height)/newheight;
+ double fx = ((double)width)/newwidth;
+ double fy = ((double)height)/newheight;
double px = 0;
int blocksize = (int)(8192/(fx*fy));
int r = 8192*256/palettesize;
double ex = px + fx;
int fromx = (int)px;
int tox = (int)ex;
- int xweight1 = (int)(((fromx+1)-px)*256);
+ int xweight1 = (int)((1-(px-fromx))*256);
int xweight2 = (int)((ex-tox)*256);
double py =0;
for(y=0;y<newheight;y++) {
double ey = py + fy;
int fromy = (int)py;
int toy = (int)ey;
- int yweight1 = (int)(((fromy+1)-py)*256);
+ int yweight1 = (int)((1-(py-fromy))*256);
int yweight2 = (int)((ey-toy)*256);
int a = 0;
int xx,yy;
+ if(tox>=width)
+ tox = width-1;
+ if(toy>=height)
+ toy = height-1;
for(xx=fromx;xx<=tox;xx++)
for(yy=fromy;yy<=toy;yy++) {
int b = 1-data[width*yy+xx];
/* TODO: pass image_dpi to device instead */
dev->setparameter(dev, "next_bitmap_is_jpeg", "1");
+ dump_outline(&p1);
dev->fillbitmap(dev, &p1, &img, &m, 0);
}
this->transformXY(state, 1, 0, &x3, &y3);
this->transformXY(state, 1, 1, &x4, &y4);
+ if(type3active) {
+ /* as type 3 bitmaps are antialized, we need to place them
+ at integer coordinates, otherwise flash player's antializing
+ will kick in and make everything blurry */
+ x1 = (int)(x1);y1 = (int)(y1);
+ x2 = (int)(x2);y2 = (int)(y2);
+ x3 = (int)(x3);y3 = (int)(y3);
+ x4 = (int)(x4);y4 = (int)(y4);
+ }
+
if(!pbminfo && !(str->getKind()==strDCT)) {
if(!type3active) {
- msg("<notice> file contains pbm pictures %s",mask?"(masked)":"");
+ msg("<notice> File contains pbm pictures %s",mask?"(masked)":"");
pbminfo = 1;
}
if(mask)
msg("<verbose> drawing %d by %d masked picture", width, height);
}
if(!jpeginfo && (str->getKind()==strDCT)) {
- msg("<notice> file contains jpeg pictures");
+ msg("<notice> File contains jpeg pictures");
jpeginfo = 1;
}
delete imgStr;
return;
}
-
+
width = realwidth;
height = realheight;
- free(pic);
+ delete[] pic;
pic = pic2;
/* make a black/white palette */
pal[t].b = colToByte(rgb.b);
pal[t].a = (unsigned char)(t*r);
}
+
}
gfxcolor_t*pic2 = new gfxcolor_t[width*height];
}
}
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;
GBool forSoftMask)
{
const char*colormodename = "";
-
+
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("beginTransparencyGroup device=%08x %.1f/%.1f/%.1f/%.1f %s isolated=%d knockout=%d forsoftmask=%d", device, bbox[0],bbox[1],bbox[2],bbox[3], colormodename, isolated, knockout, forSoftMask);
msg("<verbose> 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;
void GFXOutputDev::endTransparencyGroup(GfxState *state)
{
dbgindent-=2;
- dbg("endTransparencyGroup");
- msg("<verbose> 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("<verbose> endTransparencyGroup forsoftmask=%d recording=%08x/%08x", states[statepos].createsoftmask, r, recording);
+
if(states[statepos].createsoftmask) {
states[statepos-1].softmaskrecording = recording;
} else {
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;
void GFXOutputDev::setSoftMask(GfxState *state, double *bbox, GBool alpha, Function *transferFunc, GfxColor *rgb)
{
+ if(states[statepos].softmask) {
+ /* shouldn't happen, but *does* happen */
+ clearSoftMask(state);
+ }
+
/* 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("<verbose> setSoftMask %.1f/%.1f/%.1f/%.1f alpha=%d backdrop=%02x%02x%02x",
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);
+ dbg("softmaskrecording is %08x (dev=%08x) at statepos %d\n", states[statepos].softmaskrecording, this->device, statepos);
states[statepos].softmask = 1;
states[statepos].softmask_alpha = alpha;
return;
states[statepos].softmask = 0;
dbg("clearSoftMask statepos=%d", statepos);
- msg("<verbose> clearSoftMask");
+ msg("<verbose> clearSoftMask statepos=%d", statepos);
if(!states[statepos].softmaskrecording || strcmp(this->device->name, "record")) {
msg("<error> Error in softmask/tgroup ordering");
}
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 */
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;