Orb/Doxygen/src/formula.cpp
changeset 0 42188c7ea2d9
child 4 468f4c8d3d5b
equal deleted inserted replaced
-1:000000000000 0:42188c7ea2d9
       
     1 /******************************************************************************
       
     2  * 
       
     3  *
       
     4  * Copyright (C) 1997-2008 by Dimitri van Heesch.
       
     5  *
       
     6  * Permission to use, copy, modify, and distribute this software and its
       
     7  * documentation under the terms of the GNU General Public License is hereby 
       
     8  * granted. No representations are made about the suitability of this software 
       
     9  * for any purpose. It is provided "as is" without express or implied warranty.
       
    10  * See the GNU General Public License for more details.
       
    11  *
       
    12  * Documents produced by Doxygen are derivative works derived from the
       
    13  * input used in their production; they are not affected by this license.
       
    14  *
       
    15  */
       
    16 
       
    17 #include <stdlib.h>
       
    18 #include <unistd.h>
       
    19 
       
    20 #include "qtbc.h"
       
    21 #include <qfile.h>
       
    22 #include <qtextstream.h>
       
    23 #include <qfileinfo.h>
       
    24 #include <qdir.h>
       
    25 
       
    26 #include "formula.h"
       
    27 #include "image.h"
       
    28 #include "util.h"
       
    29 #include "message.h"
       
    30 #include "config.h"
       
    31 #include "portable.h"
       
    32 #include "index.h"
       
    33 #include "doxygen.h"
       
    34 
       
    35 Formula::Formula(const char *text)
       
    36 {
       
    37   static int count=0;
       
    38   number = count++;
       
    39   form=text;
       
    40 }
       
    41 
       
    42 Formula::~Formula()
       
    43 {
       
    44 }
       
    45 
       
    46 int Formula::getId()
       
    47 {
       
    48   return number;
       
    49 }
       
    50 
       
    51 void FormulaList::generateBitmaps(const char *path)
       
    52 {
       
    53   int x1,y1,x2,y2;
       
    54   QDir d(path);
       
    55   // store the original directory
       
    56   if (!d.exists()) { err("Error: Output dir %s does not exist!\n",path); exit(1); }
       
    57   QCString oldDir = convertToQCString(QDir::currentDirPath());
       
    58   // go to the html output directory (i.e. path)
       
    59   QDir::setCurrent(d.absPath());
       
    60   QDir thisDir;
       
    61   // generate a latex file containing one formula per page.
       
    62   QCString texName="_formulas.tex";
       
    63   QList<int> pagesToGenerate;
       
    64   pagesToGenerate.setAutoDelete(TRUE);
       
    65   FormulaListIterator fli(*this);
       
    66   Formula *formula;
       
    67   QFile f(texName);
       
    68   bool formulaError=FALSE;
       
    69   if (f.open(IO_WriteOnly))
       
    70   {
       
    71     QTextStream t(&f);
       
    72     if (Config_getBool("LATEX_BATCHMODE")) t << "\\batchmode" << endl;
       
    73     t << "\\documentclass{article}" << endl;
       
    74     t << "\\usepackage{epsfig}" << endl; // for those who want to include images
       
    75     const char *s=Config_getList("EXTRA_PACKAGES").first();
       
    76     while (s)
       
    77     {
       
    78       t << "\\usepackage{" << s << "}\n";
       
    79       s=Config_getList("EXTRA_PACKAGES").next();
       
    80     }
       
    81     t << "\\pagestyle{empty}" << endl; 
       
    82     t << "\\begin{document}" << endl;
       
    83     int page=0;
       
    84     for (fli.toFirst();(formula=fli.current());++fli)
       
    85     {
       
    86       QCString resultName;
       
    87       resultName.sprintf("form_%d.png",formula->getId());
       
    88       // only formulas for which no image exists are generated
       
    89       QFileInfo fi(resultName);
       
    90       if (!fi.exists())
       
    91       {
       
    92         // we force a pagebreak after each formula
       
    93         t << formula->getFormulaText() << endl << "\\pagebreak\n\n";
       
    94         pagesToGenerate.append(new int(page));
       
    95       }
       
    96       Doxygen::indexList.addImageFile(resultName);
       
    97       page++;
       
    98     }
       
    99     t << "\\end{document}" << endl;
       
   100     f.close();
       
   101   }
       
   102   if (pagesToGenerate.count()>0) // there are new formulas
       
   103   {
       
   104     //printf("Running latex...\n");
       
   105     //system("latex _formulas.tex </dev/null >/dev/null");
       
   106     QCString latexCmd = Config_getString("LATEX_CMD_NAME");
       
   107     if (latexCmd.isEmpty()) latexCmd="latex";
       
   108     if (portable_system(latexCmd,"_formulas.tex")!=0)
       
   109     {
       
   110       err("Problems running latex. Check your installation or look "
       
   111           "for typos in _formulas.tex and check _formulas.log!\n");
       
   112       formulaError=TRUE;
       
   113       //return;
       
   114     }
       
   115     //printf("Running dvips...\n");
       
   116     QListIterator<int> pli(pagesToGenerate);
       
   117     int *pagePtr;
       
   118     int pageIndex=1;
       
   119     for (;(pagePtr=pli.current());++pli,++pageIndex)
       
   120     {
       
   121       int pageNum=*pagePtr;
       
   122       msg("Generating image form_%d.png for formula\n",pageNum);
       
   123       char dviArgs[4096];
       
   124       QCString formBase;
       
   125       formBase.sprintf("_form%d",pageNum);
       
   126       // run dvips to convert the page with number pageIndex to an
       
   127       // encapsulated postscript.
       
   128       sprintf(dviArgs,"-q -D 600 -E -n 1 -p %d -o %s.eps _formulas.dvi",
       
   129           pageIndex,formBase.data());
       
   130       if (portable_system("dvips",dviArgs)!=0)
       
   131       {
       
   132         err("Problems running dvips. Check your installation!\n");
       
   133         return;
       
   134       }
       
   135       // now we read the generated postscript file to extract the bounding box
       
   136       QFileInfo fi(formBase+".eps");
       
   137       if (fi.exists())
       
   138       {
       
   139         QCString eps = fileToString(formBase+".eps");
       
   140         int i=eps.find("%%BoundingBox:");
       
   141         if (i!=-1)
       
   142         {
       
   143           sscanf(eps.data()+i,"%%%%BoundingBox:%d %d %d %d",&x1,&y1,&x2,&y2);
       
   144         }
       
   145         else
       
   146         {
       
   147           err("Error: Couldn't extract bounding box!\n");
       
   148         }
       
   149       } 
       
   150       // next we generate a postscript file which contains the eps
       
   151       // and displays it in the right colors and the right bounding box
       
   152       f.setName(formBase+".ps");
       
   153       if (f.open(IO_WriteOnly))
       
   154       {
       
   155         QTextStream t(&f);
       
   156         t << "1 1 1 setrgbcolor" << endl;  // anti-alias to white background
       
   157         t << "newpath" << endl;
       
   158         t << "-1 -1 moveto" << endl;
       
   159         t << (x2-x1+2) << " -1 lineto" << endl;
       
   160         t << (x2-x1+2) << " " << (y2-y1+2) << " lineto" << endl;
       
   161         t << "-1 " << (y2-y1+2) << " lineto" <<endl;
       
   162         t << "closepath" << endl;
       
   163         t << "fill" << endl;
       
   164         t << -x1 << " " << -y1 << " translate" << endl;
       
   165         t << "0 0 0 setrgbcolor" << endl;
       
   166         t << "(" << formBase << ".eps) run" << endl;
       
   167         f.close();
       
   168       }
       
   169       // scale the image so that it is four times larger than needed.
       
   170       // and the sizes are a multiple of four.
       
   171       double scaleFactor = 16.0/3.0; 
       
   172       int zoomFactor = Config_getInt("FORMULA_FONTSIZE");
       
   173       if (zoomFactor<8 || zoomFactor>50) zoomFactor=10;
       
   174       scaleFactor *= zoomFactor/10.0;
       
   175       int gx = (((int)((x2-x1)*scaleFactor))+3)&~2;
       
   176       int gy = (((int)((y2-y1)*scaleFactor))+3)&~2;
       
   177       // Then we run ghostscript to convert the postscript to a pixmap
       
   178       // The pixmap is a truecolor image, where only black and white are
       
   179       // used.  
       
   180 
       
   181       char gsArgs[4096];
       
   182       sprintf(gsArgs,"-q -g%dx%d -r%dx%dx -sDEVICE=ppmraw "
       
   183                     "-sOutputFile=%s.pnm -dNOPAUSE -dBATCH -- %s.ps",
       
   184                     gx,gy,(int)(scaleFactor*72),(int)(scaleFactor*72),
       
   185                     formBase.data(),formBase.data()
       
   186              );
       
   187       if (portable_system(portable_ghostScriptCommand(),gsArgs)!=0)
       
   188       {
       
   189         err("Problem running ghostscript %s %s. Check your installation!\n",portable_ghostScriptCommand(),gsArgs);
       
   190         return;
       
   191       }
       
   192       f.setName(formBase+".pnm");
       
   193       uint imageX=0,imageY=0;
       
   194       // we read the generated image again, to obtain the pixel data.
       
   195       if (f.open(IO_ReadOnly))
       
   196       {
       
   197         QTextStream t(&f);
       
   198         QCString s;
       
   199         if (!t.eof())
       
   200           s=t.readLine();
       
   201         if (s.length()<2 || s.left(2)!="P6")
       
   202           err("Error: ghostscript produced an illegal image format!");
       
   203         else
       
   204         {
       
   205           // assume the size if after the first line that does not start with
       
   206           // # excluding the first line of the file.
       
   207           while (!t.eof() && (s=t.readLine()) && !s.isEmpty() && s.at(0)=='#') { }
       
   208           sscanf(s,"%d %d",&imageX,&imageY);
       
   209         }
       
   210         if (imageX>0 && imageY>0)
       
   211         {
       
   212           //printf("Converting image...\n");
       
   213           char *data = new char[imageX*imageY*3]; // rgb 8:8:8 format
       
   214           uint i,x,y,ix,iy;
       
   215           f.readBlock(data,imageX*imageY*3);
       
   216           Image srcImage(imageX,imageY),
       
   217                 filteredImage(imageX,imageY),
       
   218                 dstImage(imageX/4,imageY/4);
       
   219           uchar *ps=srcImage.getData();
       
   220           // convert image to black (1) and white (0) index.
       
   221           for (i=0;i<imageX*imageY;i++) *ps++= (data[i*3]==0 ? 1 : 0);
       
   222           // apply a simple box filter to the image 
       
   223           static int filterMask[]={1,2,1,2,8,2,1,2,1};
       
   224           for (y=0;y<srcImage.getHeight();y++)
       
   225           {
       
   226             for (x=0;x<srcImage.getWidth();x++)
       
   227             {
       
   228               int s=0;
       
   229               for (iy=0;iy<2;iy++)
       
   230               {
       
   231                 for (ix=0;ix<2;ix++)
       
   232                 {
       
   233                   s+=srcImage.getPixel(x+ix-1,y+iy-1)*filterMask[iy*3+ix];
       
   234                 }
       
   235               }
       
   236               filteredImage.setPixel(x,y,s);
       
   237             }
       
   238           }
       
   239           // down-sample the image to 1/16th of the area using 16 gray scale
       
   240           // colors.
       
   241           // TODO: optimize this code.
       
   242           for (y=0;y<dstImage.getHeight();y++)
       
   243           {
       
   244             for (x=0;x<dstImage.getWidth();x++)
       
   245             {
       
   246               int xp=x<<2;
       
   247               int yp=y<<2;
       
   248               int c=filteredImage.getPixel(xp+0,yp+0)+
       
   249                     filteredImage.getPixel(xp+1,yp+0)+
       
   250                     filteredImage.getPixel(xp+2,yp+0)+
       
   251                     filteredImage.getPixel(xp+3,yp+0)+
       
   252                     filteredImage.getPixel(xp+0,yp+1)+
       
   253                     filteredImage.getPixel(xp+1,yp+1)+
       
   254                     filteredImage.getPixel(xp+2,yp+1)+
       
   255                     filteredImage.getPixel(xp+3,yp+1)+
       
   256                     filteredImage.getPixel(xp+0,yp+2)+
       
   257                     filteredImage.getPixel(xp+1,yp+2)+
       
   258                     filteredImage.getPixel(xp+2,yp+2)+
       
   259                     filteredImage.getPixel(xp+3,yp+2)+
       
   260                     filteredImage.getPixel(xp+0,yp+3)+
       
   261                     filteredImage.getPixel(xp+1,yp+3)+
       
   262                     filteredImage.getPixel(xp+2,yp+3)+
       
   263                     filteredImage.getPixel(xp+3,yp+3);
       
   264               // here we scale and clip the color value so the
       
   265               // resulting image has a reasonable contrast
       
   266               dstImage.setPixel(x,y,QMIN(15,(c*15)/(16*10)));
       
   267             }
       
   268           }
       
   269           // save the result as a bitmap
       
   270           QCString resultName;
       
   271           resultName.sprintf("form_%d.png",pageNum);
       
   272           // the option parameter 1 is used here as a temporary hack
       
   273           // to select the right color palette! 
       
   274           dstImage.save(resultName,1);
       
   275           delete[] data;
       
   276         }
       
   277         f.close();
       
   278       } 
       
   279       // remove intermediate image files
       
   280       thisDir.remove(formBase+".eps");
       
   281       thisDir.remove(formBase+".pnm");
       
   282       thisDir.remove(formBase+".ps");
       
   283     }
       
   284     // remove intermediate files produced by latex
       
   285     thisDir.remove("_formulas.dvi");
       
   286     if (!formulaError) thisDir.remove("_formulas.log"); // keep file in case of errors
       
   287     thisDir.remove("_formulas.aux");
       
   288   }
       
   289   // remove the latex file itself
       
   290   if (!formulaError) thisDir.remove("_formulas.tex");
       
   291   // write/update the formula repository so we know what text the 
       
   292   // generated images represent (we use this next time to avoid regeneration
       
   293   // of the images, and to avoid forcing the user to delete all images in order
       
   294   // to let a browser refresh the images).
       
   295   f.setName("formula.repository");
       
   296   if (f.open(IO_WriteOnly))
       
   297   {
       
   298     QTextStream t(&f);
       
   299     for (fli.toFirst();(formula=fli.current());++fli)
       
   300     {
       
   301       t << "\\form#" << formula->getId() << ":" << formula->getFormulaText() << endl;
       
   302     }
       
   303     f.close();
       
   304   }
       
   305   // reset the directory to the original location.
       
   306   QDir::setCurrent(oldDir);
       
   307 }
       
   308 
       
   309 
       
   310 #ifdef FORMULA_TEST
       
   311 int main()
       
   312 {
       
   313   FormulaList fl;
       
   314   fl.append(new Formula("$x^2$"));
       
   315   fl.append(new Formula("$y^2$"));
       
   316   fl.append(new Formula("$\\sqrt{x_0^2+x_1^2+x_2^2}$"));
       
   317   fl.generateBitmaps("dest");
       
   318   return 0;
       
   319 }
       
   320 #endif