added new output devices
[swftools.git] / lib / pdf / GFXOutputDev.cc
index 0eb7c04..da5df32 100644 (file)
@@ -1,4 +1,4 @@
-/* pdfswf.cc
+/* GFXOutputDev.cc
    implements a pdf output device (OutputDev).
 
    This file is part of swftools.
@@ -31,9 +31,6 @@
 #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"
@@ -194,7 +191,7 @@ GFXOutputState::GFXOutputState() {
 
 GBool GFXOutputDev::interpretType3Chars() 
 {
-    return this->do_interpretType3Chars;
+    return gTrue;
 }
 
 typedef struct _drawnchar
@@ -328,11 +325,22 @@ DisplayFontParam *GFXGlobalParams::getDisplayFont(GString *fontName)
            return dfp;
        }
     }
+    for(t=0;t<fontnum;t++) {
+       if(strstr(fonts[t].filename, name)) {
+           DisplayFontParam *dfp = new DisplayFontParam(new GString(fontName), displayFontT1);
+           dfp->t1.fileName = new GString(fonts[t].filename);
+           return dfp;
+       }
+    }
     return GlobalParams::getDisplayFont(fontName);
 }
 
-GFXOutputDev::GFXOutputDev(parameter_t*p)
+GFXOutputDev::GFXOutputDev(InfoOutputDev*info, PDFDoc*doc)
 {
+    this->info = info;
+    this->doc = doc;
+    this->xref = doc->getXRef();
+
     this->jpeginfo = 0;
     this->textmodeinfo = 0;
     this->linkinfo = 0;
@@ -356,52 +364,39 @@ GFXOutputDev::GFXOutputDev(parameter_t*p)
     this->pages = 0;
     this->pagebuflen = 0;
     this->pagepos = 0;
-    this->config_use_fontconfig=1;
     this->config_break_on_warning=0;
     this->config_remapunicode=0;
     this->config_transparent=0;
-    this->do_interpretType3Chars = gTrue;
+    this->config_extrafontdata = 0;
 
-    this->parameters = p;
-    
     this->gfxfontlist = gfxfontlist_create();
   
     memset(states, 0, sizeof(states));
-
-    /* configure device */
-    while(p) {
-        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")) {
+    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 if(!strcmp(key,"transparent")) {
         this->config_transparent = atoi(value);
+    } else if(!strcmp(key,"extrafontdata")) {
+        this->config_extrafontdata = atoi(value);
+    } else if(!strcmp(key,"help")) {
+       printf("\nPDF layer options:\n");
+       printf("breakonwarning=0/1  Abort conversion if graphic objects are found which\n");
+       printf("                    are not 100%% supported\n");
+       printf("transparent=0/1     Make PDF transparent (alpha background)\n");
+       printf("extrafontdata=0/1   Store Type3 characters and capture characters\n");
     }
+    
 }
   
 void GFXOutputDev::setDevice(gfxdevice_t*dev)
 {
-    parameter_t*p = this->parameters;
-
-    /* pass parameters to output device */
     this->device = dev;
-    if(this->device) {
-       while(p) {
-           this->device->setparameter(this->device, p->name, p->value);
-           p = p->next;
-       }
-    }
 }
   
 void GFXOutputDev::setMove(int x,int y)
@@ -650,6 +645,10 @@ GBool GFXOutputDev::needNonText()
 void GFXOutputDev::endPage() 
 {
     msg("<verbose> endPage");
+    if(outer_clip_box) {
+       device->endclip(device);
+       outer_clip_box = 0;
+    }
 }
 
 #define STROKE_FILL 1
@@ -819,14 +818,6 @@ void GFXOutputDev::clipToStrokePath(GfxState *state)
     gfxline_free(line);
 }
 
-void GFXOutputDev::endframe()
-{
-    if(outer_clip_box) {
-       device->endclip(device);
-       outer_clip_box = 0;
-    }
-}
-
 void GFXOutputDev::finish()
 {
     if(outer_clip_box) {
@@ -845,7 +836,7 @@ GFXOutputDev::~GFXOutputDev()
        free(this->pages); this->pages = 0;
     }
 
-    gfxfontlist_free(this->gfxfontlist);
+    gfxfontlist_free(this->gfxfontlist, 1);
 };
 GBool GFXOutputDev::upsideDown() 
 {
@@ -890,6 +881,31 @@ char* makeStringPrintable(char*str)
     tmp_printstr[len] = 0;
     return tmp_printstr;
 }
+#define INTERNAL_FONT_SIZE 1024.0
+void GFXOutputDev::updateFontMatrix(GfxState*state)
+{
+    double* ctm = state->getCTM();
+    double fontSize = state->getFontSize();
+    double*textMat = state->getTextMat();
+
+    /*  taking the absolute value of horizScaling seems to be required for
+       some italic fonts. FIXME: SplashOutputDev doesn't need this- why? */
+    double hscale = fabs(state->getHorizScaling());
+   
+    // from xpdf-3.02/SplashOutputDev:updateFont
+    double mm11 = textMat[0] * fontSize * hscale;
+    double mm12 = textMat[1] * fontSize * hscale;
+    double mm21 = textMat[2] * fontSize;
+    double mm22 = textMat[3] * fontSize;
+
+    // multiply with ctm, like state->getFontTransMat() does
+    this->current_font_matrix.m00 = (ctm[0]*mm11 + ctm[2]*mm12) / INTERNAL_FONT_SIZE;
+    this->current_font_matrix.m01 = (ctm[1]*mm11 + ctm[3]*mm12) / INTERNAL_FONT_SIZE;
+    this->current_font_matrix.m10 = (ctm[0]*mm21 + ctm[2]*mm22) / INTERNAL_FONT_SIZE;
+    this->current_font_matrix.m11 = (ctm[1]*mm21 + ctm[3]*mm22) / INTERNAL_FONT_SIZE;
+    this->current_font_matrix.tx = 0;
+    this->current_font_matrix.ty = 0;
+}
 
 void GFXOutputDev::beginString(GfxState *state, GString *s) 
 { 
@@ -899,17 +915,6 @@ void GFXOutputDev::beginString(GfxState *state, GString *s)
     }
 
     msg("<trace> beginString(%s) render=%d", makeStringPrintable(s->getCString()), render);
-    double m11,m21,m12,m22;
-    state->getFontTransMat(&m11, &m12, &m21, &m22);
-    m11 *= state->getHorizScaling();
-    m21 *= state->getHorizScaling();
-
-    this->current_font_matrix.m00 = m11 / 1024.0;
-    this->current_font_matrix.m01 = m12 / 1024.0;
-    this->current_font_matrix.m10 = -m21 / 1024.0;
-    this->current_font_matrix.m11 = -m22 / 1024.0;
-    this->current_font_matrix.tx = 0;
-    this->current_font_matrix.ty = 0;
 }
 
 static gfxline_t* mkEmptyGfxShape(double x, double y)
@@ -935,7 +940,7 @@ void GFXOutputDev::drawChar(GfxState *state, double x, double y,
        msg("<error> Invalid charid %d for font %s", charid, current_font_id);
        return;
     }
-
+  
     CharCode glyphid = current_fontinfo->glyphs[charid]->glyphid;
 
     int render = state->getRender();
@@ -944,18 +949,20 @@ 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;
+       if(!config_extrafontdata)
+           return;
     }
 
     GfxFont*font = state->getFont();
 
-    if(font->getType() == fontType3 && do_interpretType3Chars) {
+    if(font->getType() == fontType3) {
        /* type 3 chars are passed as graphics */
        msg("<debug> type3 char at %f/%f", x, y);
        return;
     }
 
     Unicode u = uLen?(_u[0]):0;
-    msg("<debug> drawChar(%f,%f,c='%c' (%d), u=%d <%d>) CID=%d render=%d\n",x,y,(charid&127)>=32?charid:'?', charid, u, uLen, font->isCIDFont(), render);
+    msg("<debug> drawChar(%f,%f,c='%c' (%d), u=%d <%d>) CID=%d render=%d glyphid=%d\n",x,y,(charid&127)>=32?charid:'?', charid, u, uLen, font->isCIDFont(), render, glyphid);
 
     gfxmatrix_t m = this->current_font_matrix;
     state->transform(x, y, &m.tx, &m.ty);
@@ -1034,38 +1041,49 @@ void GFXOutputDev::endTextObject(GfxState *state)
 /* the logic seems to be as following:
    first, beginType3Char is called, with the charcode and the coordinates.
    if this function returns true, it already knew about the char and has now drawn it.
-   if the function returns false, it's a new char, and type3D1 is called with some parameters-
-   the all draw operations until endType3Char are part of the char (which in this moment is
+   if the function returns false, it's a new char, and type3D0 and/or type3D1 might be 
+   called with some parameters.
+   Afterwards, all draw operations until endType3Char are part of the char (which in this moment is
    at the position first passed to beginType3Char). the char ends with endType3Char.
 
    The drawing operations between beginType3Char and endType3Char are somewhat different to
-   the normal ones. For example, the fillcolor equals the stroke color.
+   the normal ones. For example, the fillcolor equals the stroke color. (Because the stroke
+   color determines the color of a font)
 */
 
-GBool GFXOutputDev::beginType3Char(GfxState *state, double x, double y, double dx, double dy, CharCode code, Unicode *u, int uLen)
+GBool GFXOutputDev::beginType3Char(GfxState *state, double x, double y, double dx, double dy, CharCode charid, Unicode *u, int uLen)
 {
-    msg("<debug> beginType3Char %d, %08x, %d", code, *u, uLen);
+    msg("<debug> beginType3Char %d u=%d", charid, uLen?u[0]:0);
     type3active = 1;
+    
+    if(config_extrafontdata && current_fontinfo) {
+
+       gfxmatrix_t m = this->current_font_matrix;
+       state->transform(0, 0, &m.tx, &m.ty);
+       m.m00*=INTERNAL_FONT_SIZE;
+       m.m01*=INTERNAL_FONT_SIZE;
+       m.m10*=INTERNAL_FONT_SIZE;
+       m.m11*=INTERNAL_FONT_SIZE;
+       m.tx += user_movex + clipmovex;
+       m.ty += user_movey + clipmovey;
+
+       if(!current_fontinfo || (unsigned)charid >= current_fontinfo->num_glyphs || !current_fontinfo->glyphs[charid]) {
+           msg("<error> Invalid charid %d for font %s", charid, current_font_id);
+           return gFalse;
+       }
+       gfxcolor_t col={0,0,0,0};
+       CharCode glyphid = current_fontinfo->glyphs[charid]->glyphid;
+       device->drawchar(device, current_gfxfont, glyphid, &col, &m);
+    }
 
-    /*int t;
-
-    gfxcolor_t col={255,0,0,0};
-    gfxmatrix_t m = {1,0,0, 0,1,0};
-
-    for(t=0;t<uLen;t++) {
-       device->drawchar(device, 0, u[t], &col, &m);
-    }*/
 
     /* the character itself is going to be passed using the draw functions */
     return gFalse; /* gTrue= is_in_cache? */
 }
 
 void GFXOutputDev::type3D0(GfxState *state, double wx, double wy) {
-    msg("<debug> type3D0 width=%f height=%f", wx, wy);
 }
 void GFXOutputDev::type3D1(GfxState *state, double wx, double wy, double llx, double lly, double urx, double ury) {
-    msg("<debug> type3D1 width=%f height=%f bbox=(%f,%f,%f,%f)", wx, wy,
-           llx,lly,urx,ury);
 }
 
 void GFXOutputDev::endType3Char(GfxState *state)
@@ -1074,18 +1092,6 @@ void GFXOutputDev::endType3Char(GfxState *state)
     msg("<debug> endType3Char");
 }
 
-void GFXOutputDev::startFrame(int width, int height) 
-{
-    if(outer_clip_box) {
-       device->endclip(device);
-       outer_clip_box = 0;
-    }
-
-    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) 
 {
     this->currentpage = pageNum;
@@ -1136,8 +1142,9 @@ void GFXOutputDev::startPage(int pageNum, GfxState *state, double crop_x1, doubl
     clippath[3].type = gfx_lineTo;clippath[3].x = x1; clippath[3].y = y2; clippath[3].next = &clippath[4];
     clippath[4].type = gfx_lineTo;clippath[4].x = x1; clippath[4].y = y1; clippath[4].next = 0;
     device->startclip(device, clippath); outer_clip_box = 1;
-    if(!config_transparent)
+    if(!config_transparent) {
         device->fill(device, clippath, &white);
+    }
 }
 
 
@@ -1417,11 +1424,6 @@ void GFXOutputDev::updateStrokeColor(GfxState *state)
     state->getStrokeRGB(&rgb);
 }
 
-void GFXOutputDev::setXRef(PDFDoc*doc, XRef *xref) 
-{
-    this->doc = doc;
-    this->xref = xref;
-}
 
 gfxfont_t* createGfxFont(GfxFont*xpdffont, FontInfo*src)
 {
@@ -1430,9 +1432,9 @@ gfxfont_t* createGfxFont(GfxFont*xpdffont, FontInfo*src)
 
     font->glyphs = (gfxglyph_t*)malloc(sizeof(gfxglyph_t)*src->num_glyphs);
     memset(font->glyphs, 0, sizeof(gfxglyph_t)*src->num_glyphs);
-    font->id = strdup(getFontName(xpdffont));
+    font->id = strdup(getFontID(xpdffont));
     int t;
-    double quality = (1024 * 0.05) / src->max_size;
+    double quality = (INTERNAL_FONT_SIZE * 0.05) / src->max_size;
     double scale = 1;
     //printf("%d glyphs\n", font->num_glyphs);
     font->num_glyphs = 0;
@@ -1455,7 +1457,7 @@ gfxfont_t* createGfxFont(GfxFont*xpdffont, FontInfo*src)
                Guchar f;
                double x, y;
                path->getPoint(s, &x, &y, &f);
-               if(x > xmax)
+               if(!s || x > xmax)
                    xmax = x;
                if(f&splashPathFirst) {
                    drawer.moveTo(&drawer, x*scale, y*scale);
@@ -1490,6 +1492,7 @@ gfxfont_t* createGfxFont(GfxFont*xpdffont, FontInfo*src)
        }
 
     }
+    msg("<trace> %d glyphs.", t, font->num_glyphs);
     return font;
 }
 
@@ -1500,21 +1503,37 @@ void GFXOutputDev::updateFont(GfxState *state)
        return; 
     }  
     char*id = getFontID(gfxFont);
+    msg("<verbose> Updating font to %s", id);
+    if(gfxFont->getType() == fontType3) {
+       infofeature("Type3 fonts");
+       if(!config_extrafontdata) {
+           return;
+       }
+    }
     if(!id) {
        msg("<error> Internal Error: FontID is null");
        return; 
     }
 
     this->current_fontinfo = this->info->getFont(id);
+    if(!this->current_fontinfo) {
+       msg("<error> Internal Error: no fontinfo for font %s\n", id);
+    }
+    if(!this->current_fontinfo->seen) {
+       dumpFontInfo("<verbose>", gfxFont);
+    }
     
     gfxfont_t*font = gfxfontlist_findfont(this->gfxfontlist,id);
     if(!font) {
        font = createGfxFont(gfxFont, current_fontinfo);
-       gfxfontlist_addfont(this->gfxfontlist, font);
+        font->id = strdup(id);
+       this->gfxfontlist = gfxfontlist_addfont(this->gfxfontlist, font);
        device->addfont(device, font);
     }
     current_gfxfont = font;
     free(id);
+
+    updateFontMatrix(state);
 }
 
 #define SQR(x) ((x)*(x))
@@ -1991,8 +2010,7 @@ void addGlobalFont(const char*filename)
     memset(&f, 0, sizeof(fontfile_t));
     f.filename = filename;
     if(fontnum < sizeof(fonts)/sizeof(fonts[0])) {
-       msg("<verbose> Adding font \"%s\".", filename);
-       msg("<warning> External fonts are not supported with this version. Ignoring font %s", filename);
+       msg("<notice> Adding font \"%s\".", filename);
        fonts[fontnum++] = f;
     } else {
        msg("<error> Too many external fonts. Not adding font file \"%s\".", filename);
@@ -2021,8 +2039,6 @@ void addGlobalLanguageDir(const char*dir)
 
 void addGlobalFontDir(const char*dirname)
 {
-    msg("<warning> External fonts are not supported with this version. Ignoring directory %s", dirname);
-    return;
 #ifdef HAVE_DIRENT_H
     msg("<notice> Adding %s to font directories", dirname);
     lastfontdir = strdup(dirname);
@@ -2049,8 +2065,7 @@ void addGlobalFontDir(const char*dirname)
            type=3;
        if(!strncasecmp(&name[l-4], ".ttf", 4)) 
            type=2;
-       if(type)
-       {
+       if(type) {
            char*fontname = (char*)malloc(strlen(dirname)+strlen(name)+2);
            strcpy(fontname, dirname);
             strcat(fontname, dirseparator());
@@ -2174,13 +2189,11 @@ void GFXOutputDev::beginTransparencyGroup(GfxState *state, double *bbox,
                                      GBool forSoftMask)
 {
     const char*colormodename = "";
-    BBox rect = mkBBox(state, bbox, this->width, this->height);
 
     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("<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;