link pages tables are now a part of SWFOutputDev
[swftools.git] / pdf2swf / SWFOutputDev.cc
index 5ce505d..9fce9e7 100644 (file)
@@ -58,6 +58,7 @@
 #else
 #include "FoFiType1C.h"
 #include "FoFiTrueType.h"
+#include "GHash.h"
 #endif
 #include "SWFOutputDev.h"
 
@@ -92,12 +93,6 @@ static int fontnum = 0;
 
 static int config_use_fontconfig = 1;
 
-// swf <-> pdf pages
-// TODO: move into pdf_doc_t
-static int*pages = 0;
-static int pagebuflen = 0;
-static int pagepos = 0;
-
 /* config */
 static double caplinewidth = 3.0;
 static double zoom = 72; /* xpdf: 86 */
@@ -143,6 +138,8 @@ typedef struct _fontlist
     _fontlist*next;
 } fontlist_t;
 
+class InfoOutputDev;
+
 class SWFOutputDev:  public OutputDev {
 public:
   gfxdevice_t* output;
@@ -155,6 +152,8 @@ public:
 
   void setMove(int x,int y);
   void setClip(int x1,int y1,int x2,int y2);
+
+  void setInfo(InfoOutputDev*info) {this->info = info;}
   
   int save(char*filename);
     
@@ -246,7 +245,7 @@ public:
   void drawGeneralImage(GfxState *state, Object *ref, Stream *str,
                                   int width, int height, GfxImageColorMap*colorMap, GBool invert,
                                   GBool inlineImg, int mask, int *maskColors);
-  int SWFOutputDev::setGfxFont(char*id, char*filename);
+  int SWFOutputDev::setGfxFont(char*id, char*filename, double quality);
   void strokeGfxline(GfxState *state, gfxline_t*line);
   void clipToGfxLine(GfxState *state, gfxline_t*line);
   void fillGfxLine(GfxState *state, gfxline_t*line);
@@ -257,6 +256,7 @@ public:
 
   char outer_clip_box; //whether the page clip box is still on
 
+  InfoOutputDev*info;
   SWFOutputState states[64];
   int statepos;
 
@@ -296,12 +296,26 @@ public:
   gfxmatrix_t current_font_matrix;
 
   fontlist_t* fontlist;
+
+  int*pages;
+  int pagebuflen;
+  int pagepos;
+
+  friend void swf_output_preparepage(swf_output_t*swf, int pdfpage, int outputpage);
 };
 
 static char*getFontID(GfxFont*font);
 
+struct FontInfo
+{
+    GfxFont*font;
+    double max_size;
+};
+
 class InfoOutputDev:  public OutputDev 
 {
+  GHash* id2font;
+  FontInfo* currentfont;
   public:
   int x1,y1,x2,y2;
   int num_links;
@@ -313,9 +327,11 @@ class InfoOutputDev:  public OutputDev
       num_links = 0;
       num_images = 0;
       num_fonts = 0;
+      id2font = new GHash();
   }
   virtual ~InfoOutputDev() 
   {
+      delete id2font;
   }
   virtual GBool upsideDown() {return gTrue;}
   virtual GBool useDrawChar() {return gTrue;}
@@ -337,14 +353,53 @@ class InfoOutputDev:  public OutputDev
   {
       num_links++;
   }
+  virtual double getMaximumFontSize(char*id)
+  {
+      FontInfo*info = (FontInfo*)id2font->lookup(id);
+      if(!info) {
+         msg("<error> Unknown font id: %s", id);
+         return 0.0;
+      }
+      return info->max_size;
+  }
+
   virtual void updateFont(GfxState *state) 
   {
       GfxFont*font = state->getFont();
       if(!font)
           return;
-      /*char*id = getFontID(font);*/
-      /* FIXME*/
-      num_fonts++;
+      char*id = getFontID(font);
+      
+      FontInfo*info = (FontInfo*)id2font->lookup(id);
+      if(!info) {
+       GString* idStr = new GString(id);
+       info = new FontInfo;
+       info->font = font;
+       info->max_size = 0;
+       id2font->add(idStr, (void*)info);
+       free(id);
+       num_fonts++;
+      }
+      currentfont = info;
+  }
+  virtual void drawChar(GfxState *state, double x, double y,
+                       double dx, double dy,
+                       double originX, double originY,
+                       CharCode code, Unicode *u, int uLen)
+  {
+      int render = state->getRender();
+      if (render == 3)
+         return;
+      double m11,m21,m12,m22;
+      state->getFontTransMat(&m11, &m12, &m21, &m22);
+      m11 *= state->getHorizScaling();
+      m21 *= state->getHorizScaling();
+      double lenx = sqrt(m11*m11 + m12*m12);
+      double leny = sqrt(m21*m21 + m22*m22);
+      double len = lenx>leny?lenx:leny;
+      if(currentfont && currentfont->max_size < len) {
+         currentfont->max_size = len;
+      }
   }
   virtual void drawImageMask(GfxState *state, Object *ref, Stream *str,
                             int width, int height, GBool invert,
@@ -383,6 +438,9 @@ SWFOutputDev::SWFOutputDev()
     fontlist = 0;
     result = 0;
     outer_clip_box = 0;
+    pages = 0;
+    pagebuflen = 0;
+    pagepos = 0;
     output = (gfxdevice_t*)malloc(sizeof(gfxdevice_t));
     gfxdevice_swf_init(output);
     /* configure device */
@@ -703,7 +761,7 @@ gfxline_t* gfxPath_to_gfxline(GfxState*state, GfxPath*path, int closed, int user
                if(cpos==0) {
                    draw.lineTo(&draw, x,y);
                } else {
-                   gfxdraw_cubicTo(&draw, bx,by, cx,cy, x,y);
+                   gfxdraw_cubicTo(&draw, bx,by, cx,cy, x,y, 0.05);
                }
                needsfix = 1;
                cpos = 0;
@@ -931,6 +989,10 @@ SWFOutputDev::~SWFOutputDev()
        this->result->destroy(this->result);
        this->result = 0;
     }
+    
+    if(this->pages) {
+       free(this->pages); this->pages = 0;
+    }
 
     fontlist_t*l = this->fontlist;
     while(l) {
@@ -1397,7 +1459,8 @@ void SWFOutputDev::drawLink(Link *link, Catalog *catalog)
                     }
                     else if(strstr(s, "last") || strstr(s, "end"))
                     {
-                        page = pagepos>0?pages[pagepos-1]:0;
+                       if(pages && pagepos>0)
+                           page = pages[pagepos-1];
                     }
                     else if(strstr(s, "first") || strstr(s, "top"))
                     {
@@ -1448,13 +1511,20 @@ void SWFOutputDev::drawLink(Link *link, Catalog *catalog)
     if(page>0)
     {
         int t;
-        for(t=0;t<pagepos;t++)
-            if(pages[t]==page)
+       int lpage = -1;
+        for(t=1;t<=pagepos;t++) {
+           printf("%d) %d (%d?)\n", t, pages[t], page);
+            if(pages[t]==page) {
+               lpage = t;
                 break;
-        if(t!=pagepos) {
+           }
+       }
+        if(lpage>=0) {
            char buf[80];
            sprintf(buf, "page%d", t);
             output->drawlink(output, points, buf);
+       } else {
+           msg("<warning> Invalid link to page %d", page);
        }
     }
     else if(url)
@@ -1846,7 +1916,7 @@ void SWFOutputDev::setXRef(PDFDoc*doc, XRef *xref)
     this->xref = xref;
 }
 
-int SWFOutputDev::setGfxFont(char*id, char*filename)
+int SWFOutputDev::setGfxFont(char*id, char*filename, double maxSize)
 {
     gfxfont_t*font = 0;
     fontlist_t*last=0,*l = this->fontlist;
@@ -1864,7 +1934,14 @@ int SWFOutputDev::setGfxFont(char*id, char*filename)
        l = l->next;
     }
     if(!filename) return 0;
-    font = gfxfont_load(filename);
+
+    /* A font size of e.g. 9 means the font will be scaled down by
+       1024 and scaled up by 9. So to have a maximum error of 1/20px,
+       we have to divide 0.05 by (fontsize/1024)
+     */
+    double quality = (1024 * 0.05) / maxSize;
+    
+    font = gfxfont_load(filename, quality);
     l = new fontlist_t;
     l->font = font;
     l->filename = strdup(filename);
@@ -1889,6 +1966,11 @@ void SWFOutputDev::updateFont(GfxState *state)
        return;
     }  
     char * fontid = getFontID(gfxFont);
+    double maxSize = 1.0;
+
+    if(this->info) {
+       maxSize = this->info->getMaximumFontSize(fontid);
+    }
     
     int t;
     /* first, look if we substituted this font before-
@@ -1904,7 +1986,7 @@ void SWFOutputDev::updateFont(GfxState *state)
 
     /* second, see if this is a font which was used before-
        if so, we are done */
-    if(setGfxFont(fontid, 0)) {
+    if(setGfxFont(fontid, 0, 0)) {
        free(fontid);
        return;
     }
@@ -1964,13 +2046,13 @@ void SWFOutputDev::updateFont(GfxState *state)
        return;
     }
        
-    msg("<verbose> updateFont(%s) -> %s", fontid, fileName);
+    msg("<verbose> updateFont(%s) -> %s (max size: %f)", fontid, fileName, maxSize);
     dumpFontInfo("<verbose>", gfxFont);
 
     //swfoutput_setfont(&output, fontid, fileName);
     
-    if(!setGfxFont(fontid, 0)) {
-       setGfxFont(fontid, fileName);
+    if(!setGfxFont(fontid, 0, 0)) {
+       setGfxFont(fontid, fileName, maxSize);
     }
    
     if(fileName && del)
@@ -2509,6 +2591,7 @@ typedef struct _pdf_doc_internal
 {
     int protect;
     PDFDoc*doc;
+    InfoOutputDev*info;
 } pdf_doc_internal_t;
 typedef struct _pdf_page_internal
 {
@@ -2585,24 +2668,19 @@ pdf_doc_t* pdf_init(char*filename, char*userPassword)
           if(!i->doc->okToChange() || !i->doc->okToAddNotes())
               i->protect = 1;
     }
-   
-    return pdf_doc;
-}
 
-static void pdfswf_preparepage(int page)
-{
-    /*FIXME*/
-    if(!pages) {
-       pages = (int*)malloc(1024*sizeof(int));
-       pagebuflen = 1024;
-    } else {
-       if(pagepos == pagebuflen)
-       {
-           pagebuflen+=1024;
-           pages = (int*)realloc(pages, pagebuflen);
-       }
+    InfoOutputDev*io = new InfoOutputDev();
+    int t;
+    for(t=1;t<=pdf_doc->num_pages;t++) {
+#ifdef XPDF_101
+       i->doc->displayPage((OutputDev*)io, t, /*zoom*/zoom, /*rotate*/0, /*doLinks*/(int)1);
+#else
+       i->doc->displayPage((OutputDev*)io, t, zoom, zoom, /*rotate*/0, true, /*doLinks*/(int)1);
+#endif
     }
-    pages[pagepos++] = page;
+    i->info = io;
+
+    return pdf_doc;
 }
 
 class MemCheck
@@ -2621,7 +2699,9 @@ void pdf_destroy(pdf_doc_t*pdf_doc)
 
     delete i->doc; i->doc=0;
     
-    free(pages); pages = 0; //FIXME
+    if(i->info) {
+       delete i->info;i->info=0;
+    }
 
     free(pdf_doc->internal);pdf_doc->internal=0;
     free(pdf_doc);pdf_doc=0;
@@ -2680,6 +2760,32 @@ void swf_output_endframe(swf_output_t*swf)
     i->outputDev->endframe();
 }
 
+void swf_output_preparepage(swf_output_t*swf, int pdfpage, int outputpage)
+{
+    swf_output_internal_t*i= (swf_output_internal_t*)swf->internal;
+    SWFOutputDev*o = i->outputDev;
+
+    if(pdfpage < 0)
+       return;
+
+    if(!o->pages) {
+       o->pagebuflen = 1024;
+       o->pages = (int*)malloc(o->pagebuflen*sizeof(int));
+       memset(o->pages, -1, o->pagebuflen*sizeof(int));
+    } else {
+       while(pdfpage >= o->pagebuflen)
+       {
+           int oldlen = o->pagebuflen;
+           o->pagebuflen+=1024;
+           o->pages = (int*)realloc(o->pages, o->pagebuflen*sizeof(int));
+           memset(&o->pages[oldlen], -1, (o->pagebuflen-oldlen)*sizeof(int));
+       }
+    }
+    o->pages[pdfpage] = outputpage;
+    if(pdfpage>o->pagepos)
+       o->pagepos = pdfpage;
+}
+
 int swf_output_save(swf_output_t*swf, char*filename)
 {
     swf_output_internal_t*i= (swf_output_internal_t*)swf->internal;
@@ -2707,10 +2813,16 @@ void pdf_page_render2(pdf_page_t*page, swf_output_t*swf)
     pdf_doc_internal_t*pi = (pdf_doc_internal_t*)page->parent->internal;
     swf_output_internal_t*si = (swf_output_internal_t*)swf->internal;
 
+    if(!pi) {
+       msg("<fatal> pdf_page_render: Parent PDF this page belongs to doesn't exist yet/anymore");
+       return;
+    }
+
     if(pi->protect) {
        gfxdevice_t*dev = si->outputDev->output;
         dev->setparameter(dev, "protect", "1");
     }
+    si->outputDev->setInfo(pi->info);
     si->outputDev->setXRef(pi->doc, pi->doc->getXRef());
 #ifdef XPDF_101
     pi->doc->displayPage((OutputDev*)si->outputDev, page->nr, /*zoom*/zoom, /*rotate*/0, /*doLinks*/(int)1);
@@ -2773,4 +2885,5 @@ pdf_page_info_t* pdf_page_getinfo(pdf_page_t*page)
 void pdf_page_info_destroy(pdf_page_info_t*info)
 {
     free(info);
+
 }