X-Git-Url: http://git.asbjorn.biz/?p=swftools.git;a=blobdiff_plain;f=lib%2Fpdf%2FInfoOutputDev.cc;h=8fb751af6e00e7ea2e49b0489dc2398f898f8c7b;hp=830776d95d65749f6e7a9e13eae60a6793989bd5;hb=fc713269e9d26d1be92f70e1ff0d508c94a65dba;hpb=a2f21288d10ce4e448ba07a72463aa2689d59ed3 diff --git a/lib/pdf/InfoOutputDev.cc b/lib/pdf/InfoOutputDev.cc index 830776d..8fb751a 100644 --- a/lib/pdf/InfoOutputDev.cc +++ b/lib/pdf/InfoOutputDev.cc @@ -1,22 +1,303 @@ +#include "config.h" +#include "Object.h" #include "InfoOutputDev.h" +#include "SplashOutputDev.h" +#ifdef HAVE_POPPLER +#include +#include +#include +#include +#else +#include "SplashTypes.h" +#include "SplashPath.h" +#include "SplashFont.h" +#include "SplashFontFile.h" +#endif #include "GfxState.h" #include "../log.h" +#include "../types.h" +#include "../q.h" +#include "../gfxfont.h" #include +#include -InfoOutputDev::InfoOutputDev() +int config_addspace = 1; +int config_fontquality = 10; +int config_bigchar = 0; + +InfoOutputDev::InfoOutputDev(XRef*xref) { num_links = 0; - num_images = 0; + num_jpeg_images = 0; + num_ppm_images = 0; + num_textfields = 0; num_fonts = 0; - id2font = new GHash(); + num_polygons= 0; + currentfont = 0; + currentglyph = 0; + id2font = new GHash(1); + SplashColor white = {255,255,255}; + splash = new SplashOutputDev(splashModeRGB8,320,0,white,0,0); + splash->startDoc(xref); } InfoOutputDev::~InfoOutputDev() { - delete id2font; + GHashIter*i; + id2font->startIter(&i); + GString*key; + FontInfo*fontinfo; + while(id2font->getNext(&i, &key, (void**)&fontinfo)) { + delete fontinfo; + } + id2font->killIter(&i); + + delete id2font;id2font=0; + delete splash;splash=0; +} + +void FontInfo::grow(int size) +{ + if(size >= this->num_glyphs) { + this->glyphs = (GlyphInfo**)realloc(this->glyphs, sizeof(GlyphInfo*)*(size)); + this->kerning = (dict_t**)realloc(this->kerning, sizeof(dict_t*)*(size)); + memset(&this->glyphs[this->num_glyphs], 0, sizeof(SplashPath*)*((size)-this->num_glyphs)); + memset(&this->kerning[this->num_glyphs], 0, sizeof(dict_t*)*((size)-this->num_glyphs)); + this->num_glyphs = size; + } +} +FontInfo::FontInfo(char*id) +{ + this->id = strdup(id); + this->seen = 0; + this->num_glyphs = 0; + this->glyphs = 0; + this->kerning = 0; + this->splash_font = 0; + this->lastchar = -1; + this->lastx = 0; + this->lasty = 0; + this->gfxfont = 0; + this->space_char = -1; + this->ascender = 0; + this->descender = 0; +} +FontInfo::~FontInfo() +{ + if(this->id) {free(this->id);this->id=0;} + this->font = 0; + int t; + for(t=0;tpath;glyphs[t]->path = 0; + delete glyphs[t]; + glyphs[t]=0; + } + } + free(glyphs);glyphs=0; + if(this->gfxfont) + gfxfont_free(this->gfxfont); + + if(kerning) { + for(t=0;tnum_glyphs;t++) { + gfxglyph_t*g = &font->glyphs[t]; + if(GLYPH_IS_SPACE(g)) { + if(g->unicode == 32) { + /* now that we have found a space char, make sure it's unique */ + int s; + for(s=0;snum_glyphs;s++) { + if(s!=t && font->glyphs[s].unicode==32) + font->glyphs[s].unicode=0; + } + return t; + } + } + } + return -1; +} + +static int addSpace(gfxfont_t*font) +{ + /* first, make sure the new space char is the only char that'll use unicode 32 */ + int t; + for(t=0;tnum_glyphs;t++) { + if(font->glyphs[t].unicode==32) + font->glyphs[t].unicode=0; + } + font->num_glyphs++; + font->glyphs = (gfxglyph_t*)realloc(font->glyphs, sizeof(gfxglyph_t)*font->num_glyphs); + gfxglyph_t*g = &font->glyphs[font->num_glyphs-1]; + memset(g, 0, sizeof(*g)); + g->unicode = 32; + g->advance = fabs(font->ascent + font->descent)*2 / 3; + if(font->max_unicode > 32) + font->unicode2glyph[32] = font->num_glyphs-1; +#if 0 + g->line = gfxline_makerectangle(0, -font->ascent, g->advance, font->descent); +#endif + return font->num_glyphs-1; } + +static gfxfont_t* createGfxFont(FontInfo*src) +{ + gfxfont_t*font = (gfxfont_t*)rfx_calloc(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 = 0; + int t; + + double quality = (INTERNAL_FONT_SIZE * 200 / config_fontquality) / src->max_size; + double scale = 1; + //printf("%d glyphs\n", font->num_glyphs); + font->num_glyphs = 0; + font->ascent = fabs(src->ascender); + font->descent = fabs(src->descender); + + for(t=0;tnum_glyphs;t++) { + if(src->glyphs[t]) { + SplashPath*path = src->glyphs[t]->path; + int len = path?path->getLength():0; + //printf("glyph %d) %08x (%d line segments)\n", t, path, len); + gfxglyph_t*glyph = &font->glyphs[font->num_glyphs]; + src->glyphs[t]->glyphid = font->num_glyphs; + glyph->unicode = src->glyphs[t]->unicode; + gfxdrawer_t drawer; + gfxdrawer_target_gfxline(&drawer); + int s; + int count = 0; + double xmax = 0; + for(s=0;sgetPoint(s, &x, &y, &f); + if(!s || x > xmax) + xmax = x; + if(f&splashPathFirst) { + drawer.moveTo(&drawer, x*scale, y*scale); + } + if(f&splashPathCurve) { + double x2,y2; + path->getPoint(++s, &x2, &y2, &f); + if(f&splashPathCurve) { + double x3,y3; + path->getPoint(++s, &x3, &y3, &f); + gfxdraw_cubicTo(&drawer, x*scale, y*scale, x2*scale, y2*scale, x3*scale, y3*scale, quality); + } else { + drawer.splineTo(&drawer, x*scale, y*scale, x2*scale, y2*scale); + } + } else { + drawer.lineTo(&drawer, x*scale, y*scale); + } + // printf("%f %f %s %s\n", x, y, (f&splashPathCurve)?"curve":"", + // (f&splashPathFirst)?"first":"", + // (f&splashPathLast)?"last":""); + } + + glyph->line = (gfxline_t*)drawer.result(&drawer); + if(src->glyphs[t]->advance>0) { + glyph->advance = src->glyphs[t]->advance; + } else { + glyph->advance = xmax*scale; + } + if(config_bigchar) { + double max = src->glyphs[t]->advance_max; + if(max>0 && max > glyph->advance) { + glyph->advance = max; + } + } + + font->num_glyphs++; + } + } + + int kerning_size = 0; + for(t=0;tnum_glyphs;t++) { + dict_t* d = src->kerning[t]; + if(!d) continue; + DICT_ITERATE_ITEMS(d,void*,key,mtf_t*,m) { + if(m) { + kerning_size++; + } + } + } + font->kerning_size = kerning_size; + font->kerning = (gfxkerning_t*)malloc(sizeof(gfxkerning_t)*kerning_size); + int pos = 0; + for(t=0;tnum_glyphs;t++) { + dict_t* d = src->kerning[t]; + if(!d) continue; + DICT_ITERATE_ITEMS(d,void*,key,mtf_t*,m) { + if(m) { + font->kerning[pos].c1 = src->glyphs[t]->glyphid; + font->kerning[pos].c2 = src->glyphs[(int)(ptroff_t)key]->glyphid; + font->kerning[pos].advance = (int)(ptroff_t)m->first->key; + pos++; + } + } + } + //int advance = (int)(ptroff_t)m->first->key; + + return font; +} + +static float find_average_glyph_advance(gfxfont_t*f) +{ + if(!f->num_glyphs) + return 0.0; + + float*values = (float*)malloc(sizeof(float)*f->num_glyphs); + int t; + for(t=0;tnum_glyphs;t++) { + values[t] = f->glyphs[t].advance; + } + float m = medianf(values, f->num_glyphs); + free(values); + return m; +} + +gfxfont_t* FontInfo::getGfxFont() +{ + if(!this->gfxfont) { + this->gfxfont = createGfxFont(this); + this->gfxfont->id = strdup(this->id); + this->space_char = findSpace(this->gfxfont); + this->average_advance = find_average_glyph_advance(this->gfxfont); + + if(this->space_char>=0) { + msg(" Font %s has space char %d (unicode=%d)", + this->id, this->space_char, + this->gfxfont->glyphs[this->space_char].unicode); + } else if(config_addspace) { + this->space_char = addSpace(this->gfxfont); + msg(" Appending space char to font %s, position %d, width %f", this->gfxfont->id, this->space_char, this->gfxfont->glyphs[this->space_char].advance); + } + gfxfont_fix_unicode(this->gfxfont); + } + return this->gfxfont; +} + GBool InfoOutputDev::upsideDown() {return gTrue;} GBool InfoOutputDev::useDrawChar() {return gTrue;} GBool InfoOutputDev::interpretType3Chars() {return gTrue;} +GBool InfoOutputDev::useTilingPatternFill() {return gTrue;} + void InfoOutputDev::startPage(int pageNum, GfxState *state, double crop_x1, double crop_y1, double crop_x2, double crop_y2) { double x1,y1,x2,y2; @@ -30,10 +311,23 @@ void InfoOutputDev::startPage(int pageNum, GfxState *state, double crop_x1, doub this->y2 = (int)y2; msg(" Generating info structure for page %d", pageNum); } +void InfoOutputDev::endPage() +{ +} void InfoOutputDev::drawLink(Link *link, Catalog *catalog) { num_links++; } + +/* } else if(!strcmp(key,"fontquality")) { + this->config_fontquality = atof(value); + if(this->config_fontquality<=1) + this->config_fontquality=1; + } else if(!strcmp(key,"bigchar")) { + this->config_bigchar = atoi(value); + } + */ + double InfoOutputDev::getMaximumFontSize(char*id) { FontInfo*info = (FontInfo*)id2font->lookup(id); @@ -44,39 +338,79 @@ double InfoOutputDev::getMaximumFontSize(char*id) return info->max_size; } -static char*getFontID(GfxFont*font) +char*getFontID(GfxFont*font) { Ref*ref = font->getID(); GString*gstr = font->getName(); char* fname = gstr==0?0:gstr->getCString(); char buf[128]; if(fname==0) { - sprintf(buf, "font-%d-%d", ref->num, ref->gen); + if(font->getType() == fontType3) { + sprintf(buf, "t3font-%d-%d", ref->num, ref->gen); + } else { + sprintf(buf, "font-%d-%d", ref->num, ref->gen); + } } else { sprintf(buf, "%s-%d-%d", fname, ref->num, ref->gen); } return strdup(buf); } - void InfoOutputDev::updateFont(GfxState *state) { GfxFont*font = state->getFont(); - if(!font) + if(!font) { + currentfont = 0; + return; + } + if(font->getType() == fontType3) { + currentfont = 0; return; + } char*id = getFontID(font); - - FontInfo*info = (FontInfo*)id2font->lookup(id); - if(!info) { - GString* idStr = new GString(id); - info = new FontInfo; - info->font = font; - info->max_size = 0; - id2font->add(idStr, (void*)info); - free(id); - num_fonts++; + + if(currentfont) + currentfont->splash_font = 0; + + currentfont = (FontInfo*)id2font->lookup(id); + if(!currentfont) { + currentfont = new FontInfo(id); + currentfont->font = font; + currentfont->max_size = 0; + GString* idStr = new GString(id); + id2font->add(idStr, (void*)currentfont); + num_fonts++; + } + + state->setCTM(1.0,0,0,1.0,0,0); + splash->updateCTM(state, 0,0,0,0,0,0); + state->setTextMat(1.0,0,0,1.0,0,0); + state->setFont(font, 1024.0); + splash->doUpdateFont(state); + currentfont->splash_font = splash->getCurrentFont(); + if(currentfont->splash_font) { + currentfont->ascender = currentfont->splash_font->ascender; + currentfont->descender = currentfont->splash_font->descender; + } else { + currentfont->ascender = currentfont->descender = 0; } - currentfont = info; + + free(id); +} + +void InfoOutputDev::fill(GfxState *state) +{ + num_polygons++; +} + +void InfoOutputDev::eoFill(GfxState *state) +{ + num_polygons++; +} + +FontInfo* InfoOutputDev::getFont(char*id) +{ + return (FontInfo*)id2font->lookup(id); } void InfoOutputDev::drawChar(GfxState *state, double x, double y, @@ -84,9 +418,6 @@ void InfoOutputDev::drawChar(GfxState *state, double x, double y, double originX, double originY, CharCode code, int nBytes, Unicode *u, int uLen) { - int render = state->getRender(); - if (render == 3) - return; double m11,m21,m12,m22; state->getFontTransMat(&m11, &m12, &m21, &m22); m11 *= state->getHorizScaling(); @@ -94,22 +425,150 @@ void InfoOutputDev::drawChar(GfxState *state, double x, double y, double lenx = sqrt(m11*m11 + m12*m12); double leny = sqrt(m21*m21 + m22*m22); double len = lenx>leny?lenx:leny; + if(!currentfont || !currentfont->splash_font) { + return; //error + } if(currentfont && currentfont->max_size < len) { currentfont->max_size = len; } + + num_textfields++; + + currentfont->grow(code+1); + GlyphInfo*g = currentfont->glyphs[code]; + if(!g) { + g = currentfont->glyphs[code] = new GlyphInfo(); + g->advance_max = 0; + currentfont->splash_font->last_advance = -1; + g->path = currentfont->splash_font->getGlyphPath(code); + g->advance = currentfont->splash_font->last_advance; + g->unicode = 0; + } + if(uLen && ((u[0]>=32 && u[0]unicode) || !g->unicode)) { + g->unicode = u[0]; + } + if(currentfont->lastchar>=0 && currentfont->lasty == y) { + double xshift = (x - currentfont->lastx); + if(xshift>=0 && xshift > g->advance_max) { + g->advance_max = xshift; + } + int advance = (int)xshift; + if(advance>=0 && advanceadvance*4 && advance!=currentfont->lastadvance) { + int c1 = currentfont->lastchar; + int c2 = code; + dict_t*d = currentfont->kerning[c1]; + if(!d) { + d = currentfont->kerning[c1] = dict_new2(&int_type); + } + mtf_t*k = (mtf_t*)dict_lookup(d, (void*)(ptroff_t)c2); + if(!k) { + k = mtf_new(&int_type); + dict_put(d, (void*)(ptroff_t)c2, k); + } + mtf_increase(k, (void*)(ptroff_t)advance); + } + } + + currentfont->lastx = x; + currentfont->lasty = y; + currentfont->lastchar = code; + currentfont->lastadvance = (int)(g->advance+0.5); } + +GBool InfoOutputDev::beginType3Char(GfxState *state, double x, double y, double dx, double dy, CharCode code, Unicode *u, int uLen) +{ + GfxFont*font = state->getFont(); + if(!font) + return gTrue; + if(font->getType() != fontType3) + return gTrue; + + char*id = getFontID(font); + currentfont = (FontInfo*)id2font->lookup(id); + if(!currentfont) { + currentfont = new FontInfo(id); + currentfont->font = font; + GString* idStr = new GString(id); + id2font->add(idStr, (void*)currentfont); + num_fonts++; + } + currentfont = currentfont; + free(id); + + currentfont->grow(code+1); + if(!currentfont->glyphs[code]) { + currentglyph = currentfont->glyphs[code] = new GlyphInfo(); + currentglyph->unicode = uLen?u[0]:0; + currentglyph->path = new SplashPath(); + currentglyph->x1=0; + currentglyph->y1=0; + currentglyph->x2=dx; + currentglyph->y2=dy; + currentglyph->advance=dx; + return gFalse; + } else { + return gTrue; + } +} + +void InfoOutputDev::type3D0(GfxState *state, double wx, double wy) +{ + currentglyph->x1=0; + currentglyph->y1=0; + currentglyph->x2=wx; + currentglyph->y2=wy; +} + +void InfoOutputDev::type3D1(GfxState *state, double wx, double wy, double llx, double lly, double urx, double ury) +{ + if(-lly>currentfont->descender) + currentfont->descender = -lly; + if(ury>currentfont->ascender) + currentfont->ascender = ury; + + currentglyph->x1=llx; + currentglyph->y1=lly; + currentglyph->x2=urx; + currentglyph->y2=ury; +} + +void InfoOutputDev::endType3Char(GfxState *state) +{ + double x1 = currentglyph->x1; + double y1 = currentglyph->y1; + double x2 = currentglyph->x2; + double y2 = currentglyph->y2; + currentglyph->path->moveTo(x1,y1); + currentglyph->path->lineTo(x2,y1); + currentglyph->path->lineTo(x2,y2); + currentglyph->path->lineTo(x1,y2); + currentglyph->path->close(); +} + +void InfoOutputDev::saveState(GfxState *state) +{ + updateAll(state); +} + +void InfoOutputDev::restoreState(GfxState *state) +{ + updateAll(state); +} + void InfoOutputDev::drawImageMask(GfxState *state, Object *ref, Stream *str, int width, int height, GBool invert, GBool inlineImg) { - num_images++; + if(str->getKind()==strDCT) num_jpeg_images++; else num_ppm_images++; + OutputDev::drawImageMask(state,ref,str,width,height,invert,inlineImg); } void InfoOutputDev::drawImage(GfxState *state, Object *ref, Stream *str, int width, int height, GfxImageColorMap *colorMap, int *maskColors, GBool inlineImg) { - num_images++; + if(str->getKind()==strDCT) num_jpeg_images++; else num_ppm_images++; + OutputDev::drawImage(state,ref,str,width,height,colorMap,maskColors,inlineImg); } void InfoOutputDev::drawMaskedImage(GfxState *state, Object *ref, Stream *str, @@ -119,6 +578,8 @@ void InfoOutputDev::drawMaskedImage(GfxState *state, Object *ref, Stream *str, int maskWidth, int maskHeight, GBool maskInvert) { + if(str->getKind()==strDCT) num_jpeg_images++; else num_ppm_images++; + OutputDev::drawMaskedImage(state,ref,str,width,height,colorMap,maskStr,maskWidth,maskHeight,maskInvert); } @@ -129,5 +590,18 @@ void InfoOutputDev::drawSoftMaskedImage(GfxState *state, Object *ref, Stream *st int maskWidth, int maskHeight, GfxImageColorMap *maskColorMap) { + if(str->getKind()==strDCT) num_jpeg_images++; else num_ppm_images++; + OutputDev::drawSoftMaskedImage(state,ref,str,width,height,colorMap,maskStr,maskWidth,maskHeight,maskColorMap); } + +void InfoOutputDev::dumpfonts(gfxdevice_t*dev) +{ + GHashIter*i; + GString*key; + FontInfo*font; + id2font->startIter(&i); + while(id2font->getNext(&i, &key, (void**)&font)) { + dev->addfont(dev, font->getGfxFont()); + } +}