formula.cpp

Go to the documentation of this file.
00001 /******************************************************************************
00002  i
00003  * $Id: formula.cpp,v 1.16 2001/03/19 19:27:40 root Exp $
00004  *
00005  * Copyright (C) 1997-2008 by Dimitri van Heesch.
00006  *
00007  * Permission to use, copy, modify, and distribute this software and its
00008  * documentation under the terms of the GNU General Public License is hereby 
00009  * granted. No representations are made about the suitability of this software 
00010  * for any purpose. It is provided "as is" without express or implied warranty.
00011  * See the GNU General Public License for more details.
00012  *
00013  * Documents produced by Doxygen are derivative works derived from the
00014  * input used in their production; they are not affected by this license.
00015  *
00016  */
00017 
00018 #include <stdlib.h>
00019 #include <unistd.h>
00020 
00021 #include "qtbc.h"
00022 #include <qfile.h>
00023 #include <qtextstream.h>
00024 #include <qfileinfo.h>
00025 #include <qdir.h>
00026 
00027 #include "formula.h"
00028 #include "image.h"
00029 #include "util.h"
00030 #include "message.h"
00031 #include "config.h"
00032 #include "portable.h"
00033 
00034 Formula::Formula(const char *text)
00035 {
00036   static int count=0;
00037   number = count++;
00038   form=text;
00039 }
00040 
00041 Formula::~Formula()
00042 {
00043 }
00044 
00045 int Formula::getId()
00046 {
00047   return number;
00048 }
00049 
00050 void FormulaList::generateBitmaps(const char *path)
00051 {
00052   int x1,y1,x2,y2;
00053   QDir d(path);
00054   // store the original directory
00055   if (!d.exists()) { err("Error: Output dir %s does not exist!\n",path); exit(1); }
00056   QCString oldDir = convertToQCString(QDir::currentDirPath());
00057   // go to the html output directory (i.e. path)
00058   QDir::setCurrent(d.absPath());
00059   QDir thisDir;
00060   // generate a latex file containing one formula per page.
00061   QCString texName="_formulas.tex";
00062   QList<int> pagesToGenerate;
00063   pagesToGenerate.setAutoDelete(TRUE);
00064   FormulaListIterator fli(*this);
00065   Formula *formula;
00066   QFile f(texName);
00067   bool formulaError=FALSE;
00068   if (f.open(IO_WriteOnly))
00069   {
00070     QTextStream t(&f);
00071     if (Config_getBool("LATEX_BATCHMODE")) t << "\\batchmode" << endl;
00072     t << "\\documentclass{article}" << endl;
00073     t << "\\usepackage{epsfig}" << endl; // for those who want to include images
00074     const char *s=Config_getList("EXTRA_PACKAGES").first();
00075     while (s)
00076     {
00077       t << "\\usepackage{" << s << "}\n";
00078       s=Config_getList("EXTRA_PACKAGES").next();
00079     }
00080     t << "\\pagestyle{empty}" << endl; 
00081     t << "\\begin{document}" << endl;
00082     int page=0;
00083     for (fli.toFirst();(formula=fli.current());++fli)
00084     {
00085       QCString resultName;
00086       resultName.sprintf("form_%d.png",formula->getId());
00087       // only formulas for which no image exists are generated
00088       QFileInfo fi(resultName);
00089       if (!fi.exists())
00090       {
00091         // we force a pagebreak after each formula
00092         t << formula->getFormulaText() << endl << "\\pagebreak\n\n";
00093         pagesToGenerate.append(new int(page));
00094       }
00095       page++;
00096     }
00097     t << "\\end{document}" << endl;
00098     f.close();
00099   }
00100   if (pagesToGenerate.count()>0) // there are new formulas
00101   {
00102     //printf("Running latex...\n");
00103     //system("latex _formulas.tex </dev/null >/dev/null");
00104     QCString latexCmd = Config_getString("LATEX_CMD_NAME");
00105     if (latexCmd.isEmpty()) latexCmd="latex";
00106     if (portable_system(latexCmd,"_formulas.tex")!=0)
00107     {
00108       err("Problems running latex. Check your installation or look "
00109           "for typos in _formulas.tex and check _formulas.log!\n");
00110       formulaError=TRUE;
00111       //return;
00112     }
00113     //printf("Running dvips...\n");
00114     QListIterator<int> pli(pagesToGenerate);
00115     int *pagePtr;
00116     int pageIndex=1;
00117     for (;(pagePtr=pli.current());++pli,++pageIndex)
00118     {
00119       int pageNum=*pagePtr;
00120       msg("Generating image form_%d.png for formula\n",pageNum);
00121       char dviArgs[4096];
00122       QCString formBase;
00123       formBase.sprintf("_form%d",pageNum);
00124       // run dvips to convert the page with number pageIndex to an
00125       // encapsulated postscript.
00126       sprintf(dviArgs,"-q -D 600 -E -n 1 -p %d -o %s.eps _formulas.dvi",
00127           pageIndex,formBase.data());
00128       if (portable_system("dvips",dviArgs)!=0)
00129       {
00130         err("Problems running dvips. Check your installation!\n");
00131         return;
00132       }
00133       // now we read the generated postscript file to extract the bounding box
00134       QFileInfo fi(formBase+".eps");
00135       if (fi.exists())
00136       {
00137         QCString eps = fileToString(formBase+".eps");
00138         int i=eps.find("%%BoundingBox:");
00139         if (i!=-1)
00140         {
00141           sscanf(eps.data()+i,"%%%%BoundingBox:%d %d %d %d",&x1,&y1,&x2,&y2);
00142         }
00143         else
00144         {
00145           err("Error: Couldn't extract bounding box!\n");
00146         }
00147       } 
00148       // next we generate a postscript file which contains the eps
00149       // and displays it in the right colors and the right bounding box
00150       f.setName(formBase+".ps");
00151       if (f.open(IO_WriteOnly))
00152       {
00153         QTextStream t(&f);
00154         t << "1 1 1 setrgbcolor" << endl;  // anti-alias to white background
00155         t << "newpath" << endl;
00156         t << "-1 -1 moveto" << endl;
00157         t << (x2-x1+2) << " -1 lineto" << endl;
00158         t << (x2-x1+2) << " " << (y2-y1+2) << " lineto" << endl;
00159         t << "-1 " << (y2-y1+2) << " lineto" <<endl;
00160         t << "closepath" << endl;
00161         t << "fill" << endl;
00162         t << -x1 << " " << -y1 << " translate" << endl;
00163         t << "0 0 0 setrgbcolor" << endl;
00164         t << "(" << formBase << ".eps) run" << endl;
00165         f.close();
00166       }
00167       // scale the image so that it is four times larger than needed.
00168       // and the sizes are a multiple of four.
00169       const double scaleFactor = 16.0/3.0; 
00170       int gx = (((int)((x2-x1)*scaleFactor))+3)&~2;
00171       int gy = (((int)((y2-y1)*scaleFactor))+3)&~2;
00172       // Then we run ghostscript to convert the postscript to a pixmap
00173       // The pixmap is a truecolor image, where only black and white are
00174       // used.  
00175 
00176       char gsArgs[4096];
00177       sprintf(gsArgs,"-q -g%dx%d -r%dx%dx -sDEVICE=ppmraw "
00178                     "-sOutputFile=%s.pnm -dNOPAUSE -dBATCH -- %s.ps",
00179                     gx,gy,(int)(scaleFactor*72),(int)(scaleFactor*72),
00180                     formBase.data(),formBase.data()
00181              );
00182       if (portable_system(portable_ghostScriptCommand(),gsArgs)!=0)
00183       {
00184         err("Problem running ghostscript %s %s. Check your installation!\n",portable_ghostScriptCommand(),gsArgs);
00185         return;
00186       }
00187       f.setName(formBase+".pnm");
00188       uint imageX=0,imageY=0;
00189       // we read the generated image again, to obtain the pixel data.
00190       if (f.open(IO_ReadOnly))
00191       {
00192         QTextStream t(&f);
00193         QCString s;
00194         if (!t.eof())
00195           s=t.readLine();
00196         if (s.length()<2 || s.left(2)!="P6")
00197           err("Error: ghostscript produced an illegal image format!");
00198         else
00199         {
00200           // assume the size if after the first line that does not start with
00201           // # excluding the first line of the file.
00202           while (!t.eof() && (s=t.readLine()) && !s.isEmpty() && s.at(0)=='#');
00203           sscanf(s,"%d %d",&imageX,&imageY);
00204         }
00205         if (imageX>0 && imageY>0)
00206         {
00207           //printf("Converting image...\n");
00208           char *data = new char[imageX*imageY*3]; // rgb 8:8:8 format
00209           uint i,x,y,ix,iy;
00210           f.readBlock(data,imageX*imageY*3);
00211           Image srcImage(imageX,imageY),
00212                 filteredImage(imageX,imageY),
00213                 dstImage(imageX/4,imageY/4);
00214           uchar *ps=srcImage.getData();
00215           // convert image to black (1) and white (0) index.
00216           for (i=0;i<imageX*imageY;i++) *ps++= (data[i*3]==0 ? 1 : 0);
00217           // apply a simple box filter to the image 
00218           static int filterMask[]={1,2,1,2,8,2,1,2,1};
00219           for (y=0;y<srcImage.getHeight();y++)
00220           {
00221             for (x=0;x<srcImage.getWidth();x++)
00222             {
00223               int s=0;
00224               for (iy=0;iy<2;iy++)
00225               {
00226                 for (ix=0;ix<2;ix++)
00227                 {
00228                   s+=srcImage.getPixel(x+ix-1,y+iy-1)*filterMask[iy*3+ix];
00229                 }
00230               }
00231               filteredImage.setPixel(x,y,s);
00232             }
00233           }
00234           // down-sample the image to 1/16th of the area using 16 gray scale
00235           // colors.
00236           // TODO: optimize this code.
00237           for (y=0;y<dstImage.getHeight();y++)
00238           {
00239             for (x=0;x<dstImage.getWidth();x++)
00240             {
00241               int xp=x<<2;
00242               int yp=y<<2;
00243               int c=filteredImage.getPixel(xp+0,yp+0)+
00244                     filteredImage.getPixel(xp+1,yp+0)+
00245                     filteredImage.getPixel(xp+2,yp+0)+
00246                     filteredImage.getPixel(xp+3,yp+0)+
00247                     filteredImage.getPixel(xp+0,yp+1)+
00248                     filteredImage.getPixel(xp+1,yp+1)+
00249                     filteredImage.getPixel(xp+2,yp+1)+
00250                     filteredImage.getPixel(xp+3,yp+1)+
00251                     filteredImage.getPixel(xp+0,yp+2)+
00252                     filteredImage.getPixel(xp+1,yp+2)+
00253                     filteredImage.getPixel(xp+2,yp+2)+
00254                     filteredImage.getPixel(xp+3,yp+2)+
00255                     filteredImage.getPixel(xp+0,yp+3)+
00256                     filteredImage.getPixel(xp+1,yp+3)+
00257                     filteredImage.getPixel(xp+2,yp+3)+
00258                     filteredImage.getPixel(xp+3,yp+3);
00259               // here we scale and clip the color value so the
00260               // resulting image has a reasonable contrast
00261               dstImage.setPixel(x,y,QMIN(15,(c*15)/(16*10)));
00262             }
00263           }
00264           // save the result as a png
00265           QCString resultName;
00266           resultName.sprintf("form_%d.png",pageNum);
00267           // the option parameter 1 is used here as a temporary hack
00268           // to select the right color palette! 
00269           dstImage.save(resultName,1);
00270           delete[] data;
00271         }
00272         f.close();
00273       } 
00274       // remove intermediate image files
00275       thisDir.remove(formBase+".eps");
00276       thisDir.remove(formBase+".pnm");
00277       thisDir.remove(formBase+".ps");
00278     }
00279     // remove intermediate files produced by latex
00280     thisDir.remove("_formulas.dvi");
00281     if (!formulaError) thisDir.remove("_formulas.log"); // keep file in case of errors
00282     thisDir.remove("_formulas.aux");
00283   }
00284   // remove the latex file itself
00285   if (!formulaError) thisDir.remove("_formulas.tex");
00286   // write/update the formula repository so we know what text the 
00287   // generated pngs represent (we use this next time to avoid regeneration
00288   // of the pngs, and to avoid forcing the user to delete all pngs in order
00289   // to let a browser refresh the images).
00290   f.setName("formula.repository");
00291   if (f.open(IO_WriteOnly))
00292   {
00293     QTextStream t(&f);
00294     for (fli.toFirst();(formula=fli.current());++fli)
00295     {
00296       t << "\\form#" << formula->getId() << ":" << formula->getFormulaText() << endl;
00297     }
00298     f.close();
00299   }
00300   // reset the directory to the original location.
00301   QDir::setCurrent(oldDir);
00302 }
00303 
00304 
00305 #ifdef FORMULA_TEST
00306 int main()
00307 {
00308   FormulaList fl;
00309   fl.append(new Formula("$x^2$"));
00310   fl.append(new Formula("$y^2$"));
00311   fl.append(new Formula("$\\sqrt{x_0^2+x_1^2+x_2^2}$"));
00312   fl.generateBitmaps("dest");
00313   return 0;
00314 }
00315 #endif



Generated on Mon Mar 31 10:58:38 2008 by  doxygen 1.5.1