added median advance detection
[swftools.git] / lib / pdf / InfoOutputDev.cc
index ec54572..c2accf2 100644 (file)
@@ -1,7 +1,18 @@
-#include "SplashTypes.h"
+#include "config.h"
+#include "Object.h"
+#include "InfoOutputDev.h"
 #include "SplashOutputDev.h"
+#ifdef HAVE_POPPLER
+#include <splash/SplashTypes.h>
+#include <splash/SplashPath.h>
+#include <splash/SplashFont.h>
+#include <splash/SplashFontFile.h>
+#else
+#include "SplashTypes.h"
 #include "SplashPath.h"
-#include "InfoOutputDev.h"
+#include "SplashFont.h"
+#include "SplashFontFile.h"
+#endif
 #include "GfxState.h"
 #include "../log.h"
 #include <math.h>
@@ -11,6 +22,9 @@ InfoOutputDev::InfoOutputDev(XRef*xref)
     num_links = 0;
     num_images = 0;
     num_fonts = 0;
+    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);
@@ -27,12 +41,27 @@ InfoOutputDev::~InfoOutputDev()
     }
     id2font->killIter(&i);
 
-    delete id2font;
-    delete splash;
+    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));
+       memset(&this->glyphs[this->num_glyphs], 0, sizeof(SplashPath*)*((size)-this->num_glyphs));
+       this->num_glyphs = size;
+    }
 }
 FontInfo::FontInfo()
 {
     this->charid2glyph = 0;
+    this->seen = 0;
+    this->num_glyphs = 0;
+    this->glyphs = 0;
+    this->splash_font = 0;
+    this->lastchar = -1;
+    this->lastx = 0;
+    this->lasty = 0;
 }
 FontInfo::~FontInfo()
 {
@@ -49,11 +78,13 @@ FontInfo::~FontInfo()
            glyphs[t]=0;
        }
     }
+    free(glyphs);glyphs=0;
 }
 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;
@@ -67,6 +98,9 @@ void InfoOutputDev::startPage(int pageNum, GfxState *state, double crop_x1, doub
     this->y2 = (int)y2;
     msg("<verbose> Generating info structure for page %d", pageNum);
 }
+void InfoOutputDev::endPage()
+{
+}
 void InfoOutputDev::drawLink(Link *link, Catalog *catalog) 
 {
     num_links++;
@@ -88,7 +122,11 @@ char*getFontID(GfxFont*font)
     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);
     }
@@ -98,45 +136,50 @@ char*getFontID(GfxFont*font)
 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) {
-       /* font already known */
-       free(id);
-       currentfont = info;
-       return;
-    }
+    if(currentfont)
+       currentfont->splash_font = 0;
 
-    info = new FontInfo;
-    info->font = font;
-    info->max_size = 0;
+    currentfont = (FontInfo*)id2font->lookup(id);
+    if(!currentfont) {
+       currentfont = new FontInfo;
+       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->setTextMat(1.0,0,0,1.0,0,0);
     state->setFont(font, 1024.0);
     splash->doUpdateFont(state);
-    info->splash_font = splash->getCurrentFont();
-    info->num_glyphs = 0;
-    info->glyphs = 0;
-
-    if(!info->splash_font) {
-       delete info;
-       return;
-    }
-    GString* idStr = new GString(id);
-    id2font->add(idStr, (void*)info);
-    num_fonts++;
-    currentfont = info;
+    currentfont->splash_font = splash->getCurrentFont();
+    currentfont->ascender = currentfont->splash_font->ascender;
+    currentfont->descender = currentfont->splash_font->descender;
     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);
@@ -154,25 +197,139 @@ 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;
     }
-    if(code >= currentfont->num_glyphs) {
-       currentfont->glyphs = (GlyphInfo**)realloc(currentfont->glyphs, sizeof(GlyphInfo*)*(code+1));
-       memset(&currentfont->glyphs[currentfont->num_glyphs], 0, sizeof(SplashPath*)*((code+1)-currentfont->num_glyphs));
-       currentfont->num_glyphs = code+1;
-    }
+    currentfont->grow(code+1);
     GlyphInfo*g = currentfont->glyphs[code];
     if(!g) {
        g = currentfont->glyphs[code] = new GlyphInfo();
+       g->advance_samples = 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]<g->unicode || !g->unicode)) {
        g->unicode = u[0];
     }
+    if(currentfont->lastchar>=0 && currentfont->lasty == y) {
+       double xshift = x - currentfont->lastx;
+       if(xshift>=0) {
+           AdvanceSample* old = g->advance_samples;
+           g->advance_samples = new AdvanceSample();
+           g->advance_samples->next = old;
+           g->advance_samples->advance = xshift;
+       }
+    }
 
+    currentfont->lastx = x;
+    currentfont->lasty = y;
+    currentfont->lastchar = code;
 }
+
+static int compare_double(const void *_a, const void *_b)
+{
+    const double*a = (const double*)_a;
+    const double*b = (const double*)_b;
+    if(*a < *b) 
+       return -1;
+    if(*a > *b) 
+       return 1;
+    return 0;
+}
+
+double GlyphInfo::estimateAdvance()
+{
+    AdvanceSample*a = advance_samples;
+    int n=0;
+    while(a) {
+       n++;
+       a = a->next;
+    }
+    if(!n)
+       return -1;
+    double*list = (double*)malloc(sizeof(double)*n);
+    n = 0;
+    a = advance_samples;
+    while(a) {
+       list[n++] = a->advance;
+       a = a->next;
+    }
+    // FIXME: a true median algorithm would be faster
+    qsort(list, n, sizeof(double), compare_double);
+    double median = list[n/2];
+    free(list);
+    return median;
+}
+
+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;
+       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;
+       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)
+{
+    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::drawImageMask(GfxState *state, Object *ref, Stream *str,
                           int width, int height, GBool invert,
                           GBool inlineImg)