print out a "file contains links" message once.
[swftools.git] / pdf2swf / SWFOutputDev.cc
index 840b04e..d262674 100644 (file)
@@ -46,15 +46,19 @@ extern "C" {
 #include "../lib/log.h"
 }
 
+static PDFDoc*doc = 0;
 static char* swffilename = 0;
+int numpages;
+int currentpage;
+
+// swf <-> pdf pages
+int*pages = 0;
+int pagebuflen = 0;
+int pagepos = 0;
 
 static void printInfoString(Dict *infoDict, char *key, char *fmt);
 static void printInfoDate(Dict *infoDict, char *key, char *fmt);
 
-static char userPassword[33] = "";
-static GBool printVersion = gFalse;
-static GBool printHelp = gFalse;
-
 double fontsizes[] = 
 {
  0.833,0.833,0.889,0.889,0.788,0.722,0.833,0.778,0.600,0.600,0.600,0.600,0.576,0.576,0.576,0.576
@@ -225,8 +229,11 @@ public:
 
   int setT1Font(char*name,FontEncoding*enc);
   int t1id;
-  int jpeginfo; // did we write "Page contains jpegs" yet?
-  int pbminfo; // did we write "Page contains jpegs" yet?
+  int jpeginfo; // did we write "File contains jpegs" yet?
+  int pbminfo; // did we write "File contains jpegs" yet?
+  int linkinfo; // did we write "File contains links" yet?
+
+  GfxState *laststate;
 };
 
 char mybuf[1024];
@@ -355,13 +362,13 @@ void showFontError(GfxFont*font, int nr)
     if(lastdumppos<sizeof(lastdumps)/sizeof(int))
     lastdumps[lastdumppos++] = r.num;
     if(nr == 0)
-      logf("<error> The following font caused problems:");
+      logf("<warning> The following font caused problems:");
     else if(nr == 1)
-      logf("<error> The following font caused problems (substituting):");
+      logf("<warning> The following font caused problems (substituting):");
     else if(nr == 2)
-      logf("<error> This document contains Type 3 Fonts: (some text may be incorrectly displayed)");
+      logf("<warning> This document contains Type 3 Fonts: (some text may be incorrectly displayed)");
 
-    dumpFontInfo("<error>", font);
+    dumpFontInfo("<warning>", font);
 }
 
 void dumpFontInfo(char*loglevel, GfxFont*font)
@@ -422,6 +429,9 @@ void dumpFontInfo(char*loglevel, GfxFont*font)
 
 SWFOutputDev::SWFOutputDev() 
 {
+    jpeginfo = 0;
+    linkinfo = 0;
+    pbminfo = 0;
     clippos = 0;
     clipping[clippos] = 0;
     outputstarted = 0;
@@ -497,7 +507,7 @@ void SWFOutputDev::stroke(GfxState *state)
     GfxPath * path = state->getPath();
     struct swfmatrix m;
     m.m11 = 1; m.m21 = 0; m.m22 = 1;
-    m.m21 = 0; m.m13 = 0; m.m23 = 0;
+    m.m12 = 0; m.m13 = 0; m.m23 = 0;
     T1_OUTLINE*outline = gfxPath_to_T1_OUTLINE(state, path);
     swfoutput_setdrawmode(&output, DRAWMODE_STROKE);
     swfoutput_drawpath(&output, outline, &m);
@@ -508,7 +518,7 @@ void SWFOutputDev::fill(GfxState *state)
     GfxPath * path = state->getPath();
     struct swfmatrix m;
     m.m11 = 1; m.m21 = 0; m.m22 = 1;
-    m.m21 = 0; m.m13 = 0; m.m23 = 0;
+    m.m12 = 0; m.m13 = 0; m.m23 = 0;
     T1_OUTLINE*outline = gfxPath_to_T1_OUTLINE(state, path);
     swfoutput_setdrawmode(&output, DRAWMODE_FILL);
     swfoutput_drawpath(&output, outline, &m);
@@ -519,7 +529,7 @@ void SWFOutputDev::eoFill(GfxState *state)
     GfxPath * path = state->getPath();
     struct swfmatrix m;
     m.m11 = 1; m.m21 = 0; m.m22 = 1;
-    m.m21 = 0; m.m13 = 0; m.m23 = 0;
+    m.m12 = 0; m.m13 = 0; m.m23 = 0;
     T1_OUTLINE*outline = gfxPath_to_T1_OUTLINE(state, path);
     swfoutput_setdrawmode(&output, DRAWMODE_EOFILL);
     swfoutput_drawpath(&output, outline, &m);
@@ -529,8 +539,9 @@ void SWFOutputDev::clip(GfxState *state)
     logf("<debug> clip\n");
     GfxPath * path = state->getPath();
     struct swfmatrix m;
-    m.m11 = 1; m.m21 = 0; m.m22 = 1;
-    m.m21 = 0; m.m13 = 0; m.m23 = 0;
+    m.m11 = 1; m.m22 = 1;
+    m.m12 = 0; m.m21 = 0; 
+    m.m13 = 0; m.m23 = 0;
     T1_OUTLINE*outline = gfxPath_to_T1_OUTLINE(state, path);
     swfoutput_startclip(&output, outline, &m);
     clipping[clippos] = 1;
@@ -541,7 +552,7 @@ void SWFOutputDev::eoClip(GfxState *state)
     GfxPath * path = state->getPath();
     struct swfmatrix m;
     m.m11 = 1; m.m21 = 0; m.m22 = 1;
-    m.m21 = 0; m.m13 = 0; m.m23 = 0;
+    m.m12 = 0; m.m13 = 0; m.m23 = 0;
     T1_OUTLINE*outline = gfxPath_to_T1_OUTLINE(state, path);
     swfoutput_startclip(&output, outline, &m);
     clipping[clippos] = 1;
@@ -566,7 +577,7 @@ GBool SWFOutputDev::useDrawChar()
 void SWFOutputDev::beginString(GfxState *state, GString *s) 
 { 
     double m11,m21,m12,m22;
-    logf("<debug> beginstring \"%s\"\n", s->getCString());
+//    logf("<debug> %s beginstring \"%s\"\n", gfxstate2str(state), s->getCString());
     state->getFontTransMat(&m11, &m12, &m21, &m22);
     m11 *= state->getHorizScaling();
     m21 *= state->getHorizScaling();
@@ -587,7 +598,10 @@ void SWFOutputDev::drawChar(GfxState *state, double x, double y, double dx, doub
        y1 = y;
        state->transform(x, y, &x1, &y1);
 
-       swfoutput_drawchar(&output, x1, y1, enc->getCharName(c));
+       if(enc->getCharName(c))
+         swfoutput_drawchar(&output, x1, y1, enc->getCharName(c));
+       else
+         logf("<warning> couldn't get name for character %02x from Encoding", c);
     }
 }
 
@@ -605,10 +619,9 @@ void SWFOutputDev::endString(GfxState *state)
 void SWFOutputDev::startPage(int pageNum, GfxState *state) 
 {
   double x1,y1,x2,y2;
+  laststate = state;
   logf("<debug> startPage %d\n", pageNum);
   logf("<notice> processing page %d", pageNum);
-  jpeginfo = 0;
-  pbminfo = 0;
 
   state->transform(state->getX1(),state->getY1(),&x1,&y1);
   state->transform(state->getX2(),state->getY2(),&x2,&y2);
@@ -622,45 +635,91 @@ void SWFOutputDev::startPage(int pageNum, GfxState *state)
 
 void SWFOutputDev::drawLink(Link *link, Catalog *catalog) 
 {
-  logf("<debug> drawlink\n");
   double x1, y1, x2, y2, w;
   GfxRGB rgb;
   swfcoord points[5];
   int x, y;
 
   link->getBorder(&x1, &y1, &x2, &y2, &w);
-  if (w > 0) {
+//  if (w > 0) 
+  {
     rgb.r = 0;
     rgb.g = 0;
     rgb.b = 1;
     cvtUserToDev(x1, y1, &x, &y);
-    points[0].x = points[4].x = x;
-    points[0].y = points[4].y = y;
+    points[0].x = points[4].x = (int)x;
+    points[0].y = points[4].y = (int)y;
     cvtUserToDev(x2, y1, &x, &y);
-    points[1].x = x;
-    points[1].y = y;
+    points[1].x = (int)x;
+    points[1].y = (int)y;
     cvtUserToDev(x2, y2, &x, &y);
-    points[2].x = x;
-    points[2].y = y;
+    points[2].x = (int)x;
+    points[2].y = (int)y;
     cvtUserToDev(x1, y2, &x, &y);
-    points[3].x = x;
-    points[3].y = y;
-    //PDF: draw rect
+    points[3].x = (int)x;
+    points[3].y = (int)y;
+
     LinkAction*action=link->getAction();
-    char*s;
+    char buf[128];
+    char*s = "-?-";
+    char*type = "-?-";
+    char*url = 0;
+    int page = -1;
     switch(action->getKind())
     {
        case actionGoTo: {
-           LinkGoTo*l = (LinkGoTo*)action;
-           s = l->getNamedDest()->getCString();
+           type = "GoTo";
+           LinkGoTo *ha=(LinkGoTo *)link->getAction();
+           LinkDest *dest=NULL;
+           if (ha->getDest()==NULL) 
+               dest=catalog->findDest(ha->getNamedDest());
+           else dest=ha->getDest();
+           if (dest){ 
+             if (dest->isPageRef()){
+               Ref pageref=dest->getPageRef();
+               page=catalog->findPage(pageref.num,pageref.gen);
+             }
+             else  page=dest->getPageNum();
+             sprintf(buf, "%d", page);
+             s = buf;
+           }
        }
         break;
        case actionGoToR: {
+           type = "GoToR";
            LinkGoToR*l = (LinkGoToR*)action;
-           s = l->getNamedDest()->getCString();
+           GString*g = l->getNamedDest();
+           if(g)
+            s = g->getCString();
+       }
+        break;
+       case actionNamed: {
+           type = "Named";
+           LinkNamed*l = (LinkNamed*)action;
+           GString*name = l->getName();
+           if(name) {
+             s = name->lowerCase()->getCString();
+             if(strstr(s, "next") || strstr(s, "forward"))
+             {
+                 page = currentpage + 1;
+             }
+             else if(strstr(s, "prev") || strstr(s, "back"))
+             {
+                 page = currentpage - 1;
+             }
+             else if(strstr(s, "last") || strstr(s, "end"))
+             {
+                 page = pages[pagepos-1]; //:)
+             }
+             else if(strstr(s, "first") || strstr(s, "top"))
+             {
+                 page = 1;
+             }
+           }
        }
         break;
        case actionLaunch: {
+           type = "Launch";
            LinkLaunch*l = (LinkLaunch*)action;
            GString * str = new GString(l->getFileName());
            str->append(l->getParams());
@@ -668,22 +727,45 @@ void SWFOutputDev::drawLink(Link *link, Catalog *catalog)
        }
         break;
        case actionURI: {
+           type = "URI";
            LinkURI*l = (LinkURI*)action;
-           s = l->getURI()->getCString();
-       }
-        break;
-       case actionNamed: {
-           LinkNamed*l = (LinkNamed*)action;
-           s = l->getName()->getCString();
+           GString*g = l->getURI();
+           if(g) {
+            url = g->getCString();
+            s = url;
+           }
        }
         break;
        case actionUnknown: {
+           type = "Unknown";
            LinkUnknown*l = (LinkUnknown*)action;
            s = "";
        }
         break;
+       default: {
+           logf("<error> Unknown link type!\n");
+           break;
+       }
+    }
+    if(!linkinfo && (page || url))
+    {
+       logf("<notice> File contains links");
+       linkinfo = 1;
+    }
+    if(page>0)
+    {
+       int t;
+       for(t=0;t<pagepos;t++)
+           if(pages[t]==page)
+               break;
+       if(t!=pagepos)
+       swfoutput_linktopage(&output, t, points);
+    }
+    else if(url)
+    {
+       swfoutput_linktourl(&output, url, points);
     }
-    logf("<verbose> link to \"%s\"\n", s);
+    logf("<verbose> \"%s\" link to \"%s\" (%d)\n", type, s, page);
   }
 }
 
@@ -733,11 +815,12 @@ int SWFOutputDev::setT1Font(char*name, FontEncoding*encoding)
      return 0;
 
     this->t1id = id;
+    return 1;
 }
 
 void SWFOutputDev::updateLineWidth(GfxState *state)
 {
-    double width = state->getLineWidth();
+    double width = state->getTransformedLineWidth();
     swfoutput_setlinewidth(&output, width);
 }
 
@@ -832,6 +915,8 @@ void SWFOutputDev::updateFont(GfxState *state)
   char * fontname = 0;
   GfxFont*gfxFont = state->getFont();
   char * fileName = 0;
+    
+//  logf("<debug> %s updateFont\n", gfxstate2str(state));
 
   if (!gfxFont) {
     return;
@@ -943,6 +1028,12 @@ void SWFOutputDev::updateFont(GfxState *state)
       unlink(fileName);
 }
 
+int pic_xids[1024];
+int pic_yids[1024];
+int pic_ids[1024];
+int picpos = 0;
+int pic_id = 0;
+
 void SWFOutputDev::drawGeneralImage(GfxState *state, Object *ref, Stream *str,
                                   int width, int height, GfxImageColorMap*colorMap, GBool invert,
                                   GBool inlineImg, int mask)
@@ -951,6 +1042,12 @@ void SWFOutputDev::drawGeneralImage(GfxState *state, Object *ref, Stream *str,
   int c;
   char fileName[128];
   double x1,y1,x2,y2,x3,y3,x4,y4;
+  ImageStream *imgStr;
+  Guchar pixBuf[4];
+  GfxRGB rgb;
+  if(!width || !height)
+      return;
+  
   state->transform(0, 1, &x1, &y1);
   state->transform(0, 0, &x2, &y2);
   state->transform(1, 0, &x3, &y3);
@@ -963,7 +1060,7 @@ void SWFOutputDev::drawGeneralImage(GfxState *state, Object *ref, Stream *str,
     logf("<verbose> Found jpeg. Temporary storage is %s", fileName);
     if(!jpeginfo)
     {
-       logf("<notice> Page contains jpeg pictures");
+       logf("<notice> file contains jpeg pictures");
        jpeginfo = 1;
     }
     if (!(fi = fopen(fileName, "wb"))) {
@@ -972,17 +1069,153 @@ void SWFOutputDev::drawGeneralImage(GfxState *state, Object *ref, Stream *str,
     }
     str = ((DCTStream *)str)->getRawStream();
     str->reset();
+    int xid = 0;
+    int yid = 0;
+    int count = 0;
     while ((c = str->getChar()) != EOF)
+    {
       fputc(c, fi);
+      xid += count*c;
+      yid += (~count)*c;
+      count++;
+    }
     fclose(fi);
-    swfoutput_drawimagefile(&output, fileName, width, height, x1,y1,x2,y2,x3,y3,x4,y4);
+    
+    int t,found = -1;
+    for(t=0;t<picpos;t++)
+    {
+       if(pic_xids[t] == xid &&
+          pic_yids[t] == yid) {
+           found = t;break;
+       }
+    }
+    if(found<0) {
+       pic_ids[picpos] = swfoutput_drawimagejpeg(&output, fileName, width, height, 
+               x1,y1,x2,y2,x3,y3,x4,y4);
+       pic_xids[picpos] = xid;
+       pic_yids[picpos] = yid;
+       if(picpos<1024)
+           picpos++;
+    } else {
+       swfoutput_drawimageagain(&output, pic_ids[found], width, height,
+               x1,y1,x2,y2,x3,y3,x4,y4);
+    }
     unlink(fileName);
   } else {
-      if(!pbminfo)
-      {
-         logf("<notice> Page contains pbm pictures");
-         pbminfo = 1;
-      }
+
+    if(!pbminfo) {
+       logf("<notice> file contains pbm pictures %s",mask?"(masked)":"");
+       if(mask)
+       logf("<verbose> ignoring %d by %d masked picture\n", width, height);
+       pbminfo = 1;
+    }
+
+    if(mask) {
+       str->reset();
+       int yes=0;
+       while ((c = str->getChar()) != EOF)
+       {
+           if((c<32 || c>'z') && yes && (c!=13) && (c!=10)) {
+               printf("no ascii: %02x\n", c);
+               yes = 1;
+          }
+       }
+    } else {
+       int x,y;
+       int width2 = (width+3)&(~3);
+       imgStr = new ImageStream(str, width, colorMap->getNumPixelComps(),
+                                colorMap->getBits());
+       imgStr->reset();
+
+       if(colorMap->getNumPixelComps()!=1)
+       {
+           RGBA*pic=new RGBA[width*height];
+           int xid = 0;
+           int yid = 0;
+           for (y = 0; y < height; ++y) {
+             for (x = 0; x < width; ++x) {
+               int r,g,b,a;
+               imgStr->getPixel(pixBuf);
+               colorMap->getRGB(pixBuf, &rgb);
+               pic[width*y+x].r = r = (U8)(rgb.r * 255 + 0.5);
+               pic[width*y+x].g = g = (U8)(rgb.g * 255 + 0.5);
+               pic[width*y+x].b = b = (U8)(rgb.b * 255 + 0.5);
+               pic[width*y+x].a = a = 255;//(U8)(rgb.a * 255 + 0.5);
+               xid += x*r+x*b*3+x*g*7+x*a*11;
+               yid += y*r*3+y*b*17+y*g*19+y*a*11;
+             }
+           }
+           int t,found = -1;
+           for(t=0;t<picpos;t++)
+           {
+               if(pic_xids[t] == xid &&
+                  pic_yids[t] == yid) {
+                   found = t;break;
+               }
+           }
+           if(found<0) {
+               pic_ids[picpos] = swfoutput_drawimagelossless(&output, pic, width, height, 
+                       x1,y1,x2,y2,x3,y3,x4,y4);
+               pic_xids[picpos] = xid;
+               pic_yids[picpos] = yid;
+               if(picpos<1024)
+                   picpos++;
+           } else {
+               swfoutput_drawimageagain(&output, pic_ids[found], width, height,
+                       x1,y1,x2,y2,x3,y3,x4,y4);
+           }
+           delete pic;
+       }
+       else
+       {
+           U8*pic = new U8[width2*height];
+           RGBA pal[256];
+           int t;
+           int xid=0,yid=0;
+           for(t=0;t<256;t++)
+           {
+               int r,g,b,a;
+               pixBuf[0] = t;
+               colorMap->getRGB(pixBuf, &rgb);
+               pal[t].r = r = (U8)(rgb.r * 255 + 0.5);
+               pal[t].g = g = (U8)(rgb.g * 255 + 0.5);
+               pal[t].b = b = (U8)(rgb.b * 255 + 0.5);
+               pal[t].a = a = 255;//(U8)(rgb.b * 255 + 0.5);
+               xid += t*r+t*b*3+t*g*7+t*a*11;
+               xid += (~t)*r+t*b*3+t*g*7+t*a*11;
+           }
+           for (y = 0; y < height; ++y) {
+             for (x = 0; x < width; ++x) {
+               imgStr->getPixel(pixBuf);
+               pic[width2*y+x] = pixBuf[0];
+               xid += x*pixBuf[0]*7;
+               yid += y*pixBuf[0]*3;
+             }
+           }
+           int found = -1;
+           for(t=0;t<picpos;t++)
+           {
+               if(pic_xids[t] == xid &&
+                  pic_yids[t] == yid) {
+                   found = t;break;
+               }
+           }
+           if(found<0) {
+               pic_ids[picpos] = swfoutput_drawimagelossless256(&output, pic, pal, width, height, 
+                       x1,y1,x2,y2,x3,y3,x4,y4);
+               pic_xids[picpos] = xid;
+               pic_yids[picpos] = yid;
+               if(picpos<1024)
+                   picpos++;
+           } else {
+               swfoutput_drawimageagain(&output, pic_ids[found], width, height,
+                       x1,y1,x2,y2,x3,y3,x4,y4);
+           }
+           delete pic;
+       }
+       delete imgStr;
+    }
+
   }
 }
 
@@ -1000,7 +1233,6 @@ void SWFOutputDev::drawImage(GfxState *state, Object *ref, Stream *str,
   drawGeneralImage(state,ref,str,width,height,colorMap,0,inlineImg,0);
 }
 
-PDFDoc*doc = 0;
 SWFOutputDev*output = 0; 
 
 void pdfswf_init(char*filename, char*userPassword) 
@@ -1045,6 +1277,7 @@ void pdfswf_init(char*filename, char*userPassword)
 
   // print page count
   printf("Pages:        %d\n", doc->getNumPages());
+  numpages = doc->getNumPages();
   
   // print linearization info
   printf("Linearized:   %s\n", doc->isLinearized() ? "yes" : "no");
@@ -1088,6 +1321,16 @@ void pdfswf_ignoredraworder()
     ignoredraworder = 1;
 }
 
+void pdfswf_linksopennewwindow()
+{
+    opennewwindow = 1;
+}
+
+void pdfswf_storeallcharacters()
+{
+    storeallcharacters = 1;
+}
+
 void pdfswf_jpegquality(int val)
 {
     if(val<0) val=0;
@@ -1100,9 +1343,31 @@ void pdfswf_setoutputfilename(char*_filename)
     swffilename = _filename;
 }
 
+
 void pdfswf_convertpage(int page)
 {
-    doc->displayPage((OutputDev*)output, page, /*zoom*/100, /*rotate*/0, /*doLinks*/(int)1);
+    if(!pages)
+    {
+       pages = (int*)malloc(1024*sizeof(int));
+       pagebuflen = 1024;
+    } else {
+       if(pagepos == pagebuflen)
+       {
+           pagebuflen+=1024;
+           pages = (int*)realloc(pages, pagebuflen);
+       }
+    }
+    pages[pagepos++] = page;
+}
+
+void pdfswf_performconversion()
+{
+    int t;
+    for(t=0;t<pagepos;t++)
+    {
+       currentpage = pages[t];
+       doc->displayPage((OutputDev*)output, currentpage, /*zoom*/100, /*rotate*/0, /*doLinks*/(int)1);
+    }
 }
 
 int pdfswf_numpages()