pass type3 characters as "dummy" characters
[swftools.git] / lib / pdf / GFXOutputDev.cc
index 3814dbf..f6a361d 100644 (file)
@@ -70,6 +70,7 @@
 #include "../devices/arts.h"
 #include "../devices/render.h"
 #include "../png.h"
+#include "fonts.h"
 
 #include <math.h>
 
@@ -87,26 +88,32 @@ static int fontnum = 0;
 
 static char* lastfontdir = 0;
 
-struct mapping {
+struct fontentry {
     char*pdffont;
     char*filename;
+    char*afm;
+    int afmlen;
+    char*pfb;
+    int pfblen;
+    char*fullfilename;
 } pdf2t1map[] ={
-{"Times-Roman",           "n021003l"},
-{"Times-Italic",          "n021023l"},
-{"Times-Bold",            "n021004l"},
-{"Times-BoldItalic",      "n021024l"},
-{"Helvetica",             "n019003l"},
-{"Helvetica-Oblique",     "n019023l"},
-{"Helvetica-Bold",        "n019004l"},
-{"Helvetica-BoldOblique", "n019024l"},
-{"Courier",               "n022003l"},
-{"Courier-Oblique",       "n022023l"},
-{"Courier-Bold",          "n022004l"},
-{"Courier-BoldOblique",   "n022024l"},
-{"Symbol",                "s050000l"},
-{"ZapfDingbats",          "d050000l"}};
-
-static int verbose = 1;
+{"Times-Roman",           "n021003l", n021003l_afm, n021003l_afm_len, n021003l_pfb, n021003l_pfb_len},
+{"Times-Italic",          "n021023l", n021023l_afm, n021023l_afm_len, n021023l_pfb, n021023l_pfb_len},
+{"Times-Bold",            "n021004l", n021004l_afm, n021004l_afm_len, n021004l_pfb, n021004l_pfb_len},
+{"Times-BoldItalic",      "n021024l", n021024l_afm, n021024l_afm_len, n021024l_pfb, n021024l_pfb_len},
+{"Helvetica",             "n019003l", n019003l_afm, n019003l_afm_len, n019003l_pfb, n019003l_pfb_len},
+{"Helvetica-Oblique",     "n019023l", n019023l_afm, n019023l_afm_len, n019023l_pfb, n019023l_pfb_len},
+{"Helvetica-Bold",        "n019004l", n019004l_afm, n019004l_afm_len, n019004l_pfb, n019004l_pfb_len},
+{"Helvetica-BoldOblique", "n019024l", n019024l_afm, n019024l_afm_len, n019024l_pfb, n019024l_pfb_len},
+{"Courier",               "n022003l", n022003l_afm, n022003l_afm_len, n022003l_pfb, n022003l_pfb_len},
+{"Courier-Oblique",       "n022023l", n022023l_afm, n022023l_afm_len, n022023l_pfb, n022023l_pfb_len},
+{"Courier-Bold",          "n022004l", n022004l_afm, n022004l_afm_len, n022004l_pfb, n022004l_pfb_len},
+{"Courier-BoldOblique",   "n022024l", n022024l_afm, n022024l_afm_len, n022024l_pfb, n022024l_pfb_len},
+{"Symbol",                "s050000l", s050000l_afm, s050000l_afm_len, s050000l_pfb, s050000l_pfb_len},
+{"ZapfDingbats",          "d050000l", d050000l_afm, d050000l_afm_len, d050000l_pfb, d050000l_pfb_len}};
+
+
+static int verbose = 0;
 static int dbgindent = 0;
 static void dbg(char*format, ...)
 {
@@ -141,7 +148,7 @@ typedef struct _feature
 } feature_t;
 feature_t*featurewarnings = 0;
 
-static void warnfeature(char*feature,char fully)
+void GFXOutputDev::showfeature(char*feature,char fully, char warn)
 {
     feature_t*f = featurewarnings;
     while(f) {
@@ -153,7 +160,23 @@ static void warnfeature(char*feature,char fully)
     f->string = strdup(feature);
     f->next = featurewarnings;
     featurewarnings = f;
-    msg("<warning> %s not yet %ssupported!",feature,fully?"fully ":"");
+    if(warn) {
+       msg("<warning> %s not yet %ssupported!",feature,fully?"fully ":"");
+       if(this->config_break_on_warning) {
+           msg("<fatal> Aborting conversion due to unsupported feature");
+           exit(1);
+       }
+    } else {
+       msg("<notice> File contains %s",feature);
+    }
+}
+void GFXOutputDev::warnfeature(char*feature,char fully)
+{
+    showfeature(feature,fully,1);
+}
+void GFXOutputDev::infofeature(char*feature)
+{
+    showfeature(feature,0,0);
 }
 
 GFXOutputState::GFXOutputState() {
@@ -163,6 +186,7 @@ GFXOutputState::GFXOutputState() {
     this->transparencygroup = 0;
     this->softmask = 0;
     this->grouprecording = 0;
+    this->isolated = 0;
 }
 
 GBool GFXOutputDev::interpretType3Chars() 
@@ -221,7 +245,6 @@ GFXOutputDev::GFXOutputDev(parameter_t*p)
 {
     this->jpeginfo = 0;
     this->textmodeinfo = 0;
-    this->ttfinfo = 0;
     this->linkinfo = 0;
     this->pbminfo = 0;
     this->type3active = 0;
@@ -244,18 +267,19 @@ GFXOutputDev::GFXOutputDev(parameter_t*p)
     this->pages = 0;
     this->pagebuflen = 0;
     this->pagepos = 0;
-  
-    this->forceType0Fonts=1;
     this->config_use_fontconfig=1;
+    this->config_break_on_warning=0;
 
     this->parameters = p;
+  
+    memset(states, 0, sizeof(states));
 
     /* configure device */
     while(p) {
-       if(!strcmp(p->name,"forceType0Fonts")) {
-           this->forceType0Fonts = atoi(p->value);
-       } else if(!strcmp(p->name,"fontconfig")) {
+       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);
        }
        p = p->next;
     }
@@ -265,7 +289,7 @@ void GFXOutputDev::setDevice(gfxdevice_t*dev)
 {
     parameter_t*p = this->parameters;
 
-    /* TODO: get rid of this */
+    /* pass parameters to output device */
     this->device = dev;
     if(this->device) {
        while(p) {
@@ -615,9 +639,17 @@ gfxline_t* gfxPath_to_gfxline(GfxState*state, GfxPath*path, int closed, int user
     return result;
 }
 
-/*----------------------------------------------------------------------------
- * Primitive Graphic routines
- *----------------------------------------------------------------------------*/
+GBool GFXOutputDev::useTilingPatternFill()
+{
+    infofeature("tiled patterns");
+    return gFalse;
+}
+
+GBool GFXOutputDev::useShadedFills()
+{
+    infofeature("shaded fills");
+    return gFalse;
+}
 
 void GFXOutputDev::strokeGfxline(GfxState *state, gfxline_t*line)
 {
@@ -759,8 +791,6 @@ void GFXOutputDev::endframe()
        device->endclip(device);
        outer_clip_box = 0;
     }
-
-    device->endpage(device);
 }
 
 void GFXOutputDev::finish()
@@ -927,8 +957,6 @@ void GFXOutputDev::beginString(GfxState *state, GString *s)
 
     gfxmatrix_t m = this->current_font_matrix;
 
-    /*if(render != 3 && render != 0)
-       msg("<warning> Text rendering mode %d (%s) not fully supported yet (for text \"%s\")", render, renderModeDesc[render&7], makeStringPrintable(s->getCString()));*/
     states[statepos].textRender = render;
 }
 
@@ -1097,6 +1125,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? */
 }
@@ -1180,7 +1218,8 @@ void GFXOutputDev::startPage(int pageNum, GfxState *state, double crop_x1, doubl
     device->fill(device, clippath, &white);
 }
 
-void GFXOutputDev::processLink(Link *link, Catalog *catalog) 
+
+void GFXOutputDev::processLink(Link *link, Catalog *catalog)
 {
     double x1, y1, x2, y2, w;
     gfxline_t points[5];
@@ -1399,35 +1438,57 @@ void GFXOutputDev::restoreState(GfxState *state) {
   statepos--;
 }
 
+char* writeOutStdFont(fontentry* f)
+{
+    FILE*fi;
+    char namebuf1[512];
+    char namebuf2[512];
+    char* tmpFileName = mktmpname(namebuf1);
+
+    sprintf(namebuf2, "%s.afm", tmpFileName);
+    fi = fopen(namebuf2, "wb");
+    if(!fi)
+        return 0;
+    fwrite(f->afm, 1, f->afmlen, fi);
+    fclose(fi);
+
+    sprintf(namebuf2, "%s.pfb", tmpFileName);
+    fi = fopen(namebuf2, "wb");
+    if(!fi)
+        return 0;
+    fwrite(f->pfb, 1, f->pfblen, fi);
+    fclose(fi);
+
+    return strdup(namebuf2);
+}
+
 char* GFXOutputDev::searchFont(char*name) 
 {      
     int i;
     char*filename=0;
-    int is_standard_font = 0;
        
     msg("<verbose> SearchFont(%s)", name);
 
     /* see if it is a pdf standard font */
-    for(i=0;i<sizeof(pdf2t1map)/sizeof(mapping);i++) 
+    for(i=0;i<sizeof(pdf2t1map)/sizeof(fontentry);i++) 
     {
        if(!strcmp(name, pdf2t1map[i].pdffont))
        {
-           name = pdf2t1map[i].filename;
-           is_standard_font = 1;
-           break;
+            if(!pdf2t1map[i].fullfilename) {
+                pdf2t1map[i].fullfilename = writeOutStdFont(&pdf2t1map[i]);
+                if(!pdf2t1map[i].fullfilename) {
+                    msg("<error> Couldn't save default font- is the Temp Directory writable?");
+                } else {
+                    msg("<verbose> Storing standard PDF font %s at %s", name, pdf2t1map[i].fullfilename);
+                }
+            }
+           return strdup(pdf2t1map[i].fullfilename);
        }
     }
-    /* look in all font files */
+    /* else look in all font files */
     for(i=0;i<fontnum;i++) 
     {
-       if(strstr(fonts[i].filename, name))
-       {
-           if(!fonts[i].used) {
-
-               fonts[i].used = 1;
-               if(!is_standard_font)
-                   msg("<notice> Using %s for %s", fonts[i].filename, name);
-           }
+       if(strstr(fonts[i].filename, name)) {
            return strdup(fonts[i].filename);
        }
     }
@@ -1538,7 +1599,7 @@ char*GFXOutputDev::writeEmbeddedFontToFile(XRef*ref, GfxFont*font)
       }
       FoFiType1C *cvt = FoFiType1C::make(fontBuf, fontLen);
       if(!cvt) return 0;
-      cvt->convertToType1(NULL, gTrue, FoFiWrite, f);
+      cvt->convertToType1(0, NULL, gTrue, FoFiWrite, f);
       //cvt->convertToCIDType0("test", f);
       //cvt->convertToType0("test", f);
       delete cvt;
@@ -1883,7 +1944,7 @@ void GFXOutputDev::updateFont(GfxState *state)
     if(embedded &&
        (gfxFont->getType() == fontType1 ||
        gfxFont->getType() == fontType1C ||
-       (gfxFont->getType() == fontCIDType0C && forceType0Fonts) ||
+        gfxFont->getType() == fontCIDType0C ||
        gfxFont->getType() == fontTrueType ||
        gfxFont->getType() == fontCIDType2
        ))
@@ -2039,10 +2100,6 @@ static void drawimage(gfxdevice_t*dev, gfxcolor_t* data, int sizex,int sizey,
        /* TODO: pass image_dpi to device instead */
        dev->setparameter(dev, "next_bitmap_is_jpeg", "1");
 
-    gfxline_show(&p1,stdout);
-
-    printf("%.2f %.2f %.2f\n", m.m00, m.m10, m.tx);
-    printf("%.2f %.2f %.2f\n", m.m01, m.m11, m.ty);
     dev->fillbitmap(dev, &p1, &img, &m, 0);
 }
 
@@ -2380,7 +2437,8 @@ void GFXOutputDev::stroke(GfxState *state)
 
 void GFXOutputDev::fill(GfxState *state) 
 {
-    dbg("fill");
+    gfxcolor_t col = getFillColor(state);
+    dbg("fill %02x%02x%02x%02x",col.r,col.g,col.b,col.a);
 
     GfxPath * path = state->getPath();
     gfxline_t*line= gfxPath_to_gfxline(state, path, 1, user_movex + clipmovex, user_movey + clipmovey);
@@ -2390,19 +2448,12 @@ void GFXOutputDev::fill(GfxState *state)
 
 void GFXOutputDev::eoFill(GfxState *state) 
 {
-    dbg("eofill");
-
-    GfxPath * path = state->getPath();
     gfxcolor_t col = getFillColor(state);
+    dbg("eofill %02x%02x%02x%02x",col.r,col.g,col.b,col.a);
 
+    GfxPath * path = state->getPath();
     gfxline_t*line= gfxPath_to_gfxline(state, path, 1, user_movex + clipmovex, user_movey + clipmovey);
-
-    if(getLogLevel() >= LOGLEVEL_TRACE)  {
-        msg("<trace> eofill\n");
-        dump_outline(line);
-    }
-
-    device->fill(device, line, &col);
+    fillGfxLine(state, line);
     gfxline_free(line);
 }
 
@@ -2599,7 +2650,6 @@ BBox mkBBox(GfxState*state, double*bbox, double width, double height)
     return nbbox;
 }
 
-#if xpdfUpdateVersion >= 16
 void GFXOutputDev::beginTransparencyGroup(GfxState *state, double *bbox,
                                      GfxColorSpace *blendingColorSpace,
                                      GBool isolated, GBool knockout,
@@ -2614,10 +2664,12 @@ void GFXOutputDev::beginTransparencyGroup(GfxState *state, double *bbox,
     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;
     states[statepos].transparencygroup = !forSoftMask;
-    states[statepos].olddevice = this->device;
+    states[statepos].isolated = isolated;
 
+    states[statepos].olddevice = this->device;
     this->device = (gfxdevice_t*)rfx_calloc(sizeof(gfxdevice_t));
 
     gfxdevice_record_init(this->device);
@@ -2625,7 +2677,6 @@ void GFXOutputDev::beginTransparencyGroup(GfxState *state, double *bbox,
     /*if(!forSoftMask) { ////???
        state->setFillOpacity(0.0);
     }*/
-    warnfeature("transparency groups",1);
     dbgindent+=2;
 }
 
@@ -2658,6 +2709,14 @@ void GFXOutputDev::paintTransparencyGroup(GfxState *state, double *bbox)
 
     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);
+   
+    if(state->getBlendMode() == gfxBlendNormal)
+       infofeature("transparency groups");
+    else {
+       char buffer[80];
+       sprintf(buffer, "%s blended transparency groups", blendmodes[state->getBlendMode()]);
+       warnfeature(buffer, 0);
+    }
 
     gfxresult_t*grouprecording = states[statepos].grouprecording;
    
@@ -2680,7 +2739,10 @@ void GFXOutputDev::setSoftMask(GfxState *state, double *bbox, GBool alpha, Funct
            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",
            bbox[0], bbox[1], bbox[2], bbox[3], alpha, colToByte(rgb->c[0]), colToByte(rgb->c[1]), colToByte(rgb->c[2]));
-    warnfeature("soft masks",0);
+    if(!alpha)
+       infofeature("soft masks");
+    else
+       warnfeature("soft masks from alpha channel",0);
     
     states[statepos].olddevice = this->device;
     this->device = (gfxdevice_t*)rfx_calloc(sizeof(gfxdevice_t));
@@ -2689,6 +2751,11 @@ void GFXOutputDev::setSoftMask(GfxState *state, double *bbox, GBool alpha, Funct
     dbg("softmaskrecording is %08x at statepos %d\n", states[statepos].softmaskrecording, statepos);
     
     states[statepos].softmask = 1;
+    states[statepos].softmask_alpha = alpha;
+}
+
+static inline Guchar div255(int x) {
+  return (Guchar)((x + (x >> 8) + 0x80) >> 8);
 }
 
 void GFXOutputDev::clearSoftMask(GfxState *state)
@@ -2728,12 +2795,16 @@ void GFXOutputDev::clearSoftMask(GfxState *state)
 
     gfxdevice_t belowrender;
     gfxdevice_render_init(&belowrender);
+    if(states[statepos+1].isolated) {
+       belowrender.setparameter(&belowrender, "fillwhite", "1");
+    }
     belowrender.setparameter(&belowrender, "antialize", "2");
     belowrender.startpage(&belowrender, width, height);
     gfxresult_record_replay(below, &belowrender);
     belowrender.endpage(&belowrender);
     gfxresult_t* belowresult = belowrender.finish(&belowrender);
     gfximage_t* belowimg = (gfximage_t*)belowresult->get(belowresult,"page0");
+    //writePNG("below.png", (unsigned char*)belowimg->data, belowimg->width, belowimg->height);
 
     gfxdevice_t maskrender;
     gfxdevice_render_init(&maskrender);
@@ -2753,14 +2824,18 @@ void GFXOutputDev::clearSoftMask(GfxState *state)
        gfxcolor_t* l1 = &maskimg->data[maskimg->width*y];
        gfxcolor_t* l2 = &belowimg->data[belowimg->width*y];
        for(x=0;x<width;x++) {
-           l1->a = 255;
-           l2->a = (77*l1->r + 151*l1->g + 28*l1->b) >> 8;
+           int alpha;
+           if(states[statepos].softmask_alpha) {
+               alpha = l1->a;
+           } else {
+               alpha = (77*l1->r + 151*l1->g + 28*l1->b) >> 8;
+           }
 
-           /* premultiply alpha... do we need this? (depends on output device)
-           l2->r = (l2->a*l2->r) >> 8;
-           l2->g = (l2->a*l2->g) >> 8;
-           l2->b = (l2->a*l2->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);
 
            l1++;
            l2++;
@@ -2780,8 +2855,7 @@ void GFXOutputDev::clearSoftMask(GfxState *state)
     belowresult->destroy(belowresult);
     states[statepos].softmaskrecording = 0;
 }
-#endif
-
+  
 /*class MemCheck
 {
     public: ~MemCheck()