applied transparency patch
[swftools.git] / lib / pdf / GFXOutputDev.cc
index 56257a5..8b114ce 100644 (file)
 #include "../devices/ops.h"
 #include "../devices/arts.h"
 #include "../devices/render.h"
+
+#include "../art/libart.h"
+#include "../devices/artsutils.c"
+
 #include "../png.h"
 #include "fonts.h"
 
@@ -76,7 +80,7 @@
 
 typedef struct _fontfile
 {
-    char*filename;
+    const char*filename;
     int used;
 } fontfile_t;
 
@@ -89,8 +93,8 @@ static int fontnum = 0;
 static char* lastfontdir = 0;
 
 struct fontentry {
-    char*pdffont;
-    char*filename;
+    const char*pdffont;
+    const char*filename;
     char*afm;
     int afmlen;
     char*pfb;
@@ -115,7 +119,7 @@ struct fontentry {
 
 static int verbose = 0;
 static int dbgindent = 0;
-static void dbg(char*format, ...)
+static void dbg(const char*format, ...)
 {
     char buf[1024];
     int l;
@@ -148,7 +152,7 @@ typedef struct _feature
 } feature_t;
 feature_t*featurewarnings = 0;
 
-void GFXOutputDev::showfeature(char*feature,char fully, char warn)
+void GFXOutputDev::showfeature(const char*feature,char fully, char warn)
 {
     feature_t*f = featurewarnings;
     while(f) {
@@ -170,18 +174,17 @@ void GFXOutputDev::showfeature(char*feature,char fully, char warn)
        msg("<notice> File contains %s",feature);
     }
 }
-void GFXOutputDev::warnfeature(char*feature,char fully)
+void GFXOutputDev::warnfeature(const char*feature,char fully)
 {
     showfeature(feature,fully,1);
 }
-void GFXOutputDev::infofeature(char*feature)
+void GFXOutputDev::infofeature(const char*feature)
 {
     showfeature(feature,0,0);
 }
 
 GFXOutputState::GFXOutputState() {
     this->clipping = 0;
-    this->textRender = 0;
     this->createsoftmask = 0;
     this->transparencygroup = 0;
     this->softmask = 0;
@@ -191,7 +194,7 @@ GFXOutputState::GFXOutputState() {
 
 GBool GFXOutputDev::interpretType3Chars() 
 {
-    return gTrue;
+    return this->do_interpretType3Chars;
 }
 
 typedef struct _drawnchar
@@ -269,6 +272,9 @@ GFXOutputDev::GFXOutputDev(parameter_t*p)
     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->parameters = p;
   
@@ -276,14 +282,27 @@ GFXOutputDev::GFXOutputDev(parameter_t*p)
 
     /* configure device */
     while(p) {
-       if(!strcmp(p->name,"fontconfig")) {
-           this->config_use_fontconfig = atoi(p->value);
-       } else if(!strcmp(p->name,"breakonwarning")) {
-           this->config_break_on_warning = atoi(p->value);
-       }
+        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")) {
+       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(p->name,"transparent")) {
+        this->config_transparent = atoi(p->value);
+    } else {
+        msg("<warning> Ignored parameter: %s=%s", key, value);
+    }
+}
   
 void GFXOutputDev::setDevice(gfxdevice_t*dev)
 {
@@ -462,7 +481,7 @@ static char* gfxstate2str(GfxState *state)
   return mybuf;
 }
 
-static void dumpFontInfo(char*loglevel, GfxFont*font);
+static void dumpFontInfo(const char*loglevel, GfxFont*font);
 static int lastdumps[1024];
 static int lastdumppos = 0;
 /* nr = 0  unknown
@@ -485,11 +504,11 @@ static void showFontError(GfxFont*font, int nr)
     else if(nr == 1)
       msg("<warning> The following font caused problems (substituting):");
     else if(nr == 2)
-      msg("<warning> The following Type 3 Font will be rendered as bitmap:");
+      msg("<warning> The following Type 3 Font will be rendered as graphics:");
     dumpFontInfo("<warning>", font);
 }
 
-static void dumpFontInfo(char*loglevel, GfxFont*font)
+static void dumpFontInfo(const char*loglevel, GfxFont*font)
 {
   char* id = getFontID(font);
   char* name = getFontName(font);
@@ -650,8 +669,28 @@ GBool GFXOutputDev::useShadedFills()
     infofeature("shaded fills");
     return gFalse;
 }
+  
+GBool GFXOutputDev::useDrawForm() 
+{ 
+    infofeature("forms");
+    return gFalse; 
+}
+void GFXOutputDev::drawForm(Ref id) 
+{
+    msg("<error> drawForm not implemented");
+}
+GBool GFXOutputDev::needNonText() 
+{ 
+    return gTrue; 
+}
+void GFXOutputDev::endPage() 
+{
+    msg("<verbose> endPage");
+}
 
-void GFXOutputDev::strokeGfxline(GfxState *state, gfxline_t*line)
+#define STROKE_FILL 1
+#define STROKE_CLIP 2
+void GFXOutputDev::strokeGfxline(GfxState *state, gfxline_t*line, int flags)
 {
     int lineCap = state->getLineCap(); // 0=butt, 1=round 2=square
     int lineJoin = state->getLineJoin(); // 0=miter, 1=round 2=bevel
@@ -719,9 +758,23 @@ void GFXOutputDev::strokeGfxline(GfxState *state, gfxline_t*line)
                );
         dump_outline(line);
     }
-   
-    //swfoutput_drawgfxline(output, line, width, &col, capType, joinType, miterLimit);
-    device->stroke(device, line, width, &col, capType, joinType, miterLimit);
+
+    if(flags&STROKE_FILL) {
+        ArtSVP* svp = gfxstrokeToSVP(line, width, capType, joinType, miterLimit);
+        gfxline_t*gfxline = SVPtogfxline(svp);
+        if(flags&STROKE_CLIP) {
+            device->startclip(device, gfxline);
+            states[statepos].clipping++;
+        } else {
+            device->fill(device, gfxline, &col);
+        }
+        free(gfxline);
+       art_svp_free(svp);
+    } else {
+        if(flags&STROKE_CLIP) 
+            msg("<error> Stroke&clip not supported at the same time");
+        device->stroke(device, line, width, &col, capType, joinType, miterLimit);
+    }
     
     if(line2)
        gfxline_free(line2);
@@ -784,6 +837,13 @@ void GFXOutputDev::eoClip(GfxState *state)
     states[statepos].clipping++;
     gfxline_free(line);
 }
+void GFXOutputDev::clipToStrokePath(GfxState *state)
+{
+    GfxPath * path = state->getPath();
+    gfxline_t*line= gfxPath_to_gfxline(state, path, 0, user_movex + clipmovex, user_movey + clipmovey);
+    strokeGfxline(state, line, STROKE_FILL|STROKE_CLIP);
+    gfxline_free(line);
+}
 
 void GFXOutputDev::endframe()
 {
@@ -831,7 +891,7 @@ GBool GFXOutputDev::useDrawChar()
     return gTrue;
 }
 
-char*renderModeDesc[]= {"fill", "stroke", "fill+stroke", "invisible",
+const char*renderModeDesc[]= {"fill", "stroke", "fill+stroke", "invisible",
                       "clip+fill", "stroke+clip", "fill+stroke+clip", "clip"};
 
 #define RENDER_FILL 0
@@ -869,7 +929,9 @@ char* makeStringPrintable(char*str)
 
 int getGfxCharID(gfxfont_t*font, int charnr, char *charname, int u)
 {
-    char*uniname = 0;
+    const char*uniname = 0;
+    if(!font)
+        return charnr;
     if(u>0) {
        int t;
        /* find out char name from unicode index 
@@ -924,6 +986,14 @@ int getGfxCharID(gfxfont_t*font, int charnr, char *charname, int u)
        msg("<debug> Char [%d,%s,>%d<] maps to %d\n", charnr, charname, u, font->unicode2glyph[u]);
        return font->unicode2glyph[u];
     }
+    /* try to use the unicode|0xe000 (needed for some WingDings fonts)
+       FIXME: do this only if we know the font is wingdings?
+     */
+    u |= 0xe000;
+    if(u>=0 && u<font->max_unicode && font->unicode2glyph[u]>=0) {
+       msg("<debug> Char [%d,%s,>%d<] maps to %d\n", charnr, charname, u, font->unicode2glyph[u]);
+       return font->unicode2glyph[u];
+    }
 
     if(charnr>=0 && charnr<font->num_glyphs) {
        msg("<debug> Char [>%d<,%s,%d] maps to %d\n", charnr, charname, u, charnr);
@@ -956,8 +1026,20 @@ void GFXOutputDev::beginString(GfxState *state, GString *s)
     this->current_font_matrix.ty = 0;
 
     gfxmatrix_t m = this->current_font_matrix;
+}
+
+static gfxline_t* mkEmptyGfxShape(double x, double y)
+{
+    gfxline_t*line = (gfxline_t*)malloc(sizeof(gfxline_t));
+    line->x = x;line->y = y;line->type = gfx_moveTo;line->next = 0;
+    return line;
+}
 
-    states[statepos].textRender = render;
+static char isValidUnicode(int c)
+{
+    if(c>=32 && c<0x2fffe)
+       return 1;
+    return 0;
 }
 
 void GFXOutputDev::drawChar(GfxState *state, double x, double y,
@@ -972,15 +1054,12 @@ void GFXOutputDev::drawChar(GfxState *state, double x, double y,
        return;
     }
 
-    if(states[statepos].textRender != render)
-       msg("<error> Internal error: drawChar.render!=beginString.render");
-
     gfxcolor_t col = getFillColor(state);
 
     Gushort *CIDToGIDMap = 0;
     GfxFont*font = state->getFont();
 
-    if(font->getType() == fontType3) {
+    if(font->getType() == fontType3 && do_interpretType3Chars) {
        /* type 3 chars are passed as graphics */
        msg("<debug> type3 char at %f/%f", x, y);
        return;
@@ -992,6 +1071,13 @@ void GFXOutputDev::drawChar(GfxState *state, double x, double y,
     if(uLen)
        u = _u[0];
 
+/*    char*fontname = getFontName(font);
+    if(u<256 && strstr(fontname, "ingdings")) {
+        // symbols are at 0xe000 in the unicode table
+        u |= 0xe000;
+    }
+    free(fontname);*/
+
     if(font->isCIDFont()) {
        GfxCIDFont*cfont = (GfxCIDFont*)font;
 
@@ -1034,12 +1120,35 @@ void GFXOutputDev::drawChar(GfxState *state, double x, double y,
                FIXNULL(name),c, u, FIXNULL((char*)current_gfxfont->id), current_gfxfont->num_glyphs);
        return;
     }
+    //useless- the font has already been passed to the output device
+    //if(!isValidUnicode(current_gfxfont->glyphs[charid].unicode) && isValidUnicode(u)) {
+    //    current_gfxfont->glyphs[charid].
+    //}
 
     gfxmatrix_t m = this->current_font_matrix;
     state->transform(x, y, &m.tx, &m.ty);
     m.tx += user_movex + clipmovex;
     m.ty += user_movey + clipmovey;
 
+    if((!name || strcmp(name, "space")) && charid!=32 && u!=32)
+    {
+        gfxline_t*l = current_gfxfont->glyphs[charid].line;
+        double x,y;
+        char ok = 0;
+        while(l) {
+            if((l->type == gfx_lineTo || l->type == gfx_splineTo) && l->x!=x && l->y!=y) {
+                ok = 1;
+            }
+            l = l->next;
+        }
+        if(!ok) {
+           static int lastemptychar = 0;
+           if(charid != lastemptychar)
+               msg("<warning> Drawing empty character charid=%d u=%d", charid, u);
+           lastemptychar = charid;
+        }
+    }
+
     if(render == RENDER_FILL) {
        device->drawchar(device, current_gfxfont, charid, &col, &m);
     } else {
@@ -1058,6 +1167,9 @@ void GFXOutputDev::drawChar(GfxState *state, double x, double y,
        if(render&RENDER_CLIP) {
            gfxline_t*add = gfxline_clone(tglyph);
            current_text_clip = gfxline_append(current_text_clip, add);
+           if(!current_text_clip) {
+               current_text_clip = mkEmptyGfxShape(m.tx, m.ty);
+           }
        }
        gfxline_free(tglyph);
     }
@@ -1067,8 +1179,6 @@ void GFXOutputDev::endString(GfxState *state)
 { 
     int render = state->getRender();
     msg("<trace> endString() render=%d textstroke=%08x", render, current_text_stroke);
-    if(states[statepos].textRender != render)
-       msg("<error> Internal error: drawChar.render!=beginString.render");
     
     if(current_text_stroke) {
        /* fillstroke and stroke text rendering objects we can process right
@@ -1082,11 +1192,11 @@ void GFXOutputDev::endString(GfxState *state)
            current_text_stroke = 0;
        } else if((render&3) == RENDER_FILLSTROKE) {
            fillGfxLine(state, current_text_stroke);
-           strokeGfxline(state, current_text_stroke);
+           strokeGfxline(state, current_text_stroke,0);
            gfxline_free(current_text_stroke);
            current_text_stroke = 0;
        } else if((render&3) == RENDER_STROKE) {
-           strokeGfxline(state, current_text_stroke);
+           strokeGfxline(state, current_text_stroke,0);
            gfxline_free(current_text_stroke);
            current_text_stroke = 0;
        }
@@ -1098,8 +1208,6 @@ void GFXOutputDev::endTextObject(GfxState *state)
 {
     int render = state->getRender();
     msg("<trace> endTextObject() render=%d textstroke=%08x clipstroke=%08x", render, current_text_stroke, current_text_clip);
-    if(states[statepos].textRender != render)
-       msg("<error> Internal error: drawChar.render!=beginString.render");
     
     if(current_text_clip) {
        device->setparameter(device, "mark","TXT");
@@ -1125,6 +1233,16 @@ GBool GFXOutputDev::beginType3Char(GfxState *state, double x, double y, double d
 {
     msg("<debug> beginType3Char %d, %08x, %d", code, *u, uLen);
     type3active = 1;
+
+    /*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? */
 }
@@ -1205,7 +1323,8 @@ 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;
-    device->fill(device, clippath, &white);
+    if(!config_transparent)
+        device->fill(device, clippath, &white);
 }
 
 
@@ -1253,7 +1372,7 @@ void GFXOutputDev::processLink(Link *link, Catalog *catalog)
     LinkAction*action=link->getAction();
     char buf[128];
     char*s = 0;
-    char*type = "-?-";
+    const char*type = "-?-";
     char*named = 0;
     int page = -1;
     msg("<trace> drawlink action=%d\n", action->getKind());
@@ -1403,7 +1522,6 @@ void GFXOutputDev::saveState(GfxState *state) {
       return;
     }
     statepos ++;
-    states[statepos].textRender = states[statepos-1].textRender;
     states[statepos].createsoftmask = states[statepos-1].createsoftmask;
     states[statepos].transparencygroup = states[statepos-1].transparencygroup;
     states[statepos].clipping = 0;
@@ -1416,7 +1534,8 @@ void GFXOutputDev::restoreState(GfxState *state) {
       msg("<error> Invalid restoreState");
       return;
   }
-  msg("<trace> restoreState");
+  msg("<trace> restoreState%s%s", states[statepos].softmask?" (end softmask)":"",
+                                 states[statepos].clipping?" (end clipping)":"");
   if(states[statepos].softmask) {
       clearSoftMask(state);
   }
@@ -1452,7 +1571,7 @@ char* writeOutStdFont(fontentry* f)
     return strdup(namebuf2);
 }
 
-char* GFXOutputDev::searchFont(char*name) 
+char* GFXOutputDev::searchFont(const char*name) 
 {      
     int i;
     char*filename=0;
@@ -1761,7 +1880,7 @@ char* GFXOutputDev::searchForSuitableFont(GfxFont*gfxFont)
 
 char* GFXOutputDev::substituteFont(GfxFont*gfxFont, char* oldname)
 {
-    char*fontname = 0, *filename = 0;
+    const char*fontname = 0, *filename = 0;
     msg("<notice> substituteFont(%s)", oldname);
 
     if(!(fontname = searchForSuitableFont(gfxFont))) {
@@ -1821,7 +1940,7 @@ void GFXOutputDev::setXRef(PDFDoc*doc, XRef *xref)
     this->xref = xref;
 }
 
-int GFXOutputDev::setGfxFont(char*id, char*name, char*filename, double maxSize)
+int GFXOutputDev::setGfxFont(char*id, char*name, char*filename, double maxSize, CharCodeToUnicode*ctu)
 {
     gfxfont_t*font = 0;
     fontlist_t*last=0,*l = this->fontlist;
@@ -1849,13 +1968,23 @@ int GFXOutputDev::setGfxFont(char*id, char*name, char*filename, double maxSize)
     double quality = (1024 * 0.05) / maxSize;
    
     msg("<verbose> Loading %s...", filename);
-    font = gfxfont_load(id, filename, quality);
+    font = gfxfont_load(id, filename, 0, quality);
     if(!font) {
        msg("<verbose> Couldn't load Font %s (%s)", filename, id);
        return 0;
     }
     msg("<verbose> Font %s (%s) loaded successfully", filename, id);
 
+    if(this->config_remapunicode && ctu) {
+       int c;
+       for(c=0;c<font->num_glyphs;c++) {
+           Unicode u[8];
+           int uLen = ctu->mapToUnicode(c, u, 8);
+           if(uLen && !isValidUnicode(font->glyphs[c].unicode) && isValidUnicode(u[0]))
+               font->glyphs[c].unicode = u[0];
+       }
+    }
+
     l = new fontlist_t;
     l->font = font;
     l->filename = strdup(filename);
@@ -1901,7 +2030,7 @@ void GFXOutputDev::updateFont(GfxState *state)
 
     /* second, see if this is a font which was used before-
        if so, we are done */
-    if(setGfxFont(fontid, fontname, 0, 0)) {
+    if(setGfxFont(fontid, fontname, 0, 0, gfxFont->getCTU())) {
        free(fontid);
        free(fontname);
        return;
@@ -1974,8 +2103,8 @@ void GFXOutputDev::updateFont(GfxState *state)
 
     //swfoutput_setfont(&device, fontid, fileName);
     
-    if(!setGfxFont(fontid, fontname, 0, 0)) {
-       setGfxFont(fontid, fontname, fileName, maxSize);
+    if(!setGfxFont(fontid, fontname, 0, 0, gfxFont->getCTU())) {
+       setGfxFont(fontid, fontname, fileName, maxSize, gfxFont->getCTU());
     }
    
     if(fileName && del)
@@ -2421,7 +2550,7 @@ void GFXOutputDev::stroke(GfxState *state)
 
     GfxPath * path = state->getPath();
     gfxline_t*line= gfxPath_to_gfxline(state, path, 0, user_movex + clipmovex, user_movey + clipmovey);
-    strokeGfxline(state, line);
+    strokeGfxline(state, line, 0);
     gfxline_free(line);
 }
 
@@ -2448,7 +2577,7 @@ void GFXOutputDev::eoFill(GfxState *state)
 }
 
 
-static char* dirseparator()
+static const char* dirseparator()
 {
 #ifdef WIN32
     return "\\";
@@ -2457,7 +2586,7 @@ static char* dirseparator()
 #endif
 }
 
-void addGlobalFont(char*filename)
+void addGlobalFont(const char*filename)
 {
     fontfile_t f;
     memset(&f, 0, sizeof(fontfile_t));
@@ -2470,10 +2599,10 @@ void addGlobalFont(char*filename)
     }
 }
 
-void addGlobalLanguageDir(char*dir)
+void addGlobalLanguageDir(const char*dir)
 {
     if(!globalParams)
-        globalParams = new GlobalParams("");
+        globalParams = new GlobalParams((char*)"");
     
     msg("<notice> Adding %s to language pack directories", dir);
 
@@ -2493,7 +2622,7 @@ void addGlobalLanguageDir(char*dir)
     fclose(fi);
 }
 
-void addGlobalFontDir(char*dirname)
+void addGlobalFontDir(const char*dirname)
 {
 #ifdef HAVE_DIRENT_H
     msg("<notice> Adding %s to font directories", dirname);
@@ -2645,7 +2774,7 @@ void GFXOutputDev::beginTransparencyGroup(GfxState *state, double *bbox,
                                      GBool isolated, GBool knockout,
                                      GBool forSoftMask)
 {
-    char*colormodename = "";
+    const char*colormodename = "";
     BBox rect = mkBBox(state, bbox, this->width, this->height);
 
     if(blendingColorSpace) {
@@ -2693,9 +2822,9 @@ void GFXOutputDev::endTransparencyGroup(GfxState *state)
 
 void GFXOutputDev::paintTransparencyGroup(GfxState *state, double *bbox)
 {
-    char*blendmodes[] = {"normal","multiply","screen","overlay","darken", "lighten",
-                      "colordodge","colorburn","hardlight","softlight","difference",
-                      "exclusion","hue","saturation","color","luminosity"};
+    const char*blendmodes[] = {"normal","multiply","screen","overlay","darken", "lighten",
+                               "colordodge","colorburn","hardlight","softlight","difference",
+                               "exclusion","hue","saturation","color","luminosity"};
 
     dbg("paintTransparencyGroup blend=%s softmaskon=%d", blendmodes[state->getBlendMode()], states[statepos].softmask);
     msg("<verbose> paintTransparencyGroup blend=%s softmaskon=%d", blendmodes[state->getBlendMode()], states[statepos].softmask);
@@ -2748,6 +2877,13 @@ static inline Guchar div255(int x) {
   return (Guchar)((x + (x >> 8) + 0x80) >> 8);
 }
 
+static unsigned char clampU8(unsigned char c, unsigned char min, unsigned char max)
+{
+    if(c < min) c = min;
+    if(c > max) c = max;
+    return c;
+}
+
 void GFXOutputDev::clearSoftMask(GfxState *state)
 {
     if(!states[statepos].softmask)
@@ -2782,6 +2918,8 @@ void GFXOutputDev::clearSoftMask(GfxState *state)
 #endif
     
     int width = (int)bbox.xmax,height = (int)bbox.ymax;
+    if(width<=0 || height<=0)
+        return;
 
     gfxdevice_t belowrender;
     gfxdevice_render_init(&belowrender);
@@ -2821,11 +2959,13 @@ void GFXOutputDev::clearSoftMask(GfxState *state)
                alpha = (77*l1->r + 151*l1->g + 28*l1->b) >> 8;
            }
 
-           /* premultiply alpha */
            l2->a = div255(alpha*l2->a);
-           l2->r = div255(alpha*l2->r);
-           l2->g = div255(alpha*l2->g);
-           l2->b = div255(alpha*l2->b);
+
+           /* DON'T premultiply alpha- this is done by fillbitmap,
+              depending on the output device */
+           //l2->r = div255(alpha*l2->r);
+           //l2->g = div255(alpha*l2->g);
+           //l2->b = div255(alpha*l2->b);
 
            l1++;
            l2++;